diff --git a/.pnpmfile.cjs b/.pnpmfile.cjs new file mode 100644 index 0000000..f301880 --- /dev/null +++ b/.pnpmfile.cjs @@ -0,0 +1,95 @@ +const fs = require('node:fs'); +const path = require('node:path'); + +const PACKAGE_SCOPE = '@bytelyst/'; +const PACKAGE_SOURCE = process.env.BYTELYST_PACKAGE_SOURCE || 'common-plat'; +const DEFAULT_COMMON_PLAT_ROOT = path.resolve(__dirname, '..', 'learning_ai', 'learning_ai_common_plat'); +const LEGACY_COMMON_PLAT_ROOT = path.resolve(__dirname, '..', 'learning_ai_common_plat'); +const COMMON_PLAT_ROOT = + process.env.BYTELYST_COMMON_PLAT_ROOT || + (fs.existsSync(DEFAULT_COMMON_PLAT_ROOT) ? DEFAULT_COMMON_PLAT_ROOT : LEGACY_COMMON_PLAT_ROOT); +const COMMON_PLAT_PACKAGES_ROOT = path.join(COMMON_PLAT_ROOT, 'packages'); +const VERSION_CACHE = new Map(); +let loggedSource = false; + +function packageDirFor(name) { + return name.startsWith(PACKAGE_SCOPE) ? name.slice(PACKAGE_SCOPE.length) : null; +} + +function pathIfPackageExists(rootDir, name) { + const packageDir = packageDirFor(name); + if (!packageDir) return null; + + const candidate = path.join(rootDir, packageDir); + return fs.existsSync(path.join(candidate, 'package.json')) ? candidate : null; +} + +function readPackageVersion(packagePath) { + if (VERSION_CACHE.has(packagePath)) { + return VERSION_CACHE.get(packagePath); + } + + try { + const packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json'), 'utf8')); + const version = packageJson.version || null; + VERSION_CACHE.set(packagePath, version); + return version; + } catch { + VERSION_CACHE.set(packagePath, null); + return null; + } +} + +function resolveSpecifier(name) { + if (!name.startsWith(PACKAGE_SCOPE)) { + return null; + } + + const commonPlatPath = pathIfPackageExists(COMMON_PLAT_PACKAGES_ROOT, name); + + if (PACKAGE_SOURCE === 'common-plat') { + return commonPlatPath ? 'workspace:*' : null; + } + + if (PACKAGE_SOURCE === 'gitea') { + return commonPlatPath ? readPackageVersion(commonPlatPath) : null; + } + + return commonPlatPath ? 'workspace:*' : null; +} + +function rewriteDependencySet(dependencies = {}) { + for (const dependencyName of Object.keys(dependencies)) { + const rewrittenSpecifier = resolveSpecifier(dependencyName); + if (rewrittenSpecifier) { + dependencies[dependencyName] = rewrittenSpecifier; + } + } +} + +function logSourceOnce() { + if (loggedSource) { + return; + } + + loggedSource = true; + process.stderr.write( + `[bytelyst] pnpm package source=${PACKAGE_SOURCE} commonPlatRoot=${COMMON_PLAT_ROOT}\n`, + ); +} + +module.exports = { + hooks: { + readPackage(packageJson) { + logSourceOnce(); + if (packageJson.name?.startsWith(PACKAGE_SCOPE)) { + return packageJson; + } + + rewriteDependencySet(packageJson.dependencies); + rewriteDependencySet(packageJson.devDependencies); + rewriteDependencySet(packageJson.optionalDependencies); + return packageJson; + }, + }, +}; diff --git a/AGENTS.md b/AGENTS.md index 1a85d37..ee23e83 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -232,6 +232,14 @@ learning_ai_notes/ - **Sync:** `cd ../learning_ai/learning_ai_common_plat && bash scripts/sync-npmrc.sh` - **Key rule:** Never hardcode `gitea.bytelyst.com` — use `${GITEA_NPM_HOST:-localhost}:3300` (SSH tunnel on corp network) +### Local package source toggle + +NoteLett defaults to local `@bytelyst/*` packages from `../learning_ai/learning_ai_common_plat` through `.pnpmfile.cjs`; do not use Gitea unless explicitly needed. + +- **Local/common-platform install:** `pnpm run install:common-plat` +- **Registry fallback:** `source ~/.zshrc && pnpm run install:gitea` +- **Override common-platform path:** `BYTELYST_COMMON_PLAT_ROOT=/path/to/learning_ai_common_plat` + ### MUST NOT do - Never use `console.log` in production code — use `req.log` or `app.log` in Fastify diff --git a/README.md b/README.md index 73bc929..184caed 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,11 @@ Prerequisites: ```bash # Install against the local common-platform workspace packages. -GITEA_NPM_TOKEN=dummy pnpm install --frozen-lockfile +pnpm run install:common-plat --frozen-lockfile + +# Registry fallback when explicitly needed. +source ~/.zshrc +pnpm run install:gitea --frozen-lockfile # Backend in local memory mode (port 4016) DB_PROVIDER=memory JWT_SECRET=dev-secret-do-not-use-in-prod \ @@ -69,6 +73,8 @@ NoteLett keeps product-local domain behavior in this repo: note/workspace CRUD, Common-platform responsibilities come from `../learning_ai/learning_ai_common_plat`: auth/session primitives, API clients, datastore/Cosmos abstractions, shared error/config/logging conventions, telemetry, diagnostics, feature flags, kill switch, blob access, extraction-service clients, design tokens, shared UI primitives where appropriate, MCP server integration, webhook dispatch, encryption helpers, and cross-repo automation scripts. +Package resolution defaults to local common-platform packages through `.pnpmfile.cjs` (`BYTELYST_PACKAGE_SOURCE=common-plat`). Use `pnpm run install:common-plat` for local development and `pnpm run install:gitea` only when you intentionally want registry versions. Override the local checkout with `BYTELYST_COMMON_PLAT_ROOT=/path/to/learning_ai_common_plat`. + Do not move NoteLett-specific notes logic into common platform unless another product has a concrete reuse need. Do not create repo-local substitutes for platform concerns already covered by `@bytelyst/*` packages or platform services. ## Key Features diff --git a/package.json b/package.json index a8e4126..4766b64 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,8 @@ "typecheck": "pnpm --filter @notelett/backend run typecheck && pnpm --filter @notelett/web run typecheck && pnpm --filter @notelett/mobile run typecheck", "test": "pnpm --filter @notelett/backend run test && pnpm --filter @notelett/web run test && pnpm --filter @notelett/mobile run test", "build": "pnpm --filter @notelett/backend run build && pnpm --filter @notelett/web run build", + "install:common-plat": "BYTELYST_PACKAGE_SOURCE=common-plat pnpm install -r", + "install:gitea": "BYTELYST_PACKAGE_SOURCE=gitea pnpm install -r", "smoke:local": "bash scripts/local-smoke.sh", "smoke:compose": "bash scripts/compose-smoke.sh", "seed:bootstrap": "pnpm --filter @notelett/backend run bootstrap:seed", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78ec29d..9573393 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,8 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +pnpmfileChecksum: sha256-Xt9OkcnptxWDuyQe7GrPayC7CX5A1AhdKTLR4RheNnw= + importers: .: @@ -681,52 +683,52 @@ importers: specifier: ^4.2.0 version: 4.9.3 '@bytelyst/auth': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/auth '@bytelyst/backend-config': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/backend-config '@bytelyst/backend-flags': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/backend-flags '@bytelyst/backend-telemetry': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/backend-telemetry '@bytelyst/config': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/config '@bytelyst/cosmos': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/cosmos '@bytelyst/datastore': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/datastore '@bytelyst/errors': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/errors '@bytelyst/fastify-auth': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/fastify-auth '@bytelyst/fastify-core': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/fastify-core '@bytelyst/field-encrypt': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/field-encrypt '@bytelyst/llm': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/llm '@bytelyst/logger': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/logger '@bytelyst/monitoring': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/monitoring '@bytelyst/palace': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/palace '@bytelyst/webhook-dispatch': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/webhook-dispatch fastify: specifier: 5.7.4 @@ -739,7 +741,7 @@ importers: version: 3.25.76 devDependencies: '@bytelyst/testing': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/testing '@eslint/js': specifier: ^9.39.4 @@ -766,46 +768,46 @@ importers: mobile: dependencies: '@bytelyst/api-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/api-client '@bytelyst/auth-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/auth-client '@bytelyst/billing-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/billing-client '@bytelyst/blob-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/blob-client '@bytelyst/broadcast-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/broadcast-client '@bytelyst/design-tokens': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/design-tokens '@bytelyst/diagnostics-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/diagnostics-client '@bytelyst/feature-flag-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/feature-flag-client '@bytelyst/feedback-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/feedback-client '@bytelyst/kill-switch-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/kill-switch-client '@bytelyst/offline-queue': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/offline-queue '@bytelyst/platform-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/platform-client '@bytelyst/survey-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/survey-client '@bytelyst/telemetry-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/telemetry-client expo: specifier: ~55.0.4 @@ -884,55 +886,55 @@ importers: web: dependencies: '@bytelyst/api-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/api-client '@bytelyst/billing-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/billing-client '@bytelyst/blob-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/blob-client '@bytelyst/broadcast-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/broadcast-client '@bytelyst/dashboard-components': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/dashboard-components '@bytelyst/design-tokens': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/design-tokens '@bytelyst/diagnostics-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/diagnostics-client '@bytelyst/extraction': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/extraction '@bytelyst/feature-flag-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/feature-flag-client '@bytelyst/feedback-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/feedback-client '@bytelyst/kill-switch-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/kill-switch-client '@bytelyst/offline-queue': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/offline-queue '@bytelyst/platform-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/platform-client '@bytelyst/react-auth': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/react-auth '@bytelyst/survey-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/survey-client '@bytelyst/telemetry-client': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/telemetry-client '@bytelyst/ui': - specifier: '*' + specifier: workspace:* version: link:../../learning_ai/learning_ai_common_plat/packages/ui '@tiptap/extension-placeholder': specifier: ^2.11.0 @@ -9012,7 +9014,7 @@ snapshots: connect: 3.7.0 debug: 4.4.3 dnssd-advertise: 1.1.4 - expo: 55.0.20(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(expo-router@6.0.23)(react-dom@19.2.0(react@19.2.0))(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(react-native@0.83.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo: 55.0.20(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-dom@19.2.5(react@19.2.5))(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.5))(react@19.2.5))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.5))(react@19.2.5)(typescript@5.9.3) expo-server: 55.0.8 fetch-nodeshim: 0.4.10 getenv: 2.0.0 @@ -9088,7 +9090,7 @@ snapshots: connect: 3.7.0 debug: 4.4.3 dnssd-advertise: 1.1.4 - expo: 55.0.20(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-dom@19.2.5(react@19.2.5))(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.5))(react@19.2.5))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.5))(react@19.2.5)(typescript@5.9.3) + expo: 55.0.20(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(expo-router@6.0.23)(react-dom@19.2.0(react@19.2.0))(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(react-native@0.83.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) expo-server: 55.0.8 fetch-nodeshim: 0.4.10 getenv: 2.0.0 @@ -9232,13 +9234,13 @@ snapshots: '@expo/dom-webview@55.0.3(expo@55.0.20)(react-native@0.83.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)': dependencies: - expo: 55.0.20(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-dom@19.2.5(react@19.2.5))(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.5))(react@19.2.5))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.5))(react@19.2.5)(typescript@5.9.3) + expo: 55.0.20(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(expo-router@6.0.23)(react-dom@19.2.0(react@19.2.0))(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(react-native@0.83.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) react: 19.2.0 react-native: 0.83.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) '@expo/dom-webview@55.0.3(expo@55.0.20)(react-native@0.85.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.5))(react@19.2.5)': dependencies: - expo: 55.0.20(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(expo-router@6.0.23)(react-dom@19.2.0(react@19.2.0))(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(react-native@0.83.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo: 55.0.20(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-dom@19.2.5(react@19.2.5))(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.5))(react@19.2.5))(react-native@0.85.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.5))(react@19.2.5)(typescript@5.9.3) react: 19.2.5 react-native: 0.85.2(@babel/core@7.29.0)(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.5) @@ -10575,9 +10577,7 @@ snapshots: metro-runtime: 0.83.7 transitivePeerDependencies: - '@babel/core' - - bufferutil - supports-color - - utf-8-validate '@react-native/normalize-colors@0.83.2': {}