@happyvertical/smrt-core 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +124 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +265 -0
- package/bin/smrt-prebuild.js +26 -0
- package/dist/__tests__/fixtures/advisor-test-classes.d.ts +28 -0
- package/dist/__tests__/fixtures/advisor-test-classes.d.ts.map +1 -0
- package/dist/__tests__/fixtures/collection-coverage-fixtures.d.ts +12 -0
- package/dist/__tests__/fixtures/collection-coverage-fixtures.d.ts.map +1 -0
- package/dist/__tests__/fixtures/inheritance-resolver-fixtures.d.ts +28 -0
- package/dist/__tests__/fixtures/inheritance-resolver-fixtures.d.ts.map +1 -0
- package/dist/__tests__/fixtures/inheritance-test-classes.d.ts +43 -0
- package/dist/__tests__/fixtures/inheritance-test-classes.d.ts.map +1 -0
- package/dist/__tests__/fixtures/mcp-integration-test-classes.d.ts +18 -0
- package/dist/__tests__/fixtures/mcp-integration-test-classes.d.ts.map +1 -0
- package/dist/__tests__/fixtures/object-ai-memory-fixtures.d.ts +15 -0
- package/dist/__tests__/fixtures/object-ai-memory-fixtures.d.ts.map +1 -0
- package/dist/__tests__/fixtures/object-spec-test-classes.d.ts +13 -0
- package/dist/__tests__/fixtures/object-spec-test-classes.d.ts.map +1 -0
- package/dist/__tests__/fixtures/registry-test-classes.d.ts +23 -0
- package/dist/__tests__/fixtures/registry-test-classes.d.ts.map +1 -0
- package/dist/adapters/ai-usage.d.ts +23 -0
- package/dist/adapters/ai-usage.d.ts.map +1 -0
- package/dist/adapters/ai-usage.js +105 -0
- package/dist/adapters/ai-usage.js.map +1 -0
- package/dist/adapters/cost-rates.d.ts +20 -0
- package/dist/adapters/cost-rates.d.ts.map +1 -0
- package/dist/adapters/cost-rates.js +40 -0
- package/dist/adapters/cost-rates.js.map +1 -0
- package/dist/adapters/index.d.ts +19 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/metrics.d.ts +111 -0
- package/dist/adapters/metrics.d.ts.map +1 -0
- package/dist/adapters/metrics.js +169 -0
- package/dist/adapters/metrics.js.map +1 -0
- package/dist/adapters/pubsub.d.ts +124 -0
- package/dist/adapters/pubsub.d.ts.map +1 -0
- package/dist/adapters/pubsub.js +121 -0
- package/dist/adapters/pubsub.js.map +1 -0
- package/dist/browser.d.ts +32 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +68 -0
- package/dist/browser.js.map +1 -0
- package/dist/child-accessors.d.ts +27 -0
- package/dist/child-accessors.d.ts.map +1 -0
- package/dist/child-accessors.js +35 -0
- package/dist/child-accessors.js.map +1 -0
- package/dist/class.d.ts +313 -0
- package/dist/class.d.ts.map +1 -0
- package/dist/class.js +896 -0
- package/dist/class.js.map +1 -0
- package/dist/collection-cache.d.ts +110 -0
- package/dist/collection-cache.d.ts.map +1 -0
- package/dist/collection-cache.js +187 -0
- package/dist/collection-cache.js.map +1 -0
- package/dist/collection.d.ts +894 -0
- package/dist/collection.d.ts.map +1 -0
- package/dist/collection.js +1987 -0
- package/dist/collection.js.map +1 -0
- package/dist/config/global-config.d.ts +3 -0
- package/dist/config/global-config.d.ts.map +1 -0
- package/dist/config/global-config.js +19 -0
- package/dist/config/global-config.js.map +1 -0
- package/dist/config.d.ts +145 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +57 -0
- package/dist/config.js.map +1 -0
- package/dist/consumer-plugin/index.d.ts +22 -0
- package/dist/consumer-plugin/index.d.ts.map +1 -0
- package/dist/consumer-plugin/index.js +452 -0
- package/dist/consumer-plugin/index.js.map +1 -0
- package/dist/consumer-plugin.d.ts +8 -0
- package/dist/consumer-plugin.d.ts.map +1 -0
- package/dist/consumer-plugin.js +5 -0
- package/dist/consumer-plugin.js.map +1 -0
- package/dist/database.d.ts +95 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +32 -0
- package/dist/database.js.map +1 -0
- package/dist/decorators/compatibility.d.ts +14 -0
- package/dist/decorators/compatibility.d.ts.map +1 -0
- package/dist/decorators/compatibility.js +111 -0
- package/dist/decorators/compatibility.js.map +1 -0
- package/dist/decorators/index.d.ts +381 -0
- package/dist/decorators/index.d.ts.map +1 -0
- package/dist/decorators/index.js +104 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/dispatch/bus.d.ts +306 -0
- package/dist/dispatch/bus.d.ts.map +1 -0
- package/dist/dispatch/bus.js +583 -0
- package/dist/dispatch/bus.js.map +1 -0
- package/dist/dispatch/collections/DispatchSubscriptions.d.ts +79 -0
- package/dist/dispatch/collections/DispatchSubscriptions.d.ts.map +1 -0
- package/dist/dispatch/collections/DispatchSubscriptions.js +243 -0
- package/dist/dispatch/collections/DispatchSubscriptions.js.map +1 -0
- package/dist/dispatch/collections/Dispatches.d.ts +98 -0
- package/dist/dispatch/collections/Dispatches.d.ts.map +1 -0
- package/dist/dispatch/collections/Dispatches.js +358 -0
- package/dist/dispatch/collections/Dispatches.js.map +1 -0
- package/dist/dispatch/index.d.ts +47 -0
- package/dist/dispatch/index.d.ts.map +1 -0
- package/dist/dispatch/models/Dispatch.d.ts +101 -0
- package/dist/dispatch/models/Dispatch.d.ts.map +1 -0
- package/dist/dispatch/models/Dispatch.js +162 -0
- package/dist/dispatch/models/Dispatch.js.map +1 -0
- package/dist/dispatch/models/DispatchSubscription.d.ts +83 -0
- package/dist/dispatch/models/DispatchSubscription.d.ts.map +1 -0
- package/dist/dispatch/models/DispatchSubscription.js +112 -0
- package/dist/dispatch/models/DispatchSubscription.js.map +1 -0
- package/dist/dispatch/tenant-resolver.d.ts +98 -0
- package/dist/dispatch/tenant-resolver.d.ts.map +1 -0
- package/dist/dispatch/tenant-resolver.js +32 -0
- package/dist/dispatch/tenant-resolver.js.map +1 -0
- package/dist/dispatch/types.d.ts +149 -0
- package/dist/dispatch/types.d.ts.map +1 -0
- package/dist/embeddings/hash.d.ts +33 -0
- package/dist/embeddings/hash.d.ts.map +1 -0
- package/dist/embeddings/hash.js +37 -0
- package/dist/embeddings/hash.js.map +1 -0
- package/dist/embeddings/index.d.ts +36 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/provider.d.ts +75 -0
- package/dist/embeddings/provider.d.ts.map +1 -0
- package/dist/embeddings/provider.js +170 -0
- package/dist/embeddings/provider.js.map +1 -0
- package/dist/embeddings/similarity.d.ts +47 -0
- package/dist/embeddings/similarity.d.ts.map +1 -0
- package/dist/embeddings/similarity.js +64 -0
- package/dist/embeddings/similarity.js.map +1 -0
- package/dist/embeddings/storage.d.ts +125 -0
- package/dist/embeddings/storage.d.ts.map +1 -0
- package/dist/embeddings/storage.js +283 -0
- package/dist/embeddings/storage.js.map +1 -0
- package/dist/embeddings/types.d.ts +250 -0
- package/dist/embeddings/types.d.ts.map +1 -0
- package/dist/errors.d.ts +363 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +669 -0
- package/dist/errors.js.map +1 -0
- package/dist/generators/cli.d.ts +162 -0
- package/dist/generators/cli.d.ts.map +1 -0
- package/dist/generators/cli.js +462 -0
- package/dist/generators/cli.js.map +1 -0
- package/dist/generators/index.d.ts +13 -0
- package/dist/generators/index.d.ts.map +1 -0
- package/dist/generators/mcp-runtime-template.d.ts +60 -0
- package/dist/generators/mcp-runtime-template.d.ts.map +1 -0
- package/dist/generators/mcp-runtime-template.js +509 -0
- package/dist/generators/mcp-runtime-template.js.map +1 -0
- package/dist/generators/mcp.d.ts +231 -0
- package/dist/generators/mcp.d.ts.map +1 -0
- package/dist/generators/mcp.js +1220 -0
- package/dist/generators/mcp.js.map +1 -0
- package/dist/generators/rest.d.ts +171 -0
- package/dist/generators/rest.d.ts.map +1 -0
- package/dist/generators/rest.js +591 -0
- package/dist/generators/rest.js.map +1 -0
- package/dist/generators/swagger.d.ts +21 -0
- package/dist/generators/swagger.d.ts.map +1 -0
- package/dist/generators/swagger.js +307 -0
- package/dist/generators/swagger.js.map +1 -0
- package/dist/generators/tenant-gate.d.ts +74 -0
- package/dist/generators/tenant-gate.d.ts.map +1 -0
- package/dist/generators/tenant-gate.js +15 -0
- package/dist/generators/tenant-gate.js.map +1 -0
- package/dist/generators.d.ts +8 -0
- package/dist/generators.d.ts.map +1 -0
- package/dist/generators.js +19 -0
- package/dist/generators.js.map +1 -0
- package/dist/hierarchical.d.ts +103 -0
- package/dist/hierarchical.d.ts.map +1 -0
- package/dist/hierarchical.js +184 -0
- package/dist/hierarchical.js.map +1 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +202 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptors.d.ts +251 -0
- package/dist/interceptors.d.ts.map +1 -0
- package/dist/interceptors.js +259 -0
- package/dist/interceptors.js.map +1 -0
- package/dist/junction.d.ts +99 -0
- package/dist/junction.d.ts.map +1 -0
- package/dist/junction.js +136 -0
- package/dist/junction.js.map +1 -0
- package/dist/knowledge.d.ts +11 -0
- package/dist/knowledge.d.ts.map +1 -0
- package/dist/knowledge.js +310 -0
- package/dist/knowledge.js.map +1 -0
- package/dist/lazy-config.d.ts +160 -0
- package/dist/lazy-config.d.ts.map +1 -0
- package/dist/lazy-config.js +146 -0
- package/dist/lazy-config.js.map +1 -0
- package/dist/manifest/discover-base-classes.d.ts +78 -0
- package/dist/manifest/discover-base-classes.d.ts.map +1 -0
- package/dist/manifest/discover-base-classes.js +85 -0
- package/dist/manifest/discover-base-classes.js.map +1 -0
- package/dist/manifest/discover-smrt-packages.d.ts +48 -0
- package/dist/manifest/discover-smrt-packages.d.ts.map +1 -0
- package/dist/manifest/discover-smrt-packages.js +361 -0
- package/dist/manifest/discover-smrt-packages.js.map +1 -0
- package/dist/manifest/generator.d.ts +93 -0
- package/dist/manifest/generator.d.ts.map +1 -0
- package/dist/manifest/generator.js +380 -0
- package/dist/manifest/generator.js.map +1 -0
- package/dist/manifest/index.d.ts +16 -0
- package/dist/manifest/index.d.ts.map +1 -0
- package/dist/manifest/index.js +51 -0
- package/dist/manifest/index.js.map +1 -0
- package/dist/manifest/manager.d.ts +51 -0
- package/dist/manifest/manager.d.ts.map +1 -0
- package/dist/manifest/manager.js +89 -0
- package/dist/manifest/manager.js.map +1 -0
- package/dist/manifest/manifest-loader.d.ts +187 -0
- package/dist/manifest/manifest-loader.d.ts.map +1 -0
- package/dist/manifest/manifest-loader.js +847 -0
- package/dist/manifest/manifest-loader.js.map +1 -0
- package/dist/manifest/sources/composite.d.ts +22 -0
- package/dist/manifest/sources/composite.d.ts.map +1 -0
- package/dist/manifest/sources/composite.js +60 -0
- package/dist/manifest/sources/composite.js.map +1 -0
- package/dist/manifest/sources/embedded.d.ts +7 -0
- package/dist/manifest/sources/embedded.d.ts.map +1 -0
- package/dist/manifest/sources/embedded.js +30 -0
- package/dist/manifest/sources/embedded.js.map +1 -0
- package/dist/manifest/sources/explicit-paths.d.ts +17 -0
- package/dist/manifest/sources/explicit-paths.d.ts.map +1 -0
- package/dist/manifest/sources/explicit-paths.js +35 -0
- package/dist/manifest/sources/explicit-paths.js.map +1 -0
- package/dist/manifest/sources/fallback.d.ts +25 -0
- package/dist/manifest/sources/fallback.d.ts.map +1 -0
- package/dist/manifest/sources/fallback.js +63 -0
- package/dist/manifest/sources/fallback.js.map +1 -0
- package/dist/manifest/sources/index.d.ts +17 -0
- package/dist/manifest/sources/index.d.ts.map +1 -0
- package/dist/manifest/sources/local-test.d.ts +7 -0
- package/dist/manifest/sources/local-test.d.ts.map +1 -0
- package/dist/manifest/sources/local-test.js +21 -0
- package/dist/manifest/sources/local-test.js.map +1 -0
- package/dist/manifest/sources/static.d.ts +7 -0
- package/dist/manifest/sources/static.d.ts.map +1 -0
- package/dist/manifest/sources/static.js +19 -0
- package/dist/manifest/sources/static.js.map +1 -0
- package/dist/manifest/sources/test.d.ts +7 -0
- package/dist/manifest/sources/test.d.ts.map +1 -0
- package/dist/manifest/sources/test.js +21 -0
- package/dist/manifest/sources/test.js.map +1 -0
- package/dist/manifest/sources/types.d.ts +79 -0
- package/dist/manifest/sources/types.d.ts.map +1 -0
- package/dist/manifest/sources/types.js +61 -0
- package/dist/manifest/sources/types.js.map +1 -0
- package/dist/manifest/static-manifest.d.ts +4 -0
- package/dist/manifest/static-manifest.d.ts.map +1 -0
- package/dist/manifest/static-manifest.js +1535 -0
- package/dist/manifest/static-manifest.js.map +1 -0
- package/dist/manifest/store.d.ts +111 -0
- package/dist/manifest/store.d.ts.map +1 -0
- package/dist/manifest/store.js +431 -0
- package/dist/manifest/store.js.map +1 -0
- package/dist/manifest/test-manifest-loader.d.ts +3 -0
- package/dist/manifest/test-manifest-loader.d.ts.map +1 -0
- package/dist/manifest/test-manifest-stub.d.ts +4 -0
- package/dist/manifest/test-manifest-stub.d.ts.map +1 -0
- package/dist/manifest/test-manifest-stub.js +80013 -0
- package/dist/manifest/test-manifest-stub.js.map +1 -0
- package/dist/manifest.d.ts +8 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +20 -0
- package/dist/manifest.js.map +1 -0
- package/dist/manifest.json +1489 -0
- package/dist/mcp-advisor/index.d.ts +499 -0
- package/dist/mcp-advisor/index.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/add-ai-methods.d.ts +6 -0
- package/dist/mcp-advisor/tools/add-ai-methods.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/configure-decorators.d.ts +6 -0
- package/dist/mcp-advisor/tools/configure-decorators.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/generate-collection.d.ts +6 -0
- package/dist/mcp-advisor/tools/generate-collection.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/generate-field-definitions.d.ts +6 -0
- package/dist/mcp-advisor/tools/generate-field-definitions.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/generate-smrt-class.d.ts +6 -0
- package/dist/mcp-advisor/tools/generate-smrt-class.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/get-object-config.d.ts +6 -0
- package/dist/mcp-advisor/tools/get-object-config.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/get-object-schema.d.ts +10 -0
- package/dist/mcp-advisor/tools/get-object-schema.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/list-registered-objects.d.ts +9 -0
- package/dist/mcp-advisor/tools/list-registered-objects.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/preview-api-endpoints.d.ts +9 -0
- package/dist/mcp-advisor/tools/preview-api-endpoints.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/preview-mcp-tools.d.ts +9 -0
- package/dist/mcp-advisor/tools/preview-mcp-tools.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/validate-smrt-object.d.ts +6 -0
- package/dist/mcp-advisor/tools/validate-smrt-object.d.ts.map +1 -0
- package/dist/mcp-advisor/types.d.ts +209 -0
- package/dist/mcp-advisor/types.d.ts.map +1 -0
- package/dist/migrations/backfill-tracker.d.ts +84 -0
- package/dist/migrations/backfill-tracker.d.ts.map +1 -0
- package/dist/migrations/backfill-tracker.js +118 -0
- package/dist/migrations/backfill-tracker.js.map +1 -0
- package/dist/migrations/checksum.d.ts +43 -0
- package/dist/migrations/checksum.d.ts.map +1 -0
- package/dist/migrations/checksum.js +32 -0
- package/dist/migrations/checksum.js.map +1 -0
- package/dist/migrations/differ.d.ts +186 -0
- package/dist/migrations/differ.d.ts.map +1 -0
- package/dist/migrations/differ.js +601 -0
- package/dist/migrations/differ.js.map +1 -0
- package/dist/migrations/generator.d.ts +133 -0
- package/dist/migrations/generator.d.ts.map +1 -0
- package/dist/migrations/generator.js +328 -0
- package/dist/migrations/generator.js.map +1 -0
- package/dist/migrations/index.d.ts +20 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/orchestrate.d.ts +148 -0
- package/dist/migrations/orchestrate.d.ts.map +1 -0
- package/dist/migrations/orchestrate.js +118 -0
- package/dist/migrations/orchestrate.js.map +1 -0
- package/dist/migrations/tracker.d.ts +134 -0
- package/dist/migrations/tracker.d.ts.map +1 -0
- package/dist/migrations/tracker.js +624 -0
- package/dist/migrations/tracker.js.map +1 -0
- package/dist/migrations/types.d.ts +221 -0
- package/dist/migrations/types.d.ts.map +1 -0
- package/dist/migrations.d.ts +7 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +26 -0
- package/dist/migrations.js.map +1 -0
- package/dist/node_modules/.pnpm/balanced-match@4.0.4/node_modules/balanced-match/dist/esm/index.js +56 -0
- package/dist/node_modules/.pnpm/balanced-match@4.0.4/node_modules/balanced-match/dist/esm/index.js.map +1 -0
- package/dist/node_modules/.pnpm/brace-expansion@5.0.5/node_modules/brace-expansion/dist/esm/index.js +163 -0
- package/dist/node_modules/.pnpm/brace-expansion@5.0.5/node_modules/brace-expansion/dist/esm/index.js.map +1 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/assert-valid-pattern.js +13 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/assert-valid-pattern.js.map +1 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/ast.js +654 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/ast.js.map +1 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/brace-expressions.js +111 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/brace-expressions.js.map +1 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/escape.js +10 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/escape.js.map +1 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/index.js +824 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/index.js.map +1 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/unescape.js +10 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/unescape.js.map +1 -0
- package/dist/object.d.ts +1202 -0
- package/dist/object.d.ts.map +1 -0
- package/dist/object.js +2731 -0
- package/dist/object.js.map +1 -0
- package/dist/polymorphic-association.d.ts +69 -0
- package/dist/polymorphic-association.d.ts.map +1 -0
- package/dist/polymorphic-association.js +96 -0
- package/dist/polymorphic-association.js.map +1 -0
- package/dist/prebuild/cli.d.ts +7 -0
- package/dist/prebuild/cli.d.ts.map +1 -0
- package/dist/prebuild/cli.js +29 -0
- package/dist/prebuild/cli.js.map +1 -0
- package/dist/prebuild/index.d.ts +22 -0
- package/dist/prebuild/index.d.ts.map +1 -0
- package/dist/prebuild/index.js +239 -0
- package/dist/prebuild/index.js.map +1 -0
- package/dist/prebuild.d.ts +8 -0
- package/dist/prebuild.d.ts.map +1 -0
- package/dist/prebuild.js +6 -0
- package/dist/prebuild.js.map +1 -0
- package/dist/registry/cache-config.d.ts +13 -0
- package/dist/registry/cache-config.d.ts.map +1 -0
- package/dist/registry/cache-config.js +17 -0
- package/dist/registry/cache-config.js.map +1 -0
- package/dist/registry/class-registration.d.ts +31 -0
- package/dist/registry/class-registration.d.ts.map +1 -0
- package/dist/registry/class-registration.js +1074 -0
- package/dist/registry/class-registration.js.map +1 -0
- package/dist/registry/collection-resolution.d.ts +45 -0
- package/dist/registry/collection-resolution.d.ts.map +1 -0
- package/dist/registry/collection-resolution.js +121 -0
- package/dist/registry/collection-resolution.js.map +1 -0
- package/dist/registry/collision-policy.d.ts +179 -0
- package/dist/registry/collision-policy.d.ts.map +1 -0
- package/dist/registry/collision-policy.js +153 -0
- package/dist/registry/collision-policy.js.map +1 -0
- package/dist/registry/diagnostics.d.ts +58 -0
- package/dist/registry/diagnostics.d.ts.map +1 -0
- package/dist/registry/diagnostics.js +54 -0
- package/dist/registry/diagnostics.js.map +1 -0
- package/dist/registry/embedding-manager.d.ts +23 -0
- package/dist/registry/embedding-manager.d.ts.map +1 -0
- package/dist/registry/embedding-manager.js +62 -0
- package/dist/registry/embedding-manager.js.map +1 -0
- package/dist/registry/index.d.ts +13 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/inheritance-resolver.d.ts +13 -0
- package/dist/registry/inheritance-resolver.d.ts.map +1 -0
- package/dist/registry/inheritance-resolver.js +244 -0
- package/dist/registry/inheritance-resolver.js.map +1 -0
- package/dist/registry/manifest-field-merge.d.ts +4 -0
- package/dist/registry/manifest-field-merge.d.ts.map +1 -0
- package/dist/registry/manifest-field-merge.js +82 -0
- package/dist/registry/manifest-field-merge.js.map +1 -0
- package/dist/registry/name-resolver.d.ts +102 -0
- package/dist/registry/name-resolver.d.ts.map +1 -0
- package/dist/registry/name-resolver.js +241 -0
- package/dist/registry/name-resolver.js.map +1 -0
- package/dist/registry/relationship-graph.d.ts +16 -0
- package/dist/registry/relationship-graph.d.ts.map +1 -0
- package/dist/registry/relationship-graph.js +79 -0
- package/dist/registry/relationship-graph.js.map +1 -0
- package/dist/registry/schema-builder.d.ts +113 -0
- package/dist/registry/schema-builder.d.ts.map +1 -0
- package/dist/registry/schema-builder.js +474 -0
- package/dist/registry/schema-builder.js.map +1 -0
- package/dist/registry/shared-state.d.ts +62 -0
- package/dist/registry/shared-state.d.ts.map +1 -0
- package/dist/registry/shared-state.js +135 -0
- package/dist/registry/shared-state.js.map +1 -0
- package/dist/registry/types.d.ts +667 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/validator.d.ts +13 -0
- package/dist/registry/validator.d.ts.map +1 -0
- package/dist/registry/validator.js +138 -0
- package/dist/registry/validator.js.map +1 -0
- package/dist/registry.d.ts +1358 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +2301 -0
- package/dist/registry.js.map +1 -0
- package/dist/runtime/client.d.ts +34 -0
- package/dist/runtime/client.d.ts.map +1 -0
- package/dist/runtime/client.js +104 -0
- package/dist/runtime/client.js.map +1 -0
- package/dist/runtime/index.d.ts +10 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/mcp.d.ts +47 -0
- package/dist/runtime/mcp.d.ts.map +1 -0
- package/dist/runtime/mcp.js +72 -0
- package/dist/runtime/mcp.js.map +1 -0
- package/dist/runtime/server.d.ts +92 -0
- package/dist/runtime/server.d.ts.map +1 -0
- package/dist/runtime/server.js +390 -0
- package/dist/runtime/server.js.map +1 -0
- package/dist/runtime/types.d.ts +58 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime.d.ts +8 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +10 -0
- package/dist/runtime.js.map +1 -0
- package/dist/scanner/import-scanner.d.ts +7 -0
- package/dist/scanner/import-scanner.d.ts.map +1 -0
- package/dist/scanner/index.d.ts +12 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/manifest-generator.d.ts +304 -0
- package/dist/scanner/manifest-generator.d.ts.map +1 -0
- package/dist/scanner/manifest-generator.js +1707 -0
- package/dist/scanner/manifest-generator.js.map +1 -0
- package/dist/scanner/test-file-patterns.d.ts +18 -0
- package/dist/scanner/test-file-patterns.d.ts.map +1 -0
- package/dist/scanner/test-file-patterns.js +16 -0
- package/dist/scanner/test-file-patterns.js.map +1 -0
- package/dist/scanner/types.d.ts +313 -0
- package/dist/scanner/types.d.ts.map +1 -0
- package/dist/scanner/types.js +2 -0
- package/dist/scanner/types.js.map +1 -0
- package/dist/scanner.d.ts +6 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +6 -0
- package/dist/scanner.js.map +1 -0
- package/dist/schema/code-generator.d.ts +53 -0
- package/dist/schema/code-generator.d.ts.map +1 -0
- package/dist/schema/ddl/base-strategy.d.ts +80 -0
- package/dist/schema/ddl/base-strategy.d.ts.map +1 -0
- package/dist/schema/ddl/base-strategy.js +240 -0
- package/dist/schema/ddl/base-strategy.js.map +1 -0
- package/dist/schema/ddl/duckdb-strategy.d.ts +33 -0
- package/dist/schema/ddl/duckdb-strategy.d.ts.map +1 -0
- package/dist/schema/ddl/duckdb-strategy.js +74 -0
- package/dist/schema/ddl/duckdb-strategy.js.map +1 -0
- package/dist/schema/ddl/index.d.ts +53 -0
- package/dist/schema/ddl/index.d.ts.map +1 -0
- package/dist/schema/ddl/index.js +80 -0
- package/dist/schema/ddl/index.js.map +1 -0
- package/dist/schema/ddl/json-duckdb-strategy.d.ts +8 -0
- package/dist/schema/ddl/json-duckdb-strategy.d.ts.map +1 -0
- package/dist/schema/ddl/json-duckdb-strategy.js +14 -0
- package/dist/schema/ddl/json-duckdb-strategy.js.map +1 -0
- package/dist/schema/ddl/postgres-strategy.d.ts +29 -0
- package/dist/schema/ddl/postgres-strategy.d.ts.map +1 -0
- package/dist/schema/ddl/postgres-strategy.js +102 -0
- package/dist/schema/ddl/postgres-strategy.js.map +1 -0
- package/dist/schema/ddl/sqlite-strategy.d.ts +38 -0
- package/dist/schema/ddl/sqlite-strategy.d.ts.map +1 -0
- package/dist/schema/ddl/sqlite-strategy.js +74 -0
- package/dist/schema/ddl/sqlite-strategy.js.map +1 -0
- package/dist/schema/ddl/types.d.ts +114 -0
- package/dist/schema/ddl/types.d.ts.map +1 -0
- package/dist/schema/generator.d.ts +176 -0
- package/dist/schema/generator.d.ts.map +1 -0
- package/dist/schema/generator.js +1076 -0
- package/dist/schema/generator.js.map +1 -0
- package/dist/schema/index-utils.d.ts +19 -0
- package/dist/schema/index-utils.d.ts.map +1 -0
- package/dist/schema/index-utils.js +32 -0
- package/dist/schema/index-utils.js.map +1 -0
- package/dist/schema/index.d.ts +13 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/override-system.d.ts +43 -0
- package/dist/schema/override-system.d.ts.map +1 -0
- package/dist/schema/schema-aggregator.d.ts +112 -0
- package/dist/schema/schema-aggregator.d.ts.map +1 -0
- package/dist/schema/schema-manager.d.ts +95 -0
- package/dist/schema/schema-manager.d.ts.map +1 -0
- package/dist/schema/schema-manager.js +283 -0
- package/dist/schema/schema-manager.js.map +1 -0
- package/dist/schema/sql-identifiers.d.ts +107 -0
- package/dist/schema/sql-identifiers.d.ts.map +1 -0
- package/dist/schema/sql-identifiers.js +190 -0
- package/dist/schema/sql-identifiers.js.map +1 -0
- package/dist/schema/table-verifier.d.ts +10 -0
- package/dist/schema/table-verifier.d.ts.map +1 -0
- package/dist/schema/table-verifier.js +37 -0
- package/dist/schema/table-verifier.js.map +1 -0
- package/dist/schema/types.d.ts +241 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/utils.d.ts +32 -0
- package/dist/schema/utils.d.ts.map +1 -0
- package/dist/schema/utils.js +134 -0
- package/dist/schema/utils.js.map +1 -0
- package/dist/scripts/create-wrappers.js +89 -0
- package/dist/scripts/generate-manifest.js +155 -0
- package/dist/scripts/generate-test-manifest.js +77 -0
- package/dist/scripts/migrate-datetime-to-timestamp.ts +310 -0
- package/dist/scripts/prepack.js +49 -0
- package/dist/signals/bus.d.ts +64 -0
- package/dist/signals/bus.d.ts.map +1 -0
- package/dist/signals/bus.js +102 -0
- package/dist/signals/bus.js.map +1 -0
- package/dist/signals/index.d.ts +11 -0
- package/dist/signals/index.d.ts.map +1 -0
- package/dist/signals/sanitizer.d.ts +54 -0
- package/dist/signals/sanitizer.d.ts.map +1 -0
- package/dist/signals/sanitizer.js +111 -0
- package/dist/signals/sanitizer.js.map +1 -0
- package/dist/smrt-knowledge.json +335 -0
- package/dist/system/compatibility.d.ts +8 -0
- package/dist/system/compatibility.d.ts.map +1 -0
- package/dist/system/compatibility.js +409 -0
- package/dist/system/compatibility.js.map +1 -0
- package/dist/system/index.d.ts +9 -0
- package/dist/system/index.d.ts.map +1 -0
- package/dist/system/schema.d.ts +69 -0
- package/dist/system/schema.d.ts.map +1 -0
- package/dist/system/schema.js +271 -0
- package/dist/system/schema.js.map +1 -0
- package/dist/system/types.d.ts +135 -0
- package/dist/system/types.d.ts.map +1 -0
- package/dist/system-fields.d.ts +44 -0
- package/dist/system-fields.d.ts.map +1 -0
- package/dist/system-fields.js +55 -0
- package/dist/system-fields.js.map +1 -0
- package/dist/table-cache.d.ts +28 -0
- package/dist/table-cache.d.ts.map +1 -0
- package/dist/table-cache.js +21 -0
- package/dist/table-cache.js.map +1 -0
- package/dist/test-utils.d.ts +140 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/testing/database.d.ts +73 -0
- package/dist/testing/database.d.ts.map +1 -0
- package/dist/testing/database.js +204 -0
- package/dist/testing/database.js.map +1 -0
- package/dist/testing/index.d.ts +21 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing.d.ts +6 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +5 -0
- package/dist/testing.js.map +1 -0
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/tool-executor.d.ts +101 -0
- package/dist/tools/tool-executor.d.ts.map +1 -0
- package/dist/tools/tool-executor.js +142 -0
- package/dist/tools/tool-executor.js.map +1 -0
- package/dist/tools/tool-generator.d.ts +54 -0
- package/dist/tools/tool-generator.d.ts.map +1 -0
- package/dist/tools/tool-generator.js +121 -0
- package/dist/tools/tool-generator.js.map +1 -0
- package/dist/utils/chunk.d.ts +32 -0
- package/dist/utils/chunk.d.ts.map +1 -0
- package/dist/utils/chunk.js +14 -0
- package/dist/utils/chunk.js.map +1 -0
- package/dist/utils/import-workspace-module.d.ts +8 -0
- package/dist/utils/import-workspace-module.d.ts.map +1 -0
- package/dist/utils/import-workspace-module.js +81 -0
- package/dist/utils/import-workspace-module.js.map +1 -0
- package/dist/utils/json.d.ts +102 -0
- package/dist/utils/json.d.ts.map +1 -0
- package/dist/utils/json.js +43 -0
- package/dist/utils/json.js.map +1 -0
- package/dist/utils/lru-cache.d.ts +69 -0
- package/dist/utils/lru-cache.d.ts.map +1 -0
- package/dist/utils/lru-cache.js +100 -0
- package/dist/utils/lru-cache.js.map +1 -0
- package/dist/utils/naming.d.ts +16 -0
- package/dist/utils/naming.d.ts.map +1 -0
- package/dist/utils/naming.js +23 -0
- package/dist/utils/naming.js.map +1 -0
- package/dist/utils/qualified-names.d.ts +122 -0
- package/dist/utils/qualified-names.d.ts.map +1 -0
- package/dist/utils/qualified-names.js +82 -0
- package/dist/utils/qualified-names.js.map +1 -0
- package/dist/utils/scanner-module.d.ts +37 -0
- package/dist/utils/scanner-module.d.ts.map +1 -0
- package/dist/utils.d.ts +177 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +185 -0
- package/dist/utils.js.map +1 -0
- package/dist/vite-plugin/import-build-aware.d.ts +68 -0
- package/dist/vite-plugin/import-build-aware.d.ts.map +1 -0
- package/dist/vite-plugin/import-build-aware.js +72 -0
- package/dist/vite-plugin/import-build-aware.js.map +1 -0
- package/dist/vite-plugin/index.d.ts +59 -0
- package/dist/vite-plugin/index.d.ts.map +1 -0
- package/dist/vite-plugin/index.js +1400 -0
- package/dist/vite-plugin/index.js.map +1 -0
- package/dist/vite-plugin/sveltekit-generator.d.ts +66 -0
- package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -0
- package/dist/vite-plugin/sveltekit-generator.js +1375 -0
- package/dist/vite-plugin/sveltekit-generator.js.map +1 -0
- package/dist/vite-plugin/templates/default-ui.ts +432 -0
- package/dist/vite-plugin/templates/default.html +206 -0
- package/dist/vite-plugin.d.ts +8 -0
- package/dist/vite-plugin.d.ts.map +1 -0
- package/dist/vite-plugin.js +11 -0
- package/dist/vite-plugin.js.map +1 -0
- package/package.json +208 -0
|
@@ -0,0 +1,1707 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { loadExternalManifestSync, lookupInManifest } from "../manifest/manifest-loader.js";
|
|
4
|
+
import { SchemaGenerator } from "../schema/generator.js";
|
|
5
|
+
import { generateToolManifest } from "../tools/tool-generator.js";
|
|
6
|
+
import { toSnakeCase, classnameToTablename } from "../utils/naming.js";
|
|
7
|
+
import { createQualifiedName } from "../utils/qualified-names.js";
|
|
8
|
+
import { isTestFile } from "./test-file-patterns.js";
|
|
9
|
+
const require$1 = createRequire(import.meta.url);
|
|
10
|
+
const FRAMEWORK_ABSTRACT_BASE_NAMES = /* @__PURE__ */ new Set([
|
|
11
|
+
"SmrtJunction",
|
|
12
|
+
"SmrtHierarchical",
|
|
13
|
+
"SmrtPolymorphicAssociation"
|
|
14
|
+
]);
|
|
15
|
+
function inferVisibility(filePath, explicitVisibility) {
|
|
16
|
+
if (explicitVisibility) return explicitVisibility;
|
|
17
|
+
if (isTestFile(filePath)) return "test";
|
|
18
|
+
return "public";
|
|
19
|
+
}
|
|
20
|
+
class ManifestGenerator {
|
|
21
|
+
/**
|
|
22
|
+
* Generate manifest from scan results
|
|
23
|
+
*
|
|
24
|
+
* @param scanResults - Array of scan results containing object definitions
|
|
25
|
+
* @param options - Optional configuration
|
|
26
|
+
* @param options.packageName - Package name to inject into manifest and object definitions
|
|
27
|
+
* @param options.packageVersion - Package version
|
|
28
|
+
* @param options.packageJson - Full package.json object for determining import paths
|
|
29
|
+
* @param options.smrtDependencies - List of SMRT package dependencies to include in manifest
|
|
30
|
+
* @param options.includeVisibility - Array of visibility levels to include (default: all)
|
|
31
|
+
* - For published packages: ['public']
|
|
32
|
+
* - For development: ['public', 'internal', 'test'] or omit for all
|
|
33
|
+
*/
|
|
34
|
+
generateManifest(scanResults, options) {
|
|
35
|
+
const manifest = {
|
|
36
|
+
version: "1.0.0",
|
|
37
|
+
timestamp: Date.now(),
|
|
38
|
+
objects: {}
|
|
39
|
+
};
|
|
40
|
+
if (options?.packageName) {
|
|
41
|
+
manifest.packageName = options.packageName;
|
|
42
|
+
}
|
|
43
|
+
if (options?.packageVersion) {
|
|
44
|
+
manifest.packageVersion = options.packageVersion;
|
|
45
|
+
}
|
|
46
|
+
if (options?.smrtDependencies) {
|
|
47
|
+
manifest.smrtDependencies = options.smrtDependencies;
|
|
48
|
+
}
|
|
49
|
+
for (const result of scanResults) {
|
|
50
|
+
for (const objectDef of result.objects) {
|
|
51
|
+
if (options?.packageName) {
|
|
52
|
+
objectDef.packageName = options.packageName;
|
|
53
|
+
}
|
|
54
|
+
if (options?.packageVersion) {
|
|
55
|
+
objectDef.packageVersion = options.packageVersion;
|
|
56
|
+
}
|
|
57
|
+
if (objectDef.packageName) {
|
|
58
|
+
objectDef.qualifiedName = createQualifiedName(
|
|
59
|
+
objectDef.packageName,
|
|
60
|
+
objectDef.className
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
objectDef.visibility = inferVisibility(
|
|
64
|
+
objectDef.filePath,
|
|
65
|
+
objectDef.decoratorConfig?.visibility
|
|
66
|
+
);
|
|
67
|
+
if (options?.includeVisibility && options.includeVisibility.length > 0 && !options.includeVisibility.includes(objectDef.visibility)) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (options?.packageName && options?.packageJson) {
|
|
71
|
+
objectDef.importPath = this.determineImportPath(
|
|
72
|
+
options.packageJson,
|
|
73
|
+
objectDef.filePath
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
objectDef.exportName = objectDef.exportName || objectDef.className;
|
|
77
|
+
objectDef.collectionExportName = objectDef.collectionExportName || `${objectDef.className}Collection`;
|
|
78
|
+
if (objectDef.decoratorConfig.ai) {
|
|
79
|
+
const methods = Object.values(objectDef.methods);
|
|
80
|
+
const tools = generateToolManifest(
|
|
81
|
+
methods,
|
|
82
|
+
objectDef.decoratorConfig.ai
|
|
83
|
+
);
|
|
84
|
+
if (tools.length > 0) {
|
|
85
|
+
objectDef.tools = tools;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const manifestKey = objectDef.qualifiedName || objectDef.name;
|
|
89
|
+
if (manifest.objects[manifestKey]) {
|
|
90
|
+
const existing = manifest.objects[manifestKey];
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Class name collision detected: '${objectDef.className}' (${manifestKey}) is defined in multiple files:
|
|
93
|
+
1. ${existing.filePath}
|
|
94
|
+
2. ${objectDef.filePath}
|
|
95
|
+
|
|
96
|
+
Class names must be unique within a package. Use different class names or separate packages.`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
manifest.objects[manifestKey] = objectDef;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
this.injectTenantScopedFields(manifest);
|
|
103
|
+
this.mergeInheritedFields(manifest);
|
|
104
|
+
this.generateValidationRules(manifest);
|
|
105
|
+
this.generateSchemas(manifest);
|
|
106
|
+
this.assertTenantScopedSchemaContract(manifest);
|
|
107
|
+
this.generateAgentManifests(
|
|
108
|
+
manifest,
|
|
109
|
+
options?.packageName,
|
|
110
|
+
options?.packageJson
|
|
111
|
+
);
|
|
112
|
+
return manifest;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Materialize tenantScoped schema fields.
|
|
116
|
+
*
|
|
117
|
+
* Runtime registration already injects tenant fields for
|
|
118
|
+
* `@smrt({ tenantScoped: true })`, but published manifests must contain the
|
|
119
|
+
* same field before schema generation so migrations create `tenant_id`.
|
|
120
|
+
*/
|
|
121
|
+
injectTenantScopedFields(manifest) {
|
|
122
|
+
for (const objectDef of Object.values(manifest.objects)) {
|
|
123
|
+
this.injectTenantScopedField(objectDef);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
injectTenantScopedField(objectDef) {
|
|
127
|
+
const tenantScoped = objectDef.decoratorConfig?.tenantScoped;
|
|
128
|
+
if (!tenantScoped) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const { tenantConfig, tenantOptions } = this.normalizeTenantScopedConfig(tenantScoped);
|
|
132
|
+
const fieldName = tenantConfig.field;
|
|
133
|
+
const existingField = objectDef.fields[fieldName];
|
|
134
|
+
const tenancyMeta = {
|
|
135
|
+
isTenantIdField: true,
|
|
136
|
+
...tenantConfig
|
|
137
|
+
};
|
|
138
|
+
if (existingField) {
|
|
139
|
+
const fieldTypeFailure = this.getTenantScopedFieldTypeFailure(
|
|
140
|
+
objectDef,
|
|
141
|
+
fieldName
|
|
142
|
+
);
|
|
143
|
+
if (fieldTypeFailure) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`Tenant-scoped field configuration invalid: ${fieldTypeFailure}`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
existingField._meta = {
|
|
149
|
+
...existingField._meta,
|
|
150
|
+
sqlType: "UUID",
|
|
151
|
+
__tenancy: {
|
|
152
|
+
...existingField._meta?.__tenancy,
|
|
153
|
+
...tenancyMeta
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
objectDef.fields[fieldName] = {
|
|
159
|
+
type: "text",
|
|
160
|
+
// Preserve legacy migration behavior: boolean `tenantScoped: true`
|
|
161
|
+
// enables required-mode runtime scoping, but does not add a NOT NULL
|
|
162
|
+
// column to existing tables unless mode is explicitly set.
|
|
163
|
+
required: tenantOptions.mode === "required",
|
|
164
|
+
_meta: {
|
|
165
|
+
generated: true,
|
|
166
|
+
source: "tenantScoped_decorator",
|
|
167
|
+
sqlType: "UUID",
|
|
168
|
+
__tenancy: tenancyMeta
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
console.log(
|
|
172
|
+
`[manifest-generator] Injected ${fieldName} field for ${objectDef.className} (tenantScoped: ${JSON.stringify(tenantConfig)})`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
assertTenantScopedSchemaContract(manifest) {
|
|
176
|
+
const failures = [];
|
|
177
|
+
for (const objectDef of Object.values(manifest.objects)) {
|
|
178
|
+
const tenantScoped = objectDef.decoratorConfig?.tenantScoped;
|
|
179
|
+
if (!tenantScoped) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const { tenantConfig } = this.normalizeTenantScopedConfig(tenantScoped);
|
|
183
|
+
const fieldName = tenantConfig.field;
|
|
184
|
+
const columnName = toSnakeCase(fieldName);
|
|
185
|
+
if (!objectDef.fields[fieldName]) {
|
|
186
|
+
failures.push(
|
|
187
|
+
`${objectDef.className}: missing tenant-scoped field "${fieldName}"`
|
|
188
|
+
);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const fieldTypeFailure = this.getTenantScopedFieldTypeFailure(
|
|
192
|
+
objectDef,
|
|
193
|
+
fieldName
|
|
194
|
+
);
|
|
195
|
+
if (fieldTypeFailure) {
|
|
196
|
+
failures.push(fieldTypeFailure);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
const schemaOwner = this.getTenantScopedSchemaOwner(objectDef, manifest);
|
|
200
|
+
const schemaOwnerContext = schemaOwner === objectDef ? "" : ` on STI base "${schemaOwner.className}"`;
|
|
201
|
+
if (!schemaOwner.schema?.columns) {
|
|
202
|
+
failures.push(
|
|
203
|
+
`${objectDef.className}: schema has not been generated for tenant-scoped column "${columnName}"${schemaOwnerContext}`
|
|
204
|
+
);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (!schemaOwner.schema.columns[columnName]) {
|
|
208
|
+
failures.push(
|
|
209
|
+
`${objectDef.className}: schema is missing tenant-scoped column "${columnName}"${schemaOwnerContext}`
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (failures.length > 0) {
|
|
214
|
+
throw new Error(
|
|
215
|
+
`Tenant-scoped schema contract failed:
|
|
216
|
+
${failures.map((failure) => ` - ${failure}`).join("\n")}`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
getTenantScopedSchemaOwner(objectDef, manifest) {
|
|
221
|
+
if (!this.isSTIChildClass(objectDef, manifest)) {
|
|
222
|
+
return objectDef;
|
|
223
|
+
}
|
|
224
|
+
const stiBase = this.findSTIBaseInfo(objectDef, manifest);
|
|
225
|
+
if (!stiBase) {
|
|
226
|
+
return objectDef;
|
|
227
|
+
}
|
|
228
|
+
const localBase = Object.values(manifest.objects).find(
|
|
229
|
+
(candidate) => candidate.className === stiBase.className
|
|
230
|
+
);
|
|
231
|
+
return localBase ?? objectDef;
|
|
232
|
+
}
|
|
233
|
+
getTenantScopedFieldTypeFailure(objectDef, fieldName) {
|
|
234
|
+
const field = objectDef.fields[fieldName];
|
|
235
|
+
if (!field) {
|
|
236
|
+
return void 0;
|
|
237
|
+
}
|
|
238
|
+
if (field.type !== "text" && field.type !== "foreignKey") {
|
|
239
|
+
return `${objectDef.className}: tenant-scoped field "${fieldName}" must use type "text" or "foreignKey"; received "${field.type}"`;
|
|
240
|
+
}
|
|
241
|
+
const sqlType = field._meta?.sqlType;
|
|
242
|
+
if (sqlType && !["TEXT", "UUID"].includes(String(sqlType).toUpperCase())) {
|
|
243
|
+
return `${objectDef.className}: tenant-scoped field "${fieldName}" must use SQL type "UUID" or legacy "TEXT"; received "${sqlType}"`;
|
|
244
|
+
}
|
|
245
|
+
return void 0;
|
|
246
|
+
}
|
|
247
|
+
normalizeTenantScopedConfig(tenantScoped) {
|
|
248
|
+
const tenantOptions = typeof tenantScoped === "boolean" ? {} : tenantScoped;
|
|
249
|
+
return {
|
|
250
|
+
tenantOptions,
|
|
251
|
+
tenantConfig: {
|
|
252
|
+
mode: tenantOptions.mode ?? "required",
|
|
253
|
+
field: tenantOptions.field ?? "tenantId",
|
|
254
|
+
autoFilter: tenantOptions.autoFilter ?? true,
|
|
255
|
+
autoPopulate: tenantOptions.autoPopulate ?? true,
|
|
256
|
+
allowSuperAdminBypass: tenantOptions.allowSuperAdminBypass ?? false
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Generate pre-computed validation rules for all objects in the manifest.
|
|
262
|
+
*
|
|
263
|
+
* This extracts validation constraints (required, min, max, minLength, maxLength, pattern)
|
|
264
|
+
* from field definitions and stores them as serializable rules in the manifest.
|
|
265
|
+
*
|
|
266
|
+
* At runtime, these rules can be evaluated without creating validator closures,
|
|
267
|
+
* significantly reducing CLI startup time for projects with many SMRT objects.
|
|
268
|
+
*
|
|
269
|
+
* @param manifest - The manifest to process in-place
|
|
270
|
+
*/
|
|
271
|
+
generateValidationRules(manifest) {
|
|
272
|
+
for (const [name, obj] of Object.entries(manifest.objects)) {
|
|
273
|
+
const rules = [];
|
|
274
|
+
for (const [fieldName, field] of Object.entries(obj.fields)) {
|
|
275
|
+
if (field.transient || field._meta?.transient) {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
const options = field._meta || {};
|
|
279
|
+
if (options.required || field.required) {
|
|
280
|
+
rules.push({
|
|
281
|
+
field: fieldName,
|
|
282
|
+
rule: "required",
|
|
283
|
+
fieldType: field.type
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
if (field.type === "integer" || field.type === "decimal") {
|
|
287
|
+
if (options.min !== void 0 || field.min !== void 0) {
|
|
288
|
+
rules.push({
|
|
289
|
+
field: fieldName,
|
|
290
|
+
rule: "min",
|
|
291
|
+
value: options.min ?? field.min,
|
|
292
|
+
fieldType: field.type
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
if (options.max !== void 0 || field.max !== void 0) {
|
|
296
|
+
rules.push({
|
|
297
|
+
field: fieldName,
|
|
298
|
+
rule: "max",
|
|
299
|
+
value: options.max ?? field.max,
|
|
300
|
+
fieldType: field.type
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (field.type === "text") {
|
|
305
|
+
if (options.minLength !== void 0 || field.minLength !== void 0) {
|
|
306
|
+
rules.push({
|
|
307
|
+
field: fieldName,
|
|
308
|
+
rule: "minLength",
|
|
309
|
+
value: options.minLength ?? field.minLength,
|
|
310
|
+
fieldType: field.type
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
if (options.maxLength !== void 0 || field.maxLength !== void 0) {
|
|
314
|
+
rules.push({
|
|
315
|
+
field: fieldName,
|
|
316
|
+
rule: "maxLength",
|
|
317
|
+
value: options.maxLength ?? field.maxLength,
|
|
318
|
+
fieldType: field.type
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
const pattern = options.pattern;
|
|
322
|
+
if (pattern) {
|
|
323
|
+
rules.push({
|
|
324
|
+
field: fieldName,
|
|
325
|
+
rule: "pattern",
|
|
326
|
+
value: typeof pattern === "string" ? pattern : pattern.source,
|
|
327
|
+
fieldType: field.type
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (rules.length > 0) {
|
|
333
|
+
obj.validationRules = rules;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Generate pre-computed schemas for all objects in the manifest.
|
|
339
|
+
*
|
|
340
|
+
* This enables external package consumers to use pre-generated schemas
|
|
341
|
+
* without calling generateSchema() at runtime, eliminating latency.
|
|
342
|
+
*
|
|
343
|
+
* IMPORTANT: For STI classes, we aggregate ALL descendants from both local
|
|
344
|
+
* and external packages to ensure complete schemas are generated.
|
|
345
|
+
*
|
|
346
|
+
* This method is public to allow external callers (like OXC scanner) to use it.
|
|
347
|
+
*
|
|
348
|
+
* @param manifest - The manifest to process in-place
|
|
349
|
+
*/
|
|
350
|
+
generateSchemas(manifest) {
|
|
351
|
+
const generator = new SchemaGenerator();
|
|
352
|
+
const aggregatedManifest = this.createAggregatedManifest(manifest);
|
|
353
|
+
const processedSTIBases = /* @__PURE__ */ new Set();
|
|
354
|
+
const localObjects = new Set(
|
|
355
|
+
Object.values(manifest.objects).map((o) => o.className)
|
|
356
|
+
);
|
|
357
|
+
for (const [name, obj] of Object.entries(manifest.objects)) {
|
|
358
|
+
const tableName = obj.decoratorConfig?.tableName || this.classNameToTableName(obj.className);
|
|
359
|
+
if (obj.decoratorConfig?.tableStrategy === "sti") {
|
|
360
|
+
if (processedSTIBases.has(name)) continue;
|
|
361
|
+
processedSTIBases.add(name);
|
|
362
|
+
console.log(
|
|
363
|
+
`[manifest-generator] Generating STI schema for ${name} (table: ${tableName})`
|
|
364
|
+
);
|
|
365
|
+
obj.schema = generator.generateSTISchemaFromManifest(
|
|
366
|
+
name,
|
|
367
|
+
tableName,
|
|
368
|
+
obj.fields,
|
|
369
|
+
aggregatedManifest,
|
|
370
|
+
obj.decoratorConfig
|
|
371
|
+
);
|
|
372
|
+
this.applySqlTypeOverrides(obj);
|
|
373
|
+
} else if (this.isSTIChildClass(obj, manifest)) {
|
|
374
|
+
const stiBase = this.findSTIBaseInfo(obj, manifest);
|
|
375
|
+
const baseIsLocal = stiBase && localObjects.has(stiBase.className);
|
|
376
|
+
if (baseIsLocal) {
|
|
377
|
+
console.log(
|
|
378
|
+
`[manifest-generator] Skipping schema for STI child ${name} (base ${stiBase?.className} is local)`
|
|
379
|
+
);
|
|
380
|
+
} else {
|
|
381
|
+
const baseTableName = stiBase?.tableName || tableName;
|
|
382
|
+
console.log(
|
|
383
|
+
`[manifest-generator] Generating STI schema for ${name} (external base: ${stiBase?.className}, table: ${baseTableName})`
|
|
384
|
+
);
|
|
385
|
+
obj.schema = generator.generateSTISchemaFromManifest(
|
|
386
|
+
stiBase?.className || name,
|
|
387
|
+
baseTableName,
|
|
388
|
+
obj.fields,
|
|
389
|
+
aggregatedManifest,
|
|
390
|
+
obj.decoratorConfig
|
|
391
|
+
);
|
|
392
|
+
this.applySqlTypeOverrides(obj);
|
|
393
|
+
}
|
|
394
|
+
} else {
|
|
395
|
+
console.log(
|
|
396
|
+
`[manifest-generator] Generating CTI schema for ${name} (table: ${tableName})`
|
|
397
|
+
);
|
|
398
|
+
obj.schema = generator.generateCTISchemaFromManifest(
|
|
399
|
+
name,
|
|
400
|
+
tableName,
|
|
401
|
+
obj.fields,
|
|
402
|
+
obj.decoratorConfig
|
|
403
|
+
);
|
|
404
|
+
this.applySqlTypeOverrides(obj);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
this.resolveSamePackageForeignKeyColumnTypes(manifest, generator);
|
|
408
|
+
}
|
|
409
|
+
resolveSamePackageForeignKeyColumnTypes(manifest, generator) {
|
|
410
|
+
const schemaByTable = /* @__PURE__ */ new Map();
|
|
411
|
+
const ownerBySchema = /* @__PURE__ */ new Map();
|
|
412
|
+
const changedSchemas = /* @__PURE__ */ new Set();
|
|
413
|
+
for (const [name, obj] of Object.entries(manifest.objects)) {
|
|
414
|
+
if (obj.schema?.tableName) {
|
|
415
|
+
schemaByTable.set(obj.schema.tableName, obj.schema);
|
|
416
|
+
ownerBySchema.set(obj.schema, { name, obj });
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
for (const obj of Object.values(manifest.objects)) {
|
|
420
|
+
const sourceTable = this.getObjectTableName(obj);
|
|
421
|
+
if (!sourceTable) {
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
const sourceSchema = schemaByTable.get(sourceTable);
|
|
425
|
+
if (!sourceSchema) {
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
for (const [fieldName, field] of Object.entries(obj.fields || {})) {
|
|
429
|
+
if (field.type !== "foreignKey" || !field.related || field._meta?.sqlType) {
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
const columnName = toSnakeCase(fieldName);
|
|
433
|
+
const sourceColumn = sourceSchema.columns[columnName];
|
|
434
|
+
if (!sourceColumn) {
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
const targetSchema = this.findForeignKeyTargetSchema(
|
|
438
|
+
field.related,
|
|
439
|
+
manifest,
|
|
440
|
+
schemaByTable
|
|
441
|
+
);
|
|
442
|
+
const targetIdType = targetSchema?.columns.id?.type;
|
|
443
|
+
if (!targetIdType || sourceColumn.type === targetIdType) {
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
sourceSchema.columns[columnName] = {
|
|
447
|
+
...sourceColumn,
|
|
448
|
+
type: targetIdType
|
|
449
|
+
};
|
|
450
|
+
changedSchemas.add(sourceSchema);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
for (const schema of changedSchemas) {
|
|
454
|
+
this.refreshManifestSchemaDDL(
|
|
455
|
+
schema,
|
|
456
|
+
generator,
|
|
457
|
+
ownerBySchema.get(schema),
|
|
458
|
+
manifest
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
findForeignKeyTargetSchema(related, manifest, schemaByTable) {
|
|
463
|
+
const relatedTarget = related.split(".")[0];
|
|
464
|
+
if (schemaByTable.has(relatedTarget)) {
|
|
465
|
+
return schemaByTable.get(relatedTarget);
|
|
466
|
+
}
|
|
467
|
+
const targetObj = Object.values(manifest.objects).find(
|
|
468
|
+
(candidate) => candidate.className === relatedTarget || candidate.qualifiedName === relatedTarget || candidate.name === relatedTarget || candidate.decoratorConfig?.tableName === relatedTarget || candidate.schema?.tableName === relatedTarget
|
|
469
|
+
);
|
|
470
|
+
if (!targetObj && relatedTarget.includes(":")) {
|
|
471
|
+
return void 0;
|
|
472
|
+
}
|
|
473
|
+
const targetTable = targetObj ? this.getObjectTableName(targetObj) : this.classNameToTableName(relatedTarget);
|
|
474
|
+
return targetTable ? schemaByTable.get(targetTable) : void 0;
|
|
475
|
+
}
|
|
476
|
+
getObjectTableName(obj) {
|
|
477
|
+
return obj.schema?.tableName || obj.decoratorConfig?.tableName || this.classNameToTableName(obj.className);
|
|
478
|
+
}
|
|
479
|
+
refreshManifestSchemaDDL(schema, generator, owner, manifest) {
|
|
480
|
+
const schemaDefinition = {
|
|
481
|
+
tableName: schema.tableName,
|
|
482
|
+
columns: Object.fromEntries(
|
|
483
|
+
Object.entries(schema.columns).map(([name, column]) => [
|
|
484
|
+
name,
|
|
485
|
+
{
|
|
486
|
+
type: column.type,
|
|
487
|
+
primaryKey: column.primaryKey,
|
|
488
|
+
notNull: column.notNull,
|
|
489
|
+
unique: column.unique,
|
|
490
|
+
defaultValue: column.default
|
|
491
|
+
}
|
|
492
|
+
])
|
|
493
|
+
),
|
|
494
|
+
indexes: (schema.indexes || []).map((index) => ({
|
|
495
|
+
name: index.name,
|
|
496
|
+
columns: index.columns,
|
|
497
|
+
unique: index.unique,
|
|
498
|
+
where: index.where,
|
|
499
|
+
jsonPath: index.jsonPath
|
|
500
|
+
})),
|
|
501
|
+
triggers: [],
|
|
502
|
+
foreignKeys: [],
|
|
503
|
+
dependencies: [],
|
|
504
|
+
version: schema.version,
|
|
505
|
+
packageName: ""
|
|
506
|
+
};
|
|
507
|
+
schema.ddl = generator.generateSQL(schemaDefinition);
|
|
508
|
+
schema.version = this.computeManifestSchemaVersion(schema, owner, manifest);
|
|
509
|
+
}
|
|
510
|
+
computeManifestSchemaVersion(schema, owner, manifest) {
|
|
511
|
+
if (schema.columns._meta_type && owner) {
|
|
512
|
+
const baseClassName = owner.obj.decoratorConfig?.tableStrategy === "sti" ? owner.name : this.findSTIBaseInfo(owner.obj, manifest)?.className || owner.name;
|
|
513
|
+
return createHash("sha256").update(
|
|
514
|
+
JSON.stringify({
|
|
515
|
+
columns: schema.columns,
|
|
516
|
+
baseClassName,
|
|
517
|
+
descendants: this.findDescendantsInManifest(
|
|
518
|
+
baseClassName,
|
|
519
|
+
manifest
|
|
520
|
+
)
|
|
521
|
+
})
|
|
522
|
+
).digest("hex").substring(0, 8);
|
|
523
|
+
}
|
|
524
|
+
return createHash("sha256").update(
|
|
525
|
+
JSON.stringify({
|
|
526
|
+
columns: schema.columns,
|
|
527
|
+
className: owner?.name || schema.tableName
|
|
528
|
+
})
|
|
529
|
+
).digest("hex").substring(0, 8);
|
|
530
|
+
}
|
|
531
|
+
findDescendantsInManifest(baseClassName, manifest, visited = /* @__PURE__ */ new Set()) {
|
|
532
|
+
const descendants = [];
|
|
533
|
+
if (visited.has(baseClassName)) {
|
|
534
|
+
return descendants;
|
|
535
|
+
}
|
|
536
|
+
visited.add(baseClassName);
|
|
537
|
+
const baseClassLower = this.simpleClassName(baseClassName).toLowerCase();
|
|
538
|
+
for (const [name, obj] of Object.entries(manifest.objects)) {
|
|
539
|
+
const classNameLower = this.simpleClassName(obj.className).toLowerCase();
|
|
540
|
+
const extendsLower = obj.extends ? this.simpleClassName(obj.extends).toLowerCase() : void 0;
|
|
541
|
+
if (classNameLower === baseClassLower && extendsLower === baseClassLower) {
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
if (extendsLower === baseClassLower) {
|
|
545
|
+
descendants.push(name);
|
|
546
|
+
descendants.push(
|
|
547
|
+
...this.findDescendantsInManifest(name, manifest, visited)
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return descendants;
|
|
552
|
+
}
|
|
553
|
+
applySqlTypeOverrides(obj) {
|
|
554
|
+
if (!obj.schema?.columns) {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
for (const [fieldName, field] of Object.entries(obj.fields || {})) {
|
|
558
|
+
const sqlType = field?._meta?.sqlType;
|
|
559
|
+
const referenceKind = this.getReferenceKind(field);
|
|
560
|
+
if (!sqlType && !referenceKind) {
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
const columnName = toSnakeCase(fieldName);
|
|
564
|
+
if (!obj.schema.columns[columnName]) {
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
obj.schema.columns[columnName] = {
|
|
568
|
+
...obj.schema.columns[columnName],
|
|
569
|
+
...sqlType ? { type: String(sqlType).toUpperCase() } : {},
|
|
570
|
+
...referenceKind ? { referenceKind } : {}
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
getReferenceKind(field) {
|
|
575
|
+
if (field?._meta?.__tenancy?.isTenantIdField) {
|
|
576
|
+
return "tenantId";
|
|
577
|
+
}
|
|
578
|
+
if (field?.type === "foreignKey") {
|
|
579
|
+
return "foreignKey";
|
|
580
|
+
}
|
|
581
|
+
if (field?.type === "crossPackageRef") {
|
|
582
|
+
return "crossPackageRef";
|
|
583
|
+
}
|
|
584
|
+
return void 0;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Create an aggregated manifest that includes objects from all external packages
|
|
588
|
+
*
|
|
589
|
+
* This is used for STI schema generation to ensure ALL descendants are found,
|
|
590
|
+
* regardless of which package they're defined in.
|
|
591
|
+
*
|
|
592
|
+
* @param manifest - The local manifest
|
|
593
|
+
* @returns Aggregated manifest with local + external objects
|
|
594
|
+
*/
|
|
595
|
+
createAggregatedManifest(manifest) {
|
|
596
|
+
const aggregatedObjects = {
|
|
597
|
+
...manifest.objects
|
|
598
|
+
};
|
|
599
|
+
if (manifest.smrtDependencies && manifest.smrtDependencies.length > 0) {
|
|
600
|
+
for (const packageName of manifest.smrtDependencies) {
|
|
601
|
+
const externalManifest = loadExternalManifestSync(packageName);
|
|
602
|
+
if (externalManifest) {
|
|
603
|
+
for (const [name, obj] of Object.entries(externalManifest.objects)) {
|
|
604
|
+
if (!aggregatedObjects[name]) {
|
|
605
|
+
aggregatedObjects[name] = obj;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
console.log(
|
|
609
|
+
`[manifest-generator] Aggregated ${Object.keys(externalManifest.objects).length} objects from ${packageName}`
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return {
|
|
615
|
+
...manifest,
|
|
616
|
+
objects: aggregatedObjects
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Check if an object is an STI child class (inherits from STI base)
|
|
621
|
+
*
|
|
622
|
+
* Walks up the inheritance chain to find if any ancestor has tableStrategy: 'sti'.
|
|
623
|
+
* Also checks external SMRT packages for parent class definitions.
|
|
624
|
+
*/
|
|
625
|
+
isSTIChildClass(obj, manifest) {
|
|
626
|
+
if (!obj.extends) return false;
|
|
627
|
+
const objectsByName = /* @__PURE__ */ new Map();
|
|
628
|
+
for (const [_name, objDef] of Object.entries(manifest.objects)) {
|
|
629
|
+
objectsByName.set(objDef.className, objDef);
|
|
630
|
+
objectsByName.set(objDef.className.toLowerCase(), objDef);
|
|
631
|
+
}
|
|
632
|
+
let currentClass = obj.extends;
|
|
633
|
+
const visited = /* @__PURE__ */ new Set();
|
|
634
|
+
while (currentClass) {
|
|
635
|
+
if (visited.has(currentClass)) break;
|
|
636
|
+
visited.add(currentClass);
|
|
637
|
+
let parentObj = objectsByName.get(currentClass);
|
|
638
|
+
if (!parentObj && manifest.smrtDependencies && manifest.smrtDependencies.length > 0) {
|
|
639
|
+
parentObj = this.loadParentFromExternalPackage(
|
|
640
|
+
currentClass,
|
|
641
|
+
manifest.smrtDependencies,
|
|
642
|
+
objectsByName
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
if (!parentObj) break;
|
|
646
|
+
if (parentObj.decoratorConfig?.tableStrategy === "sti") {
|
|
647
|
+
return true;
|
|
648
|
+
}
|
|
649
|
+
currentClass = parentObj.extends;
|
|
650
|
+
}
|
|
651
|
+
return false;
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Check whether `obj` extends a framework abstract base class anywhere
|
|
655
|
+
* in its chain.
|
|
656
|
+
*
|
|
657
|
+
* Framework abstract bases (`SmrtHierarchical`, `SmrtJunction`, …) have
|
|
658
|
+
* no table of their own — fields they declare must be merged into every
|
|
659
|
+
* subclass's manifest, even when the subclass uses CTI. Without this,
|
|
660
|
+
* a class like `Account extends SmrtHierarchical` would silently lose
|
|
661
|
+
* `parentId` from its `fields` map and downstream WHERE-clause
|
|
662
|
+
* validation would reject queries on the inherited column.
|
|
663
|
+
*
|
|
664
|
+
* Identified by name against the same hardcoded set the scanner's
|
|
665
|
+
* `FRAMEWORK_BASE_CLASSES` recognizes (`packages/scanner/src/
|
|
666
|
+
* inheritance-resolver.ts`). Keep the two lists in sync.
|
|
667
|
+
*/
|
|
668
|
+
extendsFrameworkAbstractBase(obj, objectsByName, manifest) {
|
|
669
|
+
if (!obj.extends) return false;
|
|
670
|
+
let currentClass = obj.extends;
|
|
671
|
+
const visited = /* @__PURE__ */ new Set();
|
|
672
|
+
while (currentClass) {
|
|
673
|
+
if (visited.has(currentClass)) break;
|
|
674
|
+
visited.add(currentClass);
|
|
675
|
+
if (FRAMEWORK_ABSTRACT_BASE_NAMES.has(currentClass)) {
|
|
676
|
+
return true;
|
|
677
|
+
}
|
|
678
|
+
let parentObj = objectsByName.get(currentClass);
|
|
679
|
+
if (!parentObj && manifest.smrtDependencies && manifest.smrtDependencies.length > 0) {
|
|
680
|
+
parentObj = this.loadParentFromExternalPackage(
|
|
681
|
+
currentClass,
|
|
682
|
+
manifest.smrtDependencies,
|
|
683
|
+
objectsByName
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
if (!parentObj) break;
|
|
687
|
+
currentClass = parentObj.extends;
|
|
688
|
+
}
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Find full STI base class info (className + tableName)
|
|
693
|
+
*
|
|
694
|
+
* Walks up the inheritance chain to find the STI base class and returns
|
|
695
|
+
* both its className and tableName. This is critical for external STI bases
|
|
696
|
+
* where the child needs to use the base's table name for schema generation.
|
|
697
|
+
*/
|
|
698
|
+
findSTIBaseInfo(obj, manifest) {
|
|
699
|
+
if (!obj.extends) return void 0;
|
|
700
|
+
const objectsByName = /* @__PURE__ */ new Map();
|
|
701
|
+
for (const [_name, objDef] of Object.entries(manifest.objects)) {
|
|
702
|
+
objectsByName.set(objDef.className, objDef);
|
|
703
|
+
objectsByName.set(objDef.className.toLowerCase(), objDef);
|
|
704
|
+
}
|
|
705
|
+
let stiBaseInfo;
|
|
706
|
+
let currentClass = obj.extends;
|
|
707
|
+
const visited = /* @__PURE__ */ new Set();
|
|
708
|
+
while (currentClass) {
|
|
709
|
+
if (visited.has(currentClass)) break;
|
|
710
|
+
visited.add(currentClass);
|
|
711
|
+
let parentObj = objectsByName.get(currentClass);
|
|
712
|
+
if (!parentObj && manifest.smrtDependencies && manifest.smrtDependencies.length > 0) {
|
|
713
|
+
parentObj = this.loadParentFromExternalPackage(
|
|
714
|
+
currentClass,
|
|
715
|
+
manifest.smrtDependencies,
|
|
716
|
+
objectsByName
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
if (!parentObj) break;
|
|
720
|
+
if (parentObj.decoratorConfig?.tableStrategy === "sti") {
|
|
721
|
+
const tableName = parentObj.decoratorConfig?.tableName || parentObj.schema?.tableName || this.classNameToTableName(parentObj.className);
|
|
722
|
+
stiBaseInfo = {
|
|
723
|
+
className: parentObj.className,
|
|
724
|
+
tableName
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
currentClass = parentObj.extends;
|
|
728
|
+
}
|
|
729
|
+
return stiBaseInfo;
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Convert class name to table name (snake_case pluralized)
|
|
733
|
+
*
|
|
734
|
+
* IMPORTANT: Must use the same algorithm as runtime's tableNameFromClass()
|
|
735
|
+
* to ensure manifest-generated table names match runtime-derived names.
|
|
736
|
+
*/
|
|
737
|
+
classNameToTableName(className) {
|
|
738
|
+
return classnameToTablename(className);
|
|
739
|
+
}
|
|
740
|
+
normalizeFrameworkInheritedField(ancestorName, fieldName, fieldDef, childClassName) {
|
|
741
|
+
if (this.simpleClassName(ancestorName) === "SmrtHierarchical" && fieldName === "parentId") {
|
|
742
|
+
return {
|
|
743
|
+
...fieldDef,
|
|
744
|
+
type: "foreignKey",
|
|
745
|
+
related: childClassName,
|
|
746
|
+
required: false,
|
|
747
|
+
_meta: {
|
|
748
|
+
...fieldDef._meta || {},
|
|
749
|
+
nullable: true
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
return fieldDef;
|
|
754
|
+
}
|
|
755
|
+
simpleClassName(className) {
|
|
756
|
+
return className.includes(":") ? className.split(":").pop() || className : className;
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Merge inherited fields into child classes (build-time inheritance resolution)
|
|
760
|
+
*
|
|
761
|
+
* For STI hierarchies, child classes don't define their own fields in source code
|
|
762
|
+
* (they inherit from parent). This method merges parent fields into child manifests
|
|
763
|
+
* so that runtime code doesn't need to do field resolution.
|
|
764
|
+
*
|
|
765
|
+
* Also handles collection classes (SmrtCollection<T>) that should inherit their
|
|
766
|
+
* item class's tableName and collection when the item uses STI.
|
|
767
|
+
*
|
|
768
|
+
* Handles multi-level inheritance (grandparents, great-grandparents, etc.)
|
|
769
|
+
* Automatically loads parent class definitions from external SMRT packages when needed
|
|
770
|
+
*
|
|
771
|
+
* @param manifest - The manifest to process in-place
|
|
772
|
+
*/
|
|
773
|
+
mergeInheritedFields(manifest) {
|
|
774
|
+
const objectsByName = /* @__PURE__ */ new Map();
|
|
775
|
+
for (const [name, obj] of Object.entries(manifest.objects)) {
|
|
776
|
+
objectsByName.set(obj.className, obj);
|
|
777
|
+
objectsByName.set(obj.className.toLowerCase(), obj);
|
|
778
|
+
}
|
|
779
|
+
for (const obj of Object.values(manifest.objects)) {
|
|
780
|
+
if (!obj.extends) continue;
|
|
781
|
+
const usesSTI = this.isSTIClass(obj, objectsByName, manifest);
|
|
782
|
+
const extendsFrameworkBase = this.extendsFrameworkAbstractBase(
|
|
783
|
+
obj,
|
|
784
|
+
objectsByName,
|
|
785
|
+
manifest
|
|
786
|
+
);
|
|
787
|
+
if (!usesSTI && !extendsFrameworkBase) {
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
790
|
+
console.log(
|
|
791
|
+
`[manifest-generator] Merging inherited fields for ${obj.className} from ${obj.extends}`
|
|
792
|
+
);
|
|
793
|
+
const inheritanceChain = [];
|
|
794
|
+
let currentClass = obj.extends;
|
|
795
|
+
const visited = /* @__PURE__ */ new Set();
|
|
796
|
+
while (currentClass) {
|
|
797
|
+
if (visited.has(currentClass)) {
|
|
798
|
+
console.warn(
|
|
799
|
+
`[manifest-generator] Circular inheritance detected for ${obj.className}`
|
|
800
|
+
);
|
|
801
|
+
break;
|
|
802
|
+
}
|
|
803
|
+
visited.add(currentClass);
|
|
804
|
+
inheritanceChain.unshift(currentClass);
|
|
805
|
+
let parentObj = objectsByName.get(currentClass);
|
|
806
|
+
if (parentObj === obj) {
|
|
807
|
+
parentObj = void 0;
|
|
808
|
+
}
|
|
809
|
+
if (!parentObj && manifest.smrtDependencies && manifest.smrtDependencies.length > 0) {
|
|
810
|
+
parentObj = this.loadParentFromExternalPackage(
|
|
811
|
+
currentClass,
|
|
812
|
+
manifest.smrtDependencies,
|
|
813
|
+
objectsByName
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
if (!parentObj) break;
|
|
817
|
+
currentClass = parentObj.extends;
|
|
818
|
+
}
|
|
819
|
+
console.log(
|
|
820
|
+
`[manifest-generator] Inheritance chain for ${obj.className}: ${inheritanceChain.join(" -> ")}`
|
|
821
|
+
);
|
|
822
|
+
const mergedFields = {};
|
|
823
|
+
const mergedMethods = {};
|
|
824
|
+
for (const ancestorName of inheritanceChain) {
|
|
825
|
+
const ancestor = objectsByName.get(ancestorName);
|
|
826
|
+
if (!ancestor) continue;
|
|
827
|
+
const ancestorIsFrameworkBase = FRAMEWORK_ABSTRACT_BASE_NAMES.has(ancestorName);
|
|
828
|
+
if (!usesSTI && !ancestorIsFrameworkBase) {
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
for (const [fieldName, fieldDef] of Object.entries(ancestor.fields)) {
|
|
832
|
+
if (!mergedFields[fieldName]) {
|
|
833
|
+
mergedFields[fieldName] = this.normalizeFrameworkInheritedField(
|
|
834
|
+
ancestorName,
|
|
835
|
+
fieldName,
|
|
836
|
+
fieldDef,
|
|
837
|
+
obj.className
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
for (const [methodName, methodDef] of Object.entries(
|
|
842
|
+
ancestor.methods || {}
|
|
843
|
+
)) {
|
|
844
|
+
if (!mergedMethods[methodName]) {
|
|
845
|
+
mergedMethods[methodName] = methodDef;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
for (const [fieldName, fieldDef] of Object.entries(obj.fields)) {
|
|
850
|
+
mergedFields[fieldName] = fieldDef;
|
|
851
|
+
}
|
|
852
|
+
for (const [methodName, methodDef] of Object.entries(obj.methods || {})) {
|
|
853
|
+
mergedMethods[methodName] = methodDef;
|
|
854
|
+
}
|
|
855
|
+
obj.fields = mergedFields;
|
|
856
|
+
obj.methods = mergedMethods;
|
|
857
|
+
const stiBase = this.findSTIBase(obj, objectsByName, manifest);
|
|
858
|
+
if (stiBase && stiBase !== obj) {
|
|
859
|
+
const baseTableName = stiBase.decoratorConfig?.tableName || this.classNameToTableName(stiBase.className);
|
|
860
|
+
obj.decoratorConfig = obj.decoratorConfig || {};
|
|
861
|
+
obj.decoratorConfig.tableName = baseTableName;
|
|
862
|
+
console.log(
|
|
863
|
+
`[manifest-generator] ${obj.className} inherits tableName: '${baseTableName}' from ${stiBase.className}`
|
|
864
|
+
);
|
|
865
|
+
if (stiBase.decoratorConfig?.tableStrategy) {
|
|
866
|
+
obj.decoratorConfig.tableStrategy = stiBase.decoratorConfig.tableStrategy;
|
|
867
|
+
console.log(
|
|
868
|
+
`[manifest-generator] ${obj.className} inherits tableStrategy: '${stiBase.decoratorConfig.tableStrategy}' from ${stiBase.className}`
|
|
869
|
+
);
|
|
870
|
+
}
|
|
871
|
+
if (stiBase.collection !== obj.collection) {
|
|
872
|
+
console.log(
|
|
873
|
+
`[manifest-generator] ${obj.className} inherits collection: '${stiBase.collection}' from ${stiBase.className}`
|
|
874
|
+
);
|
|
875
|
+
obj.collection = stiBase.collection;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
console.log(
|
|
879
|
+
`[manifest-generator] ✅ ${obj.className} now has ${Object.keys(mergedFields).length} fields (including inherited)`
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
for (const obj of Object.values(manifest.objects)) {
|
|
883
|
+
const isCollection = obj.extends === "SmrtCollection" || this.extendsCollection(obj, objectsByName);
|
|
884
|
+
if (isCollection) {
|
|
885
|
+
const itemClass = this.findItemClass(obj, manifest, objectsByName);
|
|
886
|
+
if (itemClass) {
|
|
887
|
+
console.log(
|
|
888
|
+
`[manifest-generator] ${obj.className} is a collection class for ${itemClass.className}`
|
|
889
|
+
);
|
|
890
|
+
if (itemClass.decoratorConfig?.tableName) {
|
|
891
|
+
obj.decoratorConfig = obj.decoratorConfig || {};
|
|
892
|
+
obj.decoratorConfig.tableName = itemClass.decoratorConfig.tableName;
|
|
893
|
+
console.log(
|
|
894
|
+
`[manifest-generator] ${obj.className} inherits tableName: '${itemClass.decoratorConfig.tableName}' from item class ${itemClass.className}`
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
if (itemClass.collection !== obj.collection) {
|
|
898
|
+
console.log(
|
|
899
|
+
`[manifest-generator] ${obj.className} inherits collection: '${itemClass.collection}' from item class ${itemClass.className} (was '${obj.collection}')`
|
|
900
|
+
);
|
|
901
|
+
obj.collection = itemClass.collection;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* Check if a class extends SmrtCollection (directly or indirectly)
|
|
909
|
+
*
|
|
910
|
+
* @param obj - The object definition to check
|
|
911
|
+
* @param objectsByName - Map of className -> objectDef for lookups
|
|
912
|
+
* @returns true if this class extends SmrtCollection
|
|
913
|
+
*/
|
|
914
|
+
extendsCollection(obj, objectsByName) {
|
|
915
|
+
if (!obj.extends) return false;
|
|
916
|
+
let currentClass = obj.extends;
|
|
917
|
+
const visited = /* @__PURE__ */ new Set();
|
|
918
|
+
while (currentClass) {
|
|
919
|
+
if (visited.has(currentClass)) break;
|
|
920
|
+
visited.add(currentClass);
|
|
921
|
+
if (currentClass === "SmrtCollection") return true;
|
|
922
|
+
const parentObj = objectsByName.get(currentClass);
|
|
923
|
+
if (!parentObj) break;
|
|
924
|
+
currentClass = parentObj.extends;
|
|
925
|
+
}
|
|
926
|
+
return false;
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Find the item class for a collection class
|
|
930
|
+
*
|
|
931
|
+
* Lookup priority:
|
|
932
|
+
* 1. extendsTypeArg - Generic type argument from extends clause (e.g., "Meeting" from SmrtCollection<Meeting>)
|
|
933
|
+
* 2. _itemClass static property - May be captured by AST scanner
|
|
934
|
+
* 3. Name-based inference - Fallback (e.g., "Meetings" -> "Meeting")
|
|
935
|
+
*
|
|
936
|
+
* @param collectionObj - The collection class definition
|
|
937
|
+
* @param manifest - The manifest
|
|
938
|
+
* @param objectsByName - Map of className -> objectDef for lookups
|
|
939
|
+
* @returns Item class definition or undefined
|
|
940
|
+
*/
|
|
941
|
+
findItemClass(collectionObj, manifest, objectsByName) {
|
|
942
|
+
if (collectionObj.extendsTypeArg) {
|
|
943
|
+
const itemClassName = collectionObj.extendsTypeArg;
|
|
944
|
+
console.log(
|
|
945
|
+
`[manifest-generator] ${collectionObj.className} has extendsTypeArg: ${itemClassName}`
|
|
946
|
+
);
|
|
947
|
+
const itemClass = objectsByName.get(itemClassName);
|
|
948
|
+
if (itemClass) {
|
|
949
|
+
console.log(
|
|
950
|
+
`[manifest-generator] Found item class ${itemClassName} in local manifest`
|
|
951
|
+
);
|
|
952
|
+
return itemClass;
|
|
953
|
+
}
|
|
954
|
+
if (manifest.smrtDependencies && manifest.smrtDependencies.length > 0) {
|
|
955
|
+
const externalItemClass = this.loadParentFromExternalPackage(
|
|
956
|
+
itemClassName,
|
|
957
|
+
manifest.smrtDependencies,
|
|
958
|
+
objectsByName
|
|
959
|
+
);
|
|
960
|
+
if (externalItemClass) {
|
|
961
|
+
console.log(
|
|
962
|
+
`[manifest-generator] Found item class ${itemClassName} in external package`
|
|
963
|
+
);
|
|
964
|
+
return externalItemClass;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
const itemClassField = collectionObj.fields._itemClass;
|
|
969
|
+
if (itemClassField) {
|
|
970
|
+
const itemClassName = itemClassField.related || itemClassField.default;
|
|
971
|
+
if (itemClassName && typeof itemClassName === "string") {
|
|
972
|
+
const itemClass = objectsByName.get(itemClassName);
|
|
973
|
+
if (itemClass) {
|
|
974
|
+
return itemClass;
|
|
975
|
+
}
|
|
976
|
+
if (manifest.smrtDependencies && manifest.smrtDependencies.length > 0) {
|
|
977
|
+
return this.loadParentFromExternalPackage(
|
|
978
|
+
itemClassName,
|
|
979
|
+
manifest.smrtDependencies,
|
|
980
|
+
objectsByName
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
const collectionName = collectionObj.className;
|
|
986
|
+
const candidates = [];
|
|
987
|
+
let baseName = collectionName;
|
|
988
|
+
if (baseName.endsWith("Collection")) {
|
|
989
|
+
baseName = baseName.slice(0, -"Collection".length);
|
|
990
|
+
}
|
|
991
|
+
candidates.push(baseName);
|
|
992
|
+
if (baseName.endsWith("s") && baseName.length > 1) {
|
|
993
|
+
candidates.push(baseName.slice(0, -1));
|
|
994
|
+
}
|
|
995
|
+
if (baseName.endsWith("ies") && baseName.length > 3) {
|
|
996
|
+
candidates.push(`${baseName.slice(0, -3)}y`);
|
|
997
|
+
}
|
|
998
|
+
if (baseName.endsWith("es") && baseName.length > 2) {
|
|
999
|
+
candidates.push(baseName.slice(0, -2));
|
|
1000
|
+
}
|
|
1001
|
+
for (const candidate of candidates) {
|
|
1002
|
+
if (candidate === collectionObj.className) continue;
|
|
1003
|
+
const itemClass = objectsByName.get(candidate);
|
|
1004
|
+
if (itemClass) {
|
|
1005
|
+
return itemClass;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
if (manifest.smrtDependencies && manifest.smrtDependencies.length > 0) {
|
|
1009
|
+
for (const candidate of candidates) {
|
|
1010
|
+
if (candidate === collectionObj.className) continue;
|
|
1011
|
+
const itemClass = this.loadParentFromExternalPackage(
|
|
1012
|
+
candidate,
|
|
1013
|
+
manifest.smrtDependencies,
|
|
1014
|
+
objectsByName
|
|
1015
|
+
);
|
|
1016
|
+
if (itemClass) {
|
|
1017
|
+
return itemClass;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
return void 0;
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* Check if a class uses STI (either explicitly or inherited from an ancestor)
|
|
1025
|
+
*
|
|
1026
|
+
* Walks up the inheritance chain to find if any ancestor has tableStrategy: 'sti'.
|
|
1027
|
+
* If found, all descendants inherit STI and should have fields merged.
|
|
1028
|
+
* Also checks external SMRT packages for parent class definitions.
|
|
1029
|
+
*
|
|
1030
|
+
* @param obj - The object definition to check
|
|
1031
|
+
* @param objectsByName - Map of className -> objectDef for lookups
|
|
1032
|
+
* @param manifest - The manifest (for accessing smrtDependencies)
|
|
1033
|
+
* @returns true if this class uses STI (directly or inherited)
|
|
1034
|
+
*/
|
|
1035
|
+
isSTIClass(obj, objectsByName, manifest) {
|
|
1036
|
+
if (obj.decoratorConfig?.tableStrategy === "sti") {
|
|
1037
|
+
return true;
|
|
1038
|
+
}
|
|
1039
|
+
let currentClass = obj.extends;
|
|
1040
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1041
|
+
while (currentClass) {
|
|
1042
|
+
if (visited.has(currentClass)) {
|
|
1043
|
+
break;
|
|
1044
|
+
}
|
|
1045
|
+
visited.add(currentClass);
|
|
1046
|
+
let parentDef = objectsByName.get(currentClass);
|
|
1047
|
+
if (parentDef === obj) {
|
|
1048
|
+
parentDef = void 0;
|
|
1049
|
+
}
|
|
1050
|
+
if (!parentDef && manifest.smrtDependencies && manifest.smrtDependencies.length > 0) {
|
|
1051
|
+
parentDef = this.loadParentFromExternalPackage(
|
|
1052
|
+
currentClass,
|
|
1053
|
+
manifest.smrtDependencies,
|
|
1054
|
+
objectsByName
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1057
|
+
if (!parentDef) break;
|
|
1058
|
+
if (parentDef.decoratorConfig?.tableStrategy === "sti") {
|
|
1059
|
+
return true;
|
|
1060
|
+
}
|
|
1061
|
+
currentClass = parentDef.extends;
|
|
1062
|
+
}
|
|
1063
|
+
return false;
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Find the STI base class for a given object
|
|
1067
|
+
*
|
|
1068
|
+
* Walks up the inheritance chain to find the first ancestor that defines
|
|
1069
|
+
* tableStrategy: 'sti'. This is the class that owns the shared table.
|
|
1070
|
+
*
|
|
1071
|
+
* @param obj - The object definition to find the STI base for
|
|
1072
|
+
* @param objectsByName - Map of className -> objectDef for lookups
|
|
1073
|
+
* @param manifest - The manifest (for accessing smrtDependencies)
|
|
1074
|
+
* @returns The STI base class definition, or the object itself if it's the base
|
|
1075
|
+
*/
|
|
1076
|
+
findSTIBase(obj, objectsByName, manifest) {
|
|
1077
|
+
let stiBase;
|
|
1078
|
+
if (obj.decoratorConfig?.tableStrategy === "sti") {
|
|
1079
|
+
stiBase = obj;
|
|
1080
|
+
}
|
|
1081
|
+
let currentClass = obj.extends;
|
|
1082
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1083
|
+
while (currentClass) {
|
|
1084
|
+
if (visited.has(currentClass)) {
|
|
1085
|
+
break;
|
|
1086
|
+
}
|
|
1087
|
+
visited.add(currentClass);
|
|
1088
|
+
let parentDef = objectsByName.get(currentClass);
|
|
1089
|
+
if (parentDef === obj) {
|
|
1090
|
+
parentDef = void 0;
|
|
1091
|
+
}
|
|
1092
|
+
if (!parentDef && manifest.smrtDependencies && manifest.smrtDependencies.length > 0) {
|
|
1093
|
+
parentDef = this.loadParentFromExternalPackage(
|
|
1094
|
+
currentClass,
|
|
1095
|
+
manifest.smrtDependencies,
|
|
1096
|
+
objectsByName
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
if (!parentDef) break;
|
|
1100
|
+
if (parentDef.decoratorConfig?.tableStrategy === "sti") {
|
|
1101
|
+
stiBase = parentDef;
|
|
1102
|
+
}
|
|
1103
|
+
currentClass = parentDef.extends;
|
|
1104
|
+
}
|
|
1105
|
+
return stiBase;
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Load parent class definition from external SMRT packages
|
|
1109
|
+
*
|
|
1110
|
+
* When a child class extends a parent from an external package (e.g., praeco's Council extends
|
|
1111
|
+
* smrt-profiles' Organization), this method loads the parent's manifest and extracts the
|
|
1112
|
+
* parent's field definitions.
|
|
1113
|
+
*
|
|
1114
|
+
* @param parentClassName - Name of the parent class to find
|
|
1115
|
+
* @param smrtDependencies - List of external SMRT packages to search
|
|
1116
|
+
* @param objectsByName - Map to cache loaded external objects
|
|
1117
|
+
* @returns Parent object definition if found, undefined otherwise
|
|
1118
|
+
*/
|
|
1119
|
+
loadParentFromExternalPackage(parentClassName, smrtDependencies, objectsByName) {
|
|
1120
|
+
for (const packageName of smrtDependencies) {
|
|
1121
|
+
const externalManifest = loadExternalManifestSync(packageName);
|
|
1122
|
+
if (!externalManifest) {
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1125
|
+
const parentObj = lookupInManifest(externalManifest, parentClassName);
|
|
1126
|
+
if (parentObj) {
|
|
1127
|
+
objectsByName.set(parentObj.className, parentObj);
|
|
1128
|
+
objectsByName.set(parentObj.className.toLowerCase(), parentObj);
|
|
1129
|
+
return parentObj;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
return void 0;
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Determine import path from package.json exports
|
|
1136
|
+
*
|
|
1137
|
+
* Tries the following strategies in order:
|
|
1138
|
+
* 1. package.json exports["./objects"] - Specific objects export
|
|
1139
|
+
* 2. package.json exports["."] - Main export
|
|
1140
|
+
* 3. package.json main - Main field
|
|
1141
|
+
* 4. Fallback to package name
|
|
1142
|
+
*/
|
|
1143
|
+
determineImportPath(packageJson, _filePath) {
|
|
1144
|
+
const packageName = packageJson.name;
|
|
1145
|
+
if (packageJson.exports) {
|
|
1146
|
+
if (packageJson.exports["./objects"]) {
|
|
1147
|
+
return `${packageName}/objects`;
|
|
1148
|
+
}
|
|
1149
|
+
const mainExport = packageJson.exports["."];
|
|
1150
|
+
if (mainExport) {
|
|
1151
|
+
if (typeof mainExport === "object") {
|
|
1152
|
+
if (mainExport.import) {
|
|
1153
|
+
return packageName;
|
|
1154
|
+
}
|
|
1155
|
+
if (mainExport.default) {
|
|
1156
|
+
return packageName;
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
return packageName;
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
if (packageJson.main) {
|
|
1163
|
+
return packageName;
|
|
1164
|
+
}
|
|
1165
|
+
return packageName;
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Generate TypeScript interfaces from manifest
|
|
1169
|
+
*/
|
|
1170
|
+
generateTypeDefinitions(manifest) {
|
|
1171
|
+
const interfaces = [];
|
|
1172
|
+
for (const [_name, obj] of Object.entries(manifest.objects)) {
|
|
1173
|
+
interfaces.push(this.generateInterface(obj));
|
|
1174
|
+
}
|
|
1175
|
+
return interfaces.join("\n\n");
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Generate a single interface definition
|
|
1179
|
+
*/
|
|
1180
|
+
generateInterface(obj) {
|
|
1181
|
+
const fields = Object.entries(obj.fields).map(([name, field]) => {
|
|
1182
|
+
const optional = !field.required ? "?" : "";
|
|
1183
|
+
const type = this.mapFieldTypeToTS(field.type);
|
|
1184
|
+
return ` ${name}${optional}: ${type};`;
|
|
1185
|
+
}).join("\n");
|
|
1186
|
+
return `export interface ${obj.className}Data {
|
|
1187
|
+
${fields}
|
|
1188
|
+
}`;
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Map field types to TypeScript types
|
|
1192
|
+
*/
|
|
1193
|
+
mapFieldTypeToTS(fieldType) {
|
|
1194
|
+
switch (fieldType) {
|
|
1195
|
+
case "text":
|
|
1196
|
+
return "string";
|
|
1197
|
+
case "decimal":
|
|
1198
|
+
return "number";
|
|
1199
|
+
case "integer":
|
|
1200
|
+
return "number";
|
|
1201
|
+
case "boolean":
|
|
1202
|
+
return "boolean";
|
|
1203
|
+
case "datetime":
|
|
1204
|
+
return "Date | string";
|
|
1205
|
+
case "json":
|
|
1206
|
+
return "any";
|
|
1207
|
+
case "foreignKey":
|
|
1208
|
+
return "string";
|
|
1209
|
+
case "crossPackageRef":
|
|
1210
|
+
return "string";
|
|
1211
|
+
default:
|
|
1212
|
+
return "any";
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
/**
|
|
1216
|
+
* Generate simple endpoint list for testing/documentation
|
|
1217
|
+
*/
|
|
1218
|
+
generateRestEndpoints(manifest) {
|
|
1219
|
+
const endpoints = [];
|
|
1220
|
+
for (const [_name, obj] of Object.entries(manifest.objects)) {
|
|
1221
|
+
const apiConfig = obj.decoratorConfig.api;
|
|
1222
|
+
if (apiConfig !== false) {
|
|
1223
|
+
endpoints.push(...this.getSimpleEndpoints(obj));
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
return endpoints.join("\n");
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Generate REST endpoint code implementations
|
|
1230
|
+
*/
|
|
1231
|
+
generateRestEndpointCode(manifest) {
|
|
1232
|
+
const endpoints = [];
|
|
1233
|
+
for (const [_name, obj] of Object.entries(manifest.objects)) {
|
|
1234
|
+
const apiConfig = obj.decoratorConfig.api;
|
|
1235
|
+
if (apiConfig !== false) {
|
|
1236
|
+
endpoints.push(this.generateRestEndpoint(obj));
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
return endpoints.join("\n\n");
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Get simple endpoint strings for an object
|
|
1243
|
+
*/
|
|
1244
|
+
getApiRouteMetadata(obj, actionName, actionDef) {
|
|
1245
|
+
const config = obj.decoratorConfig.api && typeof obj.decoratorConfig.api === "object" ? obj.decoratorConfig.api : void 0;
|
|
1246
|
+
const routeConfig = config?.routes?.[actionName];
|
|
1247
|
+
const normalizedPath = (routeConfig?.path || actionName).split("/").map((segment) => segment.trim()).filter(Boolean).join("/");
|
|
1248
|
+
const isCollectionClass = obj.extends === "SmrtCollection" || !!obj.extendsTypeArg;
|
|
1249
|
+
return {
|
|
1250
|
+
scope: routeConfig?.scope || (isCollectionClass || actionDef.isStatic ? "collection" : "item"),
|
|
1251
|
+
method: routeConfig?.method?.toUpperCase() === "GET" || routeConfig?.method?.toUpperCase() === "POST" || routeConfig?.method?.toUpperCase() === "PUT" || routeConfig?.method?.toUpperCase() === "PATCH" || routeConfig?.method?.toUpperCase() === "DELETE" ? routeConfig.method.toUpperCase() : "POST",
|
|
1252
|
+
path: normalizedPath || actionName
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1255
|
+
getSimpleEndpoints(obj) {
|
|
1256
|
+
const { collection } = obj;
|
|
1257
|
+
const config = obj.decoratorConfig.api;
|
|
1258
|
+
const exclude = typeof config === "object" && config?.exclude || [];
|
|
1259
|
+
const include = typeof config === "object" && config?.include || void 0;
|
|
1260
|
+
const isCollectionClass = obj.extends === "SmrtCollection" || !!obj.extendsTypeArg;
|
|
1261
|
+
const endpoints = [];
|
|
1262
|
+
const shouldInclude = (op) => {
|
|
1263
|
+
if (include && !include.includes(op)) return false;
|
|
1264
|
+
if (exclude.includes(op)) return false;
|
|
1265
|
+
return true;
|
|
1266
|
+
};
|
|
1267
|
+
if (!isCollectionClass) {
|
|
1268
|
+
if (shouldInclude("list")) {
|
|
1269
|
+
endpoints.push(`GET /${collection}`);
|
|
1270
|
+
}
|
|
1271
|
+
if (shouldInclude("create")) {
|
|
1272
|
+
endpoints.push(`POST /${collection}`);
|
|
1273
|
+
}
|
|
1274
|
+
if (shouldInclude("get")) {
|
|
1275
|
+
endpoints.push(`GET /${collection}/:id`);
|
|
1276
|
+
}
|
|
1277
|
+
if (shouldInclude("update")) {
|
|
1278
|
+
endpoints.push(`PUT /${collection}/:id`);
|
|
1279
|
+
}
|
|
1280
|
+
if (shouldInclude("delete")) {
|
|
1281
|
+
endpoints.push(`DELETE /${collection}/:id`);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
const standardActions = ["list", "get", "create", "update", "delete"];
|
|
1285
|
+
for (const [actionName, actionDef] of Object.entries(obj.methods)) {
|
|
1286
|
+
if (standardActions.includes(actionName) || !actionDef.isPublic || !shouldInclude(actionName)) {
|
|
1287
|
+
continue;
|
|
1288
|
+
}
|
|
1289
|
+
const route = this.getApiRouteMetadata(obj, actionName, actionDef);
|
|
1290
|
+
if (route.scope === "collection" && !isCollectionClass && !actionDef.isStatic) {
|
|
1291
|
+
continue;
|
|
1292
|
+
}
|
|
1293
|
+
if (route.scope === "item" && isCollectionClass) {
|
|
1294
|
+
continue;
|
|
1295
|
+
}
|
|
1296
|
+
const suffix = route.scope === "collection" ? "" : "/:id";
|
|
1297
|
+
endpoints.push(`${route.method} /${collection}${suffix}/${route.path}`);
|
|
1298
|
+
}
|
|
1299
|
+
return endpoints;
|
|
1300
|
+
}
|
|
1301
|
+
/**
|
|
1302
|
+
* Generate a single REST endpoint
|
|
1303
|
+
*/
|
|
1304
|
+
generateRestEndpoint(obj) {
|
|
1305
|
+
const { collection, className } = obj;
|
|
1306
|
+
const config = obj.decoratorConfig.api;
|
|
1307
|
+
const exclude = typeof config === "object" && config?.exclude || [];
|
|
1308
|
+
const include = typeof config === "object" && config?.include || void 0;
|
|
1309
|
+
const operations = [];
|
|
1310
|
+
const shouldInclude = (op) => {
|
|
1311
|
+
if (include && !include.includes(op)) return false;
|
|
1312
|
+
if (exclude.includes(op)) return false;
|
|
1313
|
+
return true;
|
|
1314
|
+
};
|
|
1315
|
+
if (shouldInclude("list")) {
|
|
1316
|
+
operations.push(` // GET /${collection} - List ${collection}`);
|
|
1317
|
+
operations.push(` app.get('/${collection}', async (req: Request) => {`);
|
|
1318
|
+
operations.push(
|
|
1319
|
+
` const collection = await get${className}Collection();`
|
|
1320
|
+
);
|
|
1321
|
+
operations.push(" const items = await collection.list(req.query);");
|
|
1322
|
+
operations.push(" return Response.json(items);");
|
|
1323
|
+
operations.push(" });");
|
|
1324
|
+
}
|
|
1325
|
+
if (shouldInclude("get")) {
|
|
1326
|
+
operations.push(` // GET /${collection}/:id - Get ${className}`);
|
|
1327
|
+
operations.push(
|
|
1328
|
+
` app.get('/${collection}/:id', async (req: Request) => {`
|
|
1329
|
+
);
|
|
1330
|
+
operations.push(
|
|
1331
|
+
` const collection = await get${className}Collection();`
|
|
1332
|
+
);
|
|
1333
|
+
operations.push(" const item = await collection.get(req.params.id);");
|
|
1334
|
+
operations.push(
|
|
1335
|
+
` if (!item) return new Response('Not found', { status: 404 });`
|
|
1336
|
+
);
|
|
1337
|
+
operations.push(" return Response.json(item);");
|
|
1338
|
+
operations.push(" });");
|
|
1339
|
+
}
|
|
1340
|
+
if (shouldInclude("create")) {
|
|
1341
|
+
operations.push(` // POST /${collection} - Create ${className}`);
|
|
1342
|
+
operations.push(` app.post('/${collection}', async (req: Request) => {`);
|
|
1343
|
+
operations.push(
|
|
1344
|
+
` const collection = await get${className}Collection();`
|
|
1345
|
+
);
|
|
1346
|
+
operations.push(" const data = await req.json();");
|
|
1347
|
+
operations.push(" const item = await collection.create(data);");
|
|
1348
|
+
operations.push(" return Response.json(item, { status: 201 });");
|
|
1349
|
+
operations.push(" });");
|
|
1350
|
+
}
|
|
1351
|
+
if (shouldInclude("update")) {
|
|
1352
|
+
operations.push(` // PUT /${collection}/:id - Update ${className}`);
|
|
1353
|
+
operations.push(
|
|
1354
|
+
` app.put('/${collection}/:id', async (req: Request) => {`
|
|
1355
|
+
);
|
|
1356
|
+
operations.push(
|
|
1357
|
+
` const collection = await get${className}Collection();`
|
|
1358
|
+
);
|
|
1359
|
+
operations.push(" const data = await req.json();");
|
|
1360
|
+
operations.push(
|
|
1361
|
+
" const item = await collection.update(req.params.id, data);"
|
|
1362
|
+
);
|
|
1363
|
+
operations.push(
|
|
1364
|
+
` if (!item) return new Response('Not found', { status: 404 });`
|
|
1365
|
+
);
|
|
1366
|
+
operations.push(" return Response.json(item);");
|
|
1367
|
+
operations.push(" });");
|
|
1368
|
+
}
|
|
1369
|
+
if (shouldInclude("delete")) {
|
|
1370
|
+
operations.push(` // DELETE /${collection}/:id - Delete ${className}`);
|
|
1371
|
+
operations.push(
|
|
1372
|
+
` app.delete('/${collection}/:id', async (req: Request) => {`
|
|
1373
|
+
);
|
|
1374
|
+
operations.push(
|
|
1375
|
+
` const collection = await get${className}Collection();`
|
|
1376
|
+
);
|
|
1377
|
+
operations.push(
|
|
1378
|
+
" const success = await collection.delete(req.params.id);"
|
|
1379
|
+
);
|
|
1380
|
+
operations.push(
|
|
1381
|
+
` if (!success) return new Response('Not found', { status: 404 });`
|
|
1382
|
+
);
|
|
1383
|
+
operations.push(` return new Response('', { status: 204 });`);
|
|
1384
|
+
operations.push(" });");
|
|
1385
|
+
}
|
|
1386
|
+
return `// ${className} endpoints
|
|
1387
|
+
${operations.join("\n")}`;
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* Generate simple MCP tool names for testing/documentation
|
|
1391
|
+
*/
|
|
1392
|
+
generateMCPTools(manifest) {
|
|
1393
|
+
const tools = [];
|
|
1394
|
+
for (const [_name, obj] of Object.entries(manifest.objects)) {
|
|
1395
|
+
const mcpConfig = obj.decoratorConfig.mcp;
|
|
1396
|
+
if (mcpConfig !== false) {
|
|
1397
|
+
tools.push(...this.getSimpleMCPToolNames(obj));
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
return tools.join("\n");
|
|
1401
|
+
}
|
|
1402
|
+
/**
|
|
1403
|
+
* Generate MCP tool JSON definitions
|
|
1404
|
+
*/
|
|
1405
|
+
generateMCPToolsCode(manifest) {
|
|
1406
|
+
const tools = [];
|
|
1407
|
+
for (const [_name, obj] of Object.entries(manifest.objects)) {
|
|
1408
|
+
const mcpConfig = obj.decoratorConfig.mcp;
|
|
1409
|
+
if (mcpConfig !== false) {
|
|
1410
|
+
tools.push(this.generateMCPTool(obj));
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
return `[
|
|
1414
|
+
${tools.join(",\n")}
|
|
1415
|
+
]`;
|
|
1416
|
+
}
|
|
1417
|
+
/**
|
|
1418
|
+
* Get simple MCP tool names for an object
|
|
1419
|
+
*/
|
|
1420
|
+
getSimpleMCPToolNames(obj) {
|
|
1421
|
+
const { collection } = obj;
|
|
1422
|
+
const config = obj.decoratorConfig.mcp;
|
|
1423
|
+
const exclude = typeof config === "object" && config?.exclude || [];
|
|
1424
|
+
const include = typeof config === "object" && config?.include || void 0;
|
|
1425
|
+
const tools = [];
|
|
1426
|
+
const shouldInclude = (op) => {
|
|
1427
|
+
if (include && !include.includes(op)) return false;
|
|
1428
|
+
if (exclude.includes(op)) return false;
|
|
1429
|
+
return true;
|
|
1430
|
+
};
|
|
1431
|
+
if (shouldInclude("list")) {
|
|
1432
|
+
tools.push(`list_${collection}`);
|
|
1433
|
+
}
|
|
1434
|
+
if (shouldInclude("get")) {
|
|
1435
|
+
tools.push(`get_${collection}`);
|
|
1436
|
+
}
|
|
1437
|
+
if (shouldInclude("create")) {
|
|
1438
|
+
tools.push(`create_${collection}`);
|
|
1439
|
+
}
|
|
1440
|
+
if (shouldInclude("update")) {
|
|
1441
|
+
tools.push(`update_${collection}`);
|
|
1442
|
+
}
|
|
1443
|
+
if (shouldInclude("delete")) {
|
|
1444
|
+
tools.push(`delete_${collection}`);
|
|
1445
|
+
}
|
|
1446
|
+
return tools;
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Generate a single MCP tool
|
|
1450
|
+
*/
|
|
1451
|
+
generateMCPTool(obj) {
|
|
1452
|
+
const { collection, className, name } = obj;
|
|
1453
|
+
const config = obj.decoratorConfig.mcp;
|
|
1454
|
+
const exclude = typeof config === "object" && config?.exclude || [];
|
|
1455
|
+
const include = typeof config === "object" && config?.include || void 0;
|
|
1456
|
+
const tools = [];
|
|
1457
|
+
const shouldInclude = (op) => {
|
|
1458
|
+
if (include && !include.includes(op)) return false;
|
|
1459
|
+
if (exclude.includes(op)) return false;
|
|
1460
|
+
return true;
|
|
1461
|
+
};
|
|
1462
|
+
if (shouldInclude("list")) {
|
|
1463
|
+
tools.push(` {
|
|
1464
|
+
name: "list_${collection}",
|
|
1465
|
+
description: "List ${collection}",
|
|
1466
|
+
inputSchema: {
|
|
1467
|
+
type: "object",
|
|
1468
|
+
properties: {
|
|
1469
|
+
limit: { type: "number" },
|
|
1470
|
+
offset: { type: "number" },
|
|
1471
|
+
where: { type: "object" }
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
}`);
|
|
1475
|
+
}
|
|
1476
|
+
if (shouldInclude("get")) {
|
|
1477
|
+
tools.push(` {
|
|
1478
|
+
name: "get_${name}",
|
|
1479
|
+
description: "Get a ${name} by ID",
|
|
1480
|
+
inputSchema: {
|
|
1481
|
+
type: "object",
|
|
1482
|
+
properties: {
|
|
1483
|
+
id: { type: "string", description: "The ${name} ID" }
|
|
1484
|
+
},
|
|
1485
|
+
required: ["id"]
|
|
1486
|
+
}
|
|
1487
|
+
}`);
|
|
1488
|
+
}
|
|
1489
|
+
if (shouldInclude("create")) {
|
|
1490
|
+
const requiredFields = Object.entries(obj.fields).filter(([_, field]) => field.required).map(([fieldName]) => fieldName);
|
|
1491
|
+
tools.push(` {
|
|
1492
|
+
name: "create_${name}",
|
|
1493
|
+
description: "Create a new ${name}",
|
|
1494
|
+
inputSchema: {
|
|
1495
|
+
type: "object",
|
|
1496
|
+
properties: ${JSON.stringify(this.generateSchemaProperties(obj.fields), null, 6)},
|
|
1497
|
+
required: ${JSON.stringify(requiredFields)}
|
|
1498
|
+
}
|
|
1499
|
+
}`);
|
|
1500
|
+
}
|
|
1501
|
+
return tools.join(",\n");
|
|
1502
|
+
}
|
|
1503
|
+
/**
|
|
1504
|
+
* Generate JSON schema properties for fields
|
|
1505
|
+
*/
|
|
1506
|
+
generateSchemaProperties(fields) {
|
|
1507
|
+
const properties = {};
|
|
1508
|
+
for (const [name, field] of Object.entries(fields)) {
|
|
1509
|
+
properties[name] = {
|
|
1510
|
+
type: this.mapFieldTypeToJSON(field.type),
|
|
1511
|
+
description: field.description || `The ${name} field`
|
|
1512
|
+
};
|
|
1513
|
+
if (field.min !== void 0) properties[name].minimum = field.min;
|
|
1514
|
+
if (field.max !== void 0) properties[name].maximum = field.max;
|
|
1515
|
+
if (field.minLength !== void 0)
|
|
1516
|
+
properties[name].minLength = field.minLength;
|
|
1517
|
+
if (field.maxLength !== void 0)
|
|
1518
|
+
properties[name].maxLength = field.maxLength;
|
|
1519
|
+
}
|
|
1520
|
+
return properties;
|
|
1521
|
+
}
|
|
1522
|
+
/**
|
|
1523
|
+
* Map field types to JSON Schema types
|
|
1524
|
+
*/
|
|
1525
|
+
mapFieldTypeToJSON(fieldType) {
|
|
1526
|
+
switch (fieldType) {
|
|
1527
|
+
case "text":
|
|
1528
|
+
return "string";
|
|
1529
|
+
case "decimal":
|
|
1530
|
+
return "number";
|
|
1531
|
+
case "integer":
|
|
1532
|
+
return "integer";
|
|
1533
|
+
case "boolean":
|
|
1534
|
+
return "boolean";
|
|
1535
|
+
case "datetime":
|
|
1536
|
+
return "string";
|
|
1537
|
+
case "json":
|
|
1538
|
+
return "object";
|
|
1539
|
+
case "foreignKey":
|
|
1540
|
+
return "string";
|
|
1541
|
+
case "crossPackageRef":
|
|
1542
|
+
return "string";
|
|
1543
|
+
default:
|
|
1544
|
+
return "string";
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Generate agent manifests for Agent subclasses (fifth pass).
|
|
1549
|
+
*
|
|
1550
|
+
* For any SmartObjectDefinition with `agent` in its decoratorConfig,
|
|
1551
|
+
* auto-generates:
|
|
1552
|
+
* - Permissions from uiSlots (manage:*) and CLI/MCP methods (execute:*)
|
|
1553
|
+
* - Features from uiSlots and exposed methods
|
|
1554
|
+
* - Menu items from uiSlots
|
|
1555
|
+
* - Component declarations from package.json exports
|
|
1556
|
+
*
|
|
1557
|
+
* @param manifest - The manifest to process in-place
|
|
1558
|
+
* @param packageName - Package name for component export paths
|
|
1559
|
+
* @param packageJson - Full package.json for component discovery
|
|
1560
|
+
*/
|
|
1561
|
+
generateAgentManifests(manifest, packageName, packageJson) {
|
|
1562
|
+
for (const obj of Object.values(manifest.objects)) {
|
|
1563
|
+
if (!obj.decoratorConfig.agent) continue;
|
|
1564
|
+
const agentConfig = obj.decoratorConfig.agent;
|
|
1565
|
+
const slug = obj.className.toLowerCase();
|
|
1566
|
+
const uiSlots = obj.staticProperties?.uiSlots ?? {};
|
|
1567
|
+
const permissions = [];
|
|
1568
|
+
for (const [slotId, slot] of Object.entries(uiSlots)) {
|
|
1569
|
+
permissions.push({
|
|
1570
|
+
id: `manage:${slotId}`,
|
|
1571
|
+
label: `Manage ${slot.label || slotId}`,
|
|
1572
|
+
category: "slot",
|
|
1573
|
+
defaultGranted: true
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
const exposedMethods = this.getExposedMethods(obj);
|
|
1577
|
+
for (const methodName of exposedMethods) {
|
|
1578
|
+
permissions.push({
|
|
1579
|
+
id: `execute:${methodName}`,
|
|
1580
|
+
label: `Execute ${methodName}`,
|
|
1581
|
+
category: "method",
|
|
1582
|
+
defaultGranted: true
|
|
1583
|
+
});
|
|
1584
|
+
}
|
|
1585
|
+
const features = [];
|
|
1586
|
+
for (const [slotId, slot] of Object.entries(uiSlots)) {
|
|
1587
|
+
const typedSlot = slot;
|
|
1588
|
+
features.push({
|
|
1589
|
+
id: slotId,
|
|
1590
|
+
label: typedSlot.label || slotId,
|
|
1591
|
+
description: typedSlot.description,
|
|
1592
|
+
type: "slot"
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
for (const methodName of exposedMethods) {
|
|
1596
|
+
features.push({
|
|
1597
|
+
id: methodName,
|
|
1598
|
+
label: methodName,
|
|
1599
|
+
type: "method"
|
|
1600
|
+
});
|
|
1601
|
+
}
|
|
1602
|
+
const menuItems = Object.entries(uiSlots).map(([slotId, slot]) => {
|
|
1603
|
+
const typedSlot = slot;
|
|
1604
|
+
return {
|
|
1605
|
+
id: slotId,
|
|
1606
|
+
label: typedSlot.label || slotId,
|
|
1607
|
+
icon: typedSlot.icon,
|
|
1608
|
+
order: typedSlot.order ?? 999,
|
|
1609
|
+
path: `/agents/${slug}/${slotId}`,
|
|
1610
|
+
requiredPermission: `manage:${slotId}`
|
|
1611
|
+
};
|
|
1612
|
+
}).sort((a, b) => a.order - b.order);
|
|
1613
|
+
const components = [];
|
|
1614
|
+
if (packageName && packageJson?.exports) {
|
|
1615
|
+
for (const [exportKey, exportValue] of Object.entries(
|
|
1616
|
+
packageJson.exports
|
|
1617
|
+
)) {
|
|
1618
|
+
if (exportKey === "." || exportKey === "./manifest") continue;
|
|
1619
|
+
const hasSvelteCondition = this.hasSvelteExport(exportValue);
|
|
1620
|
+
if (hasSvelteCondition) {
|
|
1621
|
+
const type = exportKey.replace("./", "");
|
|
1622
|
+
components.push({
|
|
1623
|
+
exportPath: `${packageName}/${type}`,
|
|
1624
|
+
type
|
|
1625
|
+
});
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
const adminRoutes = obj.staticProperties?.adminRoutes ?? [];
|
|
1630
|
+
const signalSubscriptions = obj.staticProperties?.signalSubscriptions ?? [];
|
|
1631
|
+
const agentManifest = {
|
|
1632
|
+
name: obj.className,
|
|
1633
|
+
slug,
|
|
1634
|
+
icon: agentConfig.icon,
|
|
1635
|
+
tier: agentConfig.tier || "free",
|
|
1636
|
+
description: agentConfig.description,
|
|
1637
|
+
uiSlots,
|
|
1638
|
+
...adminRoutes.length > 0 ? { adminRoutes } : {},
|
|
1639
|
+
...signalSubscriptions.length > 0 ? { signalSubscriptions } : {},
|
|
1640
|
+
permissions,
|
|
1641
|
+
features,
|
|
1642
|
+
menuItems,
|
|
1643
|
+
components
|
|
1644
|
+
};
|
|
1645
|
+
obj.agent = agentManifest;
|
|
1646
|
+
console.log(
|
|
1647
|
+
`[manifest-generator] Generated agent manifest for ${obj.className}: ${permissions.length} permissions, ${features.length} features, ${menuItems.length} menu items, ${components.length} components`
|
|
1648
|
+
);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
/**
|
|
1652
|
+
* Get deduplicated list of method names exposed via CLI and MCP config
|
|
1653
|
+
*/
|
|
1654
|
+
getExposedMethods(obj) {
|
|
1655
|
+
const methods = /* @__PURE__ */ new Set();
|
|
1656
|
+
const cliConfig = obj.decoratorConfig.cli;
|
|
1657
|
+
if (cliConfig && typeof cliConfig === "object" && cliConfig.include) {
|
|
1658
|
+
for (const m of cliConfig.include) {
|
|
1659
|
+
methods.add(m);
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
const mcpConfig = obj.decoratorConfig.mcp;
|
|
1663
|
+
if (mcpConfig && typeof mcpConfig === "object" && mcpConfig.include) {
|
|
1664
|
+
for (const m of mcpConfig.include) {
|
|
1665
|
+
methods.add(m);
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
return Array.from(methods);
|
|
1669
|
+
}
|
|
1670
|
+
/**
|
|
1671
|
+
* Check if an export value has a svelte condition (indicating a component export)
|
|
1672
|
+
*/
|
|
1673
|
+
hasSvelteExport(exportValue) {
|
|
1674
|
+
if (!exportValue || typeof exportValue !== "object") return false;
|
|
1675
|
+
if ("svelte" in exportValue) return true;
|
|
1676
|
+
for (const val of Object.values(exportValue)) {
|
|
1677
|
+
if (val && typeof val === "object" && "svelte" in val) {
|
|
1678
|
+
return true;
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
return false;
|
|
1682
|
+
}
|
|
1683
|
+
/**
|
|
1684
|
+
* Save manifest to file
|
|
1685
|
+
*/
|
|
1686
|
+
saveManifest(manifest, filePath) {
|
|
1687
|
+
const fs = require$1("node:fs");
|
|
1688
|
+
fs.writeFileSync(filePath, JSON.stringify(manifest, null, 2));
|
|
1689
|
+
}
|
|
1690
|
+
/**
|
|
1691
|
+
* Load manifest from file
|
|
1692
|
+
*/
|
|
1693
|
+
loadManifest(filePath) {
|
|
1694
|
+
const fs = require$1("node:fs");
|
|
1695
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
1696
|
+
return JSON.parse(content);
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
function generateManifest(scanResults, options) {
|
|
1700
|
+
const generator = new ManifestGenerator();
|
|
1701
|
+
return generator.generateManifest(scanResults, options);
|
|
1702
|
+
}
|
|
1703
|
+
export {
|
|
1704
|
+
ManifestGenerator,
|
|
1705
|
+
generateManifest
|
|
1706
|
+
};
|
|
1707
|
+
//# sourceMappingURL=manifest-generator.js.map
|