@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,1375 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, unlinkSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join, relative } from "node:path";
|
|
3
|
+
const BIOME_LINE_WIDTH = 80;
|
|
4
|
+
const AUTO_GENERATED_ROUTE_HEADER = "// Auto-generated by @smrt/core vite plugin";
|
|
5
|
+
const STANDARD_API_ACTIONS = ["list", "get", "create", "update", "delete"];
|
|
6
|
+
function extractSimpleClassName(qualifiedName) {
|
|
7
|
+
const colonIndex = qualifiedName.indexOf(":");
|
|
8
|
+
if (colonIndex !== -1) {
|
|
9
|
+
return qualifiedName.substring(colonIndex + 1);
|
|
10
|
+
}
|
|
11
|
+
return qualifiedName;
|
|
12
|
+
}
|
|
13
|
+
function isCollectionClass(objectDef) {
|
|
14
|
+
return objectDef.extends === "SmrtCollection" || !!objectDef.extendsTypeArg;
|
|
15
|
+
}
|
|
16
|
+
function getRegistrationPackageName(manifest, objectDef, isLocal) {
|
|
17
|
+
if (isLocal) {
|
|
18
|
+
return manifest.packageName ?? objectDef.packageName;
|
|
19
|
+
}
|
|
20
|
+
return objectDef.packageName;
|
|
21
|
+
}
|
|
22
|
+
function toSingleQuotedStringLiteral(value) {
|
|
23
|
+
const jsonLiteral = JSON.stringify(value);
|
|
24
|
+
const jsonContent = jsonLiteral.slice(1, -1).replaceAll("'", "\\'");
|
|
25
|
+
return `'${jsonContent}'`;
|
|
26
|
+
}
|
|
27
|
+
function getApiConfigObject(apiConfig) {
|
|
28
|
+
if (!apiConfig || typeof apiConfig !== "object") {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return apiConfig;
|
|
32
|
+
}
|
|
33
|
+
function isSameSerializerReference(left, right) {
|
|
34
|
+
return !!left && !!right && left.importPath === right.importPath && left.exportName === right.exportName;
|
|
35
|
+
}
|
|
36
|
+
function resolveStandardRouteSerializers(apiConfig) {
|
|
37
|
+
const config = getApiConfigObject(apiConfig);
|
|
38
|
+
const itemSerializer = config?.serializers?.item;
|
|
39
|
+
const listItemSerializer = config?.serializers?.listItem || config?.serializers?.item;
|
|
40
|
+
const importStatements = [];
|
|
41
|
+
let itemSerializerName;
|
|
42
|
+
let listItemSerializerName;
|
|
43
|
+
if (itemSerializer) {
|
|
44
|
+
itemSerializerName = "serializeItemResponse";
|
|
45
|
+
importStatements.push(
|
|
46
|
+
`import { ${itemSerializer.exportName} as ${itemSerializerName} } from '${itemSerializer.importPath}';`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
if (listItemSerializer) {
|
|
50
|
+
if (isSameSerializerReference(listItemSerializer, itemSerializer)) {
|
|
51
|
+
listItemSerializerName = itemSerializerName;
|
|
52
|
+
} else {
|
|
53
|
+
listItemSerializerName = "serializeListItemResponse";
|
|
54
|
+
importStatements.push(
|
|
55
|
+
`import { ${listItemSerializer.exportName} as ${listItemSerializerName} } from '${listItemSerializer.importPath}';`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
importStatements,
|
|
61
|
+
itemSerializerName,
|
|
62
|
+
listItemSerializerName
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function collectReadonlyFieldNames(objectDef) {
|
|
66
|
+
const fields = objectDef.fields || {};
|
|
67
|
+
const names = [];
|
|
68
|
+
for (const [name, def] of Object.entries(fields)) {
|
|
69
|
+
const meta = def._meta;
|
|
70
|
+
if (def.readonly === true || meta?.readonly === true) {
|
|
71
|
+
names.push(name);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return names;
|
|
75
|
+
}
|
|
76
|
+
function getApiWritableAllowlist(apiConfig) {
|
|
77
|
+
const config = getApiConfigObject(apiConfig);
|
|
78
|
+
return Array.isArray(config?.writable) ? config.writable : null;
|
|
79
|
+
}
|
|
80
|
+
function generateWritablePolicyHelper(objectDef) {
|
|
81
|
+
const readonly = collectReadonlyFieldNames(objectDef);
|
|
82
|
+
const writable = getApiWritableAllowlist(objectDef.decoratorConfig?.api);
|
|
83
|
+
return `
|
|
84
|
+
// Mass-assignment guard (#1540): strip framework/server-managed + read-only
|
|
85
|
+
// fields from create/update request bodies before they reach the model.
|
|
86
|
+
const WRITABLE_ALLOWLIST: string[] | null = ${writable ? JSON.stringify(writable) : "null"};
|
|
87
|
+
const READONLY_FIELDS: string[] = ${JSON.stringify(readonly)};
|
|
88
|
+
const SERVER_MANAGED_FIELDS = [
|
|
89
|
+
'id',
|
|
90
|
+
'tenantId',
|
|
91
|
+
'tenant_id',
|
|
92
|
+
'createdAt',
|
|
93
|
+
'created_at',
|
|
94
|
+
'updatedAt',
|
|
95
|
+
'updated_at',
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
function applyWritablePolicy(data: unknown): Record<string, unknown> {
|
|
99
|
+
if (!data || typeof data !== 'object') return {};
|
|
100
|
+
const result: Record<string, unknown> = {};
|
|
101
|
+
for (const [key, value] of Object.entries(data as Record<string, unknown>)) {
|
|
102
|
+
if (key.startsWith('_')) continue;
|
|
103
|
+
if (SERVER_MANAGED_FIELDS.includes(key)) continue;
|
|
104
|
+
if (READONLY_FIELDS.includes(key)) continue;
|
|
105
|
+
if (WRITABLE_ALLOWLIST && !WRITABLE_ALLOWLIST.includes(key)) continue;
|
|
106
|
+
result[key] = value;
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
`;
|
|
111
|
+
}
|
|
112
|
+
function getApiPublicAccess(apiConfig) {
|
|
113
|
+
const config = getApiConfigObject(apiConfig);
|
|
114
|
+
const value = config?.public;
|
|
115
|
+
if (value === true || value === "read") {
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
function generateAuthGuardHelper(objectDef) {
|
|
121
|
+
const publicAccess = getApiPublicAccess(objectDef.decoratorConfig?.api);
|
|
122
|
+
return `
|
|
123
|
+
// Fail-closed authorization (#1540): generated routes require an authenticated
|
|
124
|
+
// principal on \`locals\` unless explicitly marked \`@smrt({ api: { public } })\`.
|
|
125
|
+
const PUBLIC_ACCESS: boolean | 'read' = ${JSON.stringify(publicAccess)};
|
|
126
|
+
|
|
127
|
+
function hasAuthenticatedPrincipal(locals: unknown): boolean {
|
|
128
|
+
if (!locals || typeof locals !== 'object') return false;
|
|
129
|
+
const l = locals as Record<string, unknown>;
|
|
130
|
+
// Only a resolved, object-shaped principal counts. We intentionally do NOT
|
|
131
|
+
// treat \`locals.auth\` as a signal: Auth.js/SvelteKit put a callable
|
|
132
|
+
// \`auth()\` helper on every request (including anonymous ones), so honoring
|
|
133
|
+
// it would fail OPEN. Booleans don't count either (no convention sets
|
|
134
|
+
// \`locals.user = true\`); the only boolean accepted is the explicit
|
|
135
|
+
// \`smrtAuth\` opt-in marker.
|
|
136
|
+
const isResolvedPrincipal = (v: unknown) =>
|
|
137
|
+
typeof v === 'object' && v !== null;
|
|
138
|
+
return (
|
|
139
|
+
isResolvedPrincipal(l.user) ||
|
|
140
|
+
isResolvedPrincipal(l.session) ||
|
|
141
|
+
l.smrtAuth === true
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function requireRouteAuth(locals: unknown, mutating: boolean): void {
|
|
146
|
+
if (PUBLIC_ACCESS === true) return;
|
|
147
|
+
if (PUBLIC_ACCESS === 'read' && !mutating) return;
|
|
148
|
+
if (!hasAuthenticatedPrincipal(locals)) {
|
|
149
|
+
throw error(401, 'Authentication required');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Sensitive-field-safe serialization for custom-action results (#1540): a
|
|
154
|
+
// custom method may return a SmrtObject (or one nested in an array/plain
|
|
155
|
+
// object), so recurse and route each through toPublicJSON() rather than letting
|
|
156
|
+
// JSON.stringify call toJSON(). Non-plain instances (Date, etc.) and primitives
|
|
157
|
+
// pass through; a cycle guard prevents infinite loops.
|
|
158
|
+
function toPublicResult(value: any, seen: WeakSet<object> = new WeakSet()): any {
|
|
159
|
+
if (value === null || typeof value !== 'object') return value;
|
|
160
|
+
if (typeof value.toPublicJSON === 'function') return value.toPublicJSON();
|
|
161
|
+
if (Array.isArray(value)) {
|
|
162
|
+
if (seen.has(value)) return value;
|
|
163
|
+
seen.add(value);
|
|
164
|
+
return value.map((entry: any) => toPublicResult(entry, seen));
|
|
165
|
+
}
|
|
166
|
+
const proto = Object.getPrototypeOf(value);
|
|
167
|
+
if (proto !== Object.prototype && proto !== null) return value;
|
|
168
|
+
if (seen.has(value)) return value;
|
|
169
|
+
seen.add(value);
|
|
170
|
+
const out: Record<string, any> = {};
|
|
171
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
172
|
+
out[key] = toPublicResult(entry, seen);
|
|
173
|
+
}
|
|
174
|
+
return out;
|
|
175
|
+
}
|
|
176
|
+
`;
|
|
177
|
+
}
|
|
178
|
+
function isTenantScoped(objectDef) {
|
|
179
|
+
return !!objectDef.decoratorConfig?.tenantScoped;
|
|
180
|
+
}
|
|
181
|
+
function generateTenantContextHelper() {
|
|
182
|
+
return `
|
|
183
|
+
import { enterTenantContext, hasTenantContext } from '@happyvertical/smrt-tenancy';
|
|
184
|
+
|
|
185
|
+
function establishTenantContext(locals: unknown): void {
|
|
186
|
+
if (hasTenantContext()) return;
|
|
187
|
+
if (!locals || typeof locals !== 'object') return;
|
|
188
|
+
const l = locals as Record<string, any>;
|
|
189
|
+
const tenantId = l.tenantId ?? l.user?.tenantId ?? l.session?.tenantId;
|
|
190
|
+
if (typeof tenantId === 'string' && tenantId) {
|
|
191
|
+
enterTenantContext({ tenantId });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
`;
|
|
195
|
+
}
|
|
196
|
+
function routeGuardPreamble(objectDef, mutating) {
|
|
197
|
+
const lines = [` requireRouteAuth(locals, ${mutating});`];
|
|
198
|
+
if (isTenantScoped(objectDef)) {
|
|
199
|
+
lines.push(" establishTenantContext(locals);");
|
|
200
|
+
}
|
|
201
|
+
return lines.join("\n");
|
|
202
|
+
}
|
|
203
|
+
function normalizeApiHttpMethod(method) {
|
|
204
|
+
switch (method?.toUpperCase()) {
|
|
205
|
+
case "GET":
|
|
206
|
+
case "POST":
|
|
207
|
+
case "PUT":
|
|
208
|
+
case "PATCH":
|
|
209
|
+
case "DELETE":
|
|
210
|
+
return method.toUpperCase();
|
|
211
|
+
default:
|
|
212
|
+
return "POST";
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function methodNameToKebab(name) {
|
|
216
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").toLowerCase();
|
|
217
|
+
}
|
|
218
|
+
function normalizeCustomRoutePath(actionName, path, options = {}) {
|
|
219
|
+
if (path) {
|
|
220
|
+
const segments = path.split("/").map((segment2) => segment2.trim()).filter(Boolean);
|
|
221
|
+
if (segments.length > 0) {
|
|
222
|
+
return segments;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const segment = options.kebabRoutes ? methodNameToKebab(actionName) : actionName;
|
|
226
|
+
return [segment];
|
|
227
|
+
}
|
|
228
|
+
function extractRoutePathParamNames(pathSegments) {
|
|
229
|
+
return pathSegments.filter((segment) => /^\[[^\]]+\]$/.test(segment)).map((segment) => segment.slice(1, -1)).filter(Boolean);
|
|
230
|
+
}
|
|
231
|
+
function resolveApiActionRouteConfig(actionName, actionDef, apiConfig, routeOptions = {}, defaultScope = actionDef.isStatic ? "collection" : "item") {
|
|
232
|
+
const config = getApiConfigObject(apiConfig);
|
|
233
|
+
const routeConfig = config?.routes?.[actionName];
|
|
234
|
+
const pathSegments = normalizeCustomRoutePath(
|
|
235
|
+
actionName,
|
|
236
|
+
routeConfig?.path,
|
|
237
|
+
routeOptions
|
|
238
|
+
);
|
|
239
|
+
return {
|
|
240
|
+
scope: routeConfig?.scope || defaultScope,
|
|
241
|
+
method: normalizeApiHttpMethod(routeConfig?.method),
|
|
242
|
+
pathSegments,
|
|
243
|
+
pathParamNames: extractRoutePathParamNames(pathSegments)
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
function buildRouteHandlerArgs(includeParams, includeRequest) {
|
|
247
|
+
const args = ["locals"];
|
|
248
|
+
if (includeParams) {
|
|
249
|
+
args.push("params");
|
|
250
|
+
}
|
|
251
|
+
if (includeRequest) {
|
|
252
|
+
args.push("request");
|
|
253
|
+
}
|
|
254
|
+
return `{ ${args.join(", ")} }`;
|
|
255
|
+
}
|
|
256
|
+
function buildPathParamsObjectLiteral(pathParamNames) {
|
|
257
|
+
if (pathParamNames.length === 0) {
|
|
258
|
+
return "{}";
|
|
259
|
+
}
|
|
260
|
+
return `{
|
|
261
|
+
${pathParamNames.map(
|
|
262
|
+
(paramName) => ` ${JSON.stringify(paramName)}: params[${JSON.stringify(paramName)}],`
|
|
263
|
+
).join("\n")}
|
|
264
|
+
}`;
|
|
265
|
+
}
|
|
266
|
+
function buildActionInvocationArgs(actionDef) {
|
|
267
|
+
const parameters = Array.isArray(actionDef.parameters) ? actionDef.parameters : [];
|
|
268
|
+
if (parameters.length === 0) {
|
|
269
|
+
return [];
|
|
270
|
+
}
|
|
271
|
+
if (parameters.length === 1 && parameters[0]?.name === "options") {
|
|
272
|
+
return ["options"];
|
|
273
|
+
}
|
|
274
|
+
return parameters.map(
|
|
275
|
+
(parameter) => buildOptionsPropertyAccess(parameter.name)
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
function buildOptionsPropertyAccess(propertyName) {
|
|
279
|
+
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyName)) {
|
|
280
|
+
return `options.${propertyName}`;
|
|
281
|
+
}
|
|
282
|
+
const escapedPropertyName = propertyName.replaceAll("\\", "\\\\").replaceAll("'", "\\'");
|
|
283
|
+
return `options['${escapedPropertyName}']`;
|
|
284
|
+
}
|
|
285
|
+
function buildActionInvocationExpression(receiverExpression, actionName, invocationArgs) {
|
|
286
|
+
const singleLineInvocation = `await ${receiverExpression}.${actionName}(${invocationArgs.join(", ")})`;
|
|
287
|
+
const singleLineResultAssignment = ` const result = ${singleLineInvocation};`;
|
|
288
|
+
if (singleLineResultAssignment.length <= 100) {
|
|
289
|
+
return singleLineInvocation;
|
|
290
|
+
}
|
|
291
|
+
return [
|
|
292
|
+
`await ${receiverExpression}.${actionName}(`,
|
|
293
|
+
...invocationArgs.map((argument) => ` ${argument},`),
|
|
294
|
+
" )"
|
|
295
|
+
].join("\n");
|
|
296
|
+
}
|
|
297
|
+
function findItemClassRegistryKey(className, objectDef, manifest) {
|
|
298
|
+
const itemClassName = objectDef.extendsTypeArg || (className.endsWith("Collection") ? className.slice(0, -"Collection".length) : void 0);
|
|
299
|
+
if (!itemClassName) {
|
|
300
|
+
return className;
|
|
301
|
+
}
|
|
302
|
+
const manifestMatch = Object.entries(manifest.objects).find(
|
|
303
|
+
([manifestKey, candidate]) => manifestKey === itemClassName || candidate.className === itemClassName
|
|
304
|
+
);
|
|
305
|
+
return manifestMatch?.[0] || itemClassName;
|
|
306
|
+
}
|
|
307
|
+
function groupCustomActionRoutes(actionSpecs) {
|
|
308
|
+
const groupedRoutes = /* @__PURE__ */ new Map();
|
|
309
|
+
for (const { routeDir, spec } of actionSpecs) {
|
|
310
|
+
const existing = groupedRoutes.get(routeDir) || [];
|
|
311
|
+
const duplicateMethod = existing.find(
|
|
312
|
+
(candidate) => candidate.routeConfig.method === spec.routeConfig.method
|
|
313
|
+
);
|
|
314
|
+
if (duplicateMethod) {
|
|
315
|
+
throw new Error(
|
|
316
|
+
`Duplicate custom API route handler for ${routeDir} (${spec.routeConfig.method}). Methods ${duplicateMethod.actionName} and ${spec.actionName} resolve to the same generated route.`
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
existing.push(spec);
|
|
320
|
+
groupedRoutes.set(routeDir, existing);
|
|
321
|
+
}
|
|
322
|
+
return groupedRoutes;
|
|
323
|
+
}
|
|
324
|
+
async function generateSvelteKitRoutes(projectRoot, manifest, options) {
|
|
325
|
+
if (!options.enabled) return;
|
|
326
|
+
console.log("[smrt] Generating SvelteKit routes...");
|
|
327
|
+
clearGeneratedRouteFiles(join(projectRoot, options.routesDir));
|
|
328
|
+
clearGeneratedKnowledgeRoute(projectRoot, options);
|
|
329
|
+
await generateSmrtConfigFile(projectRoot, manifest, options);
|
|
330
|
+
let generatedCount = 0;
|
|
331
|
+
let skippedCollections = 0;
|
|
332
|
+
for (const [className, objectDef] of Object.entries(manifest.objects)) {
|
|
333
|
+
if (isCollectionClass(objectDef)) {
|
|
334
|
+
const generatedCollectionRoutes = await generateCollectionRoutesForObject(
|
|
335
|
+
projectRoot,
|
|
336
|
+
className,
|
|
337
|
+
objectDef,
|
|
338
|
+
manifest,
|
|
339
|
+
options
|
|
340
|
+
);
|
|
341
|
+
if (generatedCollectionRoutes) {
|
|
342
|
+
generatedCount++;
|
|
343
|
+
} else {
|
|
344
|
+
console.log(
|
|
345
|
+
`[smrt] Skipping ${className} - no collection API routes to generate`
|
|
346
|
+
);
|
|
347
|
+
skippedCollections++;
|
|
348
|
+
}
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
await generateRoutesForObject(projectRoot, className, objectDef, options);
|
|
352
|
+
generatedCount++;
|
|
353
|
+
}
|
|
354
|
+
if (options.knowledge?.api?.enabled) {
|
|
355
|
+
generateKnowledgeRoute(projectRoot, options);
|
|
356
|
+
}
|
|
357
|
+
updateGitignore(projectRoot, options);
|
|
358
|
+
const skippedMsg = skippedCollections > 0 ? ` (skipped ${skippedCollections} collection classes)` : "";
|
|
359
|
+
console.log(
|
|
360
|
+
`[smrt] Generated routes for ${generatedCount} SMRT objects${skippedMsg}`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
function clearGeneratedRouteFiles(routesRoot) {
|
|
364
|
+
if (!existsSync(routesRoot)) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
for (const entry of readdirSync(routesRoot, { withFileTypes: true })) {
|
|
368
|
+
const entryPath = join(routesRoot, entry.name);
|
|
369
|
+
if (entry.isDirectory()) {
|
|
370
|
+
clearGeneratedRouteFiles(entryPath);
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
if (!entry.isFile() || entry.name !== "+server.ts") {
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
const fileContent = readFileSync(entryPath, "utf-8");
|
|
377
|
+
if (fileContent.startsWith(AUTO_GENERATED_ROUTE_HEADER)) {
|
|
378
|
+
unlinkSync(entryPath);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
function knowledgeRouteDir(projectRoot, options) {
|
|
383
|
+
const basePath = options.knowledge?.api?.basePath || "/__smrt/knowledge";
|
|
384
|
+
const routeRoot = svelteKitRouteRoot(options.routesDir);
|
|
385
|
+
const segments = basePath.split("/").map((segment) => segment.trim()).filter(Boolean);
|
|
386
|
+
return join(projectRoot, routeRoot, ...segments);
|
|
387
|
+
}
|
|
388
|
+
function svelteKitRouteRoot(routesDir) {
|
|
389
|
+
const normalized = routesDir.replaceAll("\\", "/").replace(/\/+$/, "");
|
|
390
|
+
const marker = "/routes";
|
|
391
|
+
const markerIndex = normalized.indexOf(marker);
|
|
392
|
+
if (normalized === "src/routes" || normalized.endsWith("/routes")) {
|
|
393
|
+
return normalized;
|
|
394
|
+
}
|
|
395
|
+
if (markerIndex !== -1) {
|
|
396
|
+
return normalized.slice(0, markerIndex + marker.length);
|
|
397
|
+
}
|
|
398
|
+
return "src/routes";
|
|
399
|
+
}
|
|
400
|
+
function clearGeneratedKnowledgeRoute(projectRoot, options) {
|
|
401
|
+
const routeDir = knowledgeRouteDir(projectRoot, options);
|
|
402
|
+
const routePath = join(routeDir, "+server.ts");
|
|
403
|
+
if (!existsSync(routePath)) return;
|
|
404
|
+
const content = readFileSync(routePath, "utf-8");
|
|
405
|
+
if (content.startsWith(AUTO_GENERATED_ROUTE_HEADER)) {
|
|
406
|
+
unlinkSync(routePath);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function generateKnowledgeRoute(projectRoot, options) {
|
|
410
|
+
const routeDir = knowledgeRouteDir(projectRoot, options);
|
|
411
|
+
const route = generateKnowledgeRouteTemplate(
|
|
412
|
+
options.knowledge ?? {},
|
|
413
|
+
readKnowledgeRouteArtifact(projectRoot)
|
|
414
|
+
);
|
|
415
|
+
writeRoute(routeDir, "+server.ts", route);
|
|
416
|
+
}
|
|
417
|
+
function readKnowledgeRouteArtifact(projectRoot) {
|
|
418
|
+
for (const relativePath of [
|
|
419
|
+
".smrt/smrt-knowledge.json",
|
|
420
|
+
"dist/smrt-knowledge.json"
|
|
421
|
+
]) {
|
|
422
|
+
const fullPath = join(projectRoot, relativePath);
|
|
423
|
+
if (!existsSync(fullPath)) continue;
|
|
424
|
+
try {
|
|
425
|
+
return JSON.parse(readFileSync(fullPath, "utf-8"));
|
|
426
|
+
} catch {
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
async function generateRegistrationFile(projectRoot, manifest, options) {
|
|
433
|
+
const configPath = options.configPath || "src/lib/server";
|
|
434
|
+
const configDir = join(projectRoot, configPath);
|
|
435
|
+
const registrationFilePath = join(configDir, "smrt-register.ts");
|
|
436
|
+
const localObjects = [];
|
|
437
|
+
const packageObjects = /* @__PURE__ */ new Map();
|
|
438
|
+
for (const [className, objectDef] of Object.entries(manifest.objects)) {
|
|
439
|
+
if (isLocalObject(projectRoot, objectDef)) {
|
|
440
|
+
localObjects.push([className, objectDef]);
|
|
441
|
+
} else if (objectDef.packageName) {
|
|
442
|
+
const packageEntry = packageObjects.get(objectDef.packageName) || {
|
|
443
|
+
classNames: [],
|
|
444
|
+
hasCollectionImport: false
|
|
445
|
+
};
|
|
446
|
+
if (isCollectionClass(objectDef)) {
|
|
447
|
+
packageEntry.hasCollectionImport = true;
|
|
448
|
+
} else {
|
|
449
|
+
packageEntry.classNames.push(className);
|
|
450
|
+
}
|
|
451
|
+
packageObjects.set(objectDef.packageName, packageEntry);
|
|
452
|
+
} else {
|
|
453
|
+
localObjects.push([className, objectDef]);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
const localNamedImports = /* @__PURE__ */ new Map();
|
|
457
|
+
const localSideEffectImports = /* @__PURE__ */ new Set();
|
|
458
|
+
for (const [className, objectDef] of localObjects) {
|
|
459
|
+
const simpleClassName = extractSimpleClassName(className);
|
|
460
|
+
let importPath = "";
|
|
461
|
+
if (objectDef.filePath) {
|
|
462
|
+
const relativeToConfig = relative(configDir, objectDef.filePath);
|
|
463
|
+
const normalized = relativeToConfig.replace(/\\/g, "/").replace(/\.(ts|js|tsx|jsx)$/, "");
|
|
464
|
+
importPath = normalized.startsWith(".") ? normalized : `./${normalized}`;
|
|
465
|
+
} else {
|
|
466
|
+
importPath = getSvelteKitImportPath(
|
|
467
|
+
projectRoot,
|
|
468
|
+
void 0,
|
|
469
|
+
options.objectsDir,
|
|
470
|
+
simpleClassName
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
if (isCollectionClass(objectDef)) {
|
|
474
|
+
localSideEffectImports.add(importPath);
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
const existing = localNamedImports.get(importPath) ?? [];
|
|
478
|
+
existing.push(simpleClassName);
|
|
479
|
+
localNamedImports.set(importPath, existing);
|
|
480
|
+
}
|
|
481
|
+
const localImports = [
|
|
482
|
+
...Array.from(localNamedImports.entries()).sort(([left], [right]) => left.localeCompare(right)).map(([importPath, simpleNames]) => {
|
|
483
|
+
const sortedNames = simpleNames.sort((a, b) => a.localeCompare(b));
|
|
484
|
+
return `import { ${sortedNames.join(", ")} } from '${importPath}';`;
|
|
485
|
+
}),
|
|
486
|
+
...Array.from(localSideEffectImports.values()).sort((a, b) => a.localeCompare(b)).map((importPath) => `import '${importPath}';`)
|
|
487
|
+
].join("\n");
|
|
488
|
+
const packageImports = Array.from(packageObjects.entries()).sort(([left], [right]) => left.localeCompare(right)).flatMap(([packageName, packageEntry]) => {
|
|
489
|
+
const imports2 = [];
|
|
490
|
+
if (packageEntry.hasCollectionImport) {
|
|
491
|
+
imports2.push(`import '${packageName}';`);
|
|
492
|
+
}
|
|
493
|
+
if (packageEntry.classNames.length > 0) {
|
|
494
|
+
const simpleNames = packageEntry.classNames.map(extractSimpleClassName).sort((a, b) => a.localeCompare(b));
|
|
495
|
+
imports2.push(
|
|
496
|
+
`import { ${simpleNames.join(", ")} } from '${packageName}';`
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
return imports2;
|
|
500
|
+
}).join("\n");
|
|
501
|
+
const imports = [packageImports, localImports].filter(Boolean).join("\n");
|
|
502
|
+
const registrations = Object.entries(manifest.objects).map(([className, objectDef]) => {
|
|
503
|
+
if (isCollectionClass(objectDef)) {
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
const simpleClassName = extractSimpleClassName(className);
|
|
507
|
+
const localObject = isLocalObject(projectRoot, objectDef);
|
|
508
|
+
const packageName = getRegistrationPackageName(
|
|
509
|
+
manifest,
|
|
510
|
+
objectDef,
|
|
511
|
+
localObject
|
|
512
|
+
);
|
|
513
|
+
if (!packageName) {
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
const packageNameLiteral = toSingleQuotedStringLiteral(packageName);
|
|
517
|
+
const singleLineRegistration = `ObjectRegistry.register(${simpleClassName}, { name: '${simpleClassName}', packageName: ${packageNameLiteral} });`;
|
|
518
|
+
if (singleLineRegistration.length <= BIOME_LINE_WIDTH) {
|
|
519
|
+
return singleLineRegistration;
|
|
520
|
+
}
|
|
521
|
+
return [
|
|
522
|
+
`ObjectRegistry.register(${simpleClassName}, {`,
|
|
523
|
+
` name: '${simpleClassName}',`,
|
|
524
|
+
` packageName: ${packageNameLiteral},`,
|
|
525
|
+
`});`
|
|
526
|
+
].join("\n");
|
|
527
|
+
}).filter((registration) => registration !== null).join("\n");
|
|
528
|
+
const registrationContent = `/**
|
|
529
|
+
* Auto-generated SMRT object registration
|
|
530
|
+
* DO NOT EDIT - changes will be overwritten
|
|
531
|
+
*
|
|
532
|
+
* Importing these modules triggers their @smrt() decorators, which perform
|
|
533
|
+
* the initial registration. The explicit re-registration below is intentional:
|
|
534
|
+
* it upgrades bundled runtimes to deterministic qualified registrations.
|
|
535
|
+
*/
|
|
536
|
+
|
|
537
|
+
import { ObjectRegistry } from '@happyvertical/smrt-core';
|
|
538
|
+
|
|
539
|
+
${imports}
|
|
540
|
+
|
|
541
|
+
// Re-register imported objects with explicit package names for bundled runtimes
|
|
542
|
+
${registrations}
|
|
543
|
+
`;
|
|
544
|
+
if (!existsSync(configDir)) {
|
|
545
|
+
mkdirSync(configDir, { recursive: true });
|
|
546
|
+
}
|
|
547
|
+
writeFileSync(registrationFilePath, registrationContent, "utf-8");
|
|
548
|
+
console.log(`[smrt] Generated registration file: ${registrationFilePath}`);
|
|
549
|
+
}
|
|
550
|
+
async function generateSmrtConfigFile(projectRoot, manifest, options) {
|
|
551
|
+
const configPath = options.configPath || "src/lib/server";
|
|
552
|
+
const configFileName = options.configFileName || "smrt.ts";
|
|
553
|
+
const configDir = join(projectRoot, configPath);
|
|
554
|
+
const configFilePath = join(configDir, configFileName);
|
|
555
|
+
await generateRegistrationFile(projectRoot, manifest, options);
|
|
556
|
+
if (existsSync(configFilePath)) {
|
|
557
|
+
console.log("[smrt] Config file already exists, skipping generation");
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
const configContent = `/**
|
|
561
|
+
* Centralized SMRT configuration with per-object overrides
|
|
562
|
+
* Generated by @smrt/core vite plugin
|
|
563
|
+
*
|
|
564
|
+
* Most objects will use the default configuration.
|
|
565
|
+
* Add entries to \`objectOverrides\` for objects that need different backends.
|
|
566
|
+
*/
|
|
567
|
+
|
|
568
|
+
// Import SMRT objects to register them via @smrt() decorators
|
|
569
|
+
import './smrt-register.js';
|
|
570
|
+
|
|
571
|
+
import { ObjectRegistry } from '@happyvertical/smrt-core';
|
|
572
|
+
import type { SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
573
|
+
|
|
574
|
+
declare global {
|
|
575
|
+
// eslint-disable-next-line no-var
|
|
576
|
+
var __smrtGetRequestScopedDatabase:
|
|
577
|
+
| (() => SmrtClassOptions['db'] | undefined)
|
|
578
|
+
| undefined;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Per-object configuration overrides
|
|
583
|
+
* Define specific backends for objects that differ from project defaults
|
|
584
|
+
*
|
|
585
|
+
* @example
|
|
586
|
+
* const objectOverrides: Record<string, Partial<SmrtClassOptions>> = {
|
|
587
|
+
* // Analytics uses a separate PostgreSQL database
|
|
588
|
+
* Analytics: {
|
|
589
|
+
* db: {
|
|
590
|
+
* url: process.env.ANALYTICS_DATABASE_URL!,
|
|
591
|
+
* type: 'postgres'
|
|
592
|
+
* }
|
|
593
|
+
* },
|
|
594
|
+
*
|
|
595
|
+
* // AuditLog uses dedicated database with no AI
|
|
596
|
+
* AuditLog: {
|
|
597
|
+
* db: {
|
|
598
|
+
* url: process.env.AUDIT_DATABASE_URL!,
|
|
599
|
+
* type: 'postgres'
|
|
600
|
+
* },
|
|
601
|
+
* ai: undefined
|
|
602
|
+
* },
|
|
603
|
+
*
|
|
604
|
+
* // Cache uses REST adapter (e.g., Redis)
|
|
605
|
+
* Cache: {
|
|
606
|
+
* persistence: {
|
|
607
|
+
* type: 'rest',
|
|
608
|
+
* baseUrl: process.env.REDIS_URL!
|
|
609
|
+
* }
|
|
610
|
+
* }
|
|
611
|
+
* };
|
|
612
|
+
*/
|
|
613
|
+
const objectOverrides: Record<string, Partial<SmrtClassOptions>> = {
|
|
614
|
+
// Add your per-object configuration overrides here
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Default configuration for most SMRT objects
|
|
619
|
+
* Customize this to change project-wide defaults
|
|
620
|
+
*/
|
|
621
|
+
function getDefaultConfig(): SmrtClassOptions {
|
|
622
|
+
return {
|
|
623
|
+
db: {
|
|
624
|
+
url: process.env.DATABASE_URL || ':memory:',
|
|
625
|
+
type: (process.env.DATABASE_TYPE as 'sqlite' | 'postgres') || 'sqlite'
|
|
626
|
+
},
|
|
627
|
+
ai: process.env.OPENAI_API_KEY ? {
|
|
628
|
+
type: 'openai',
|
|
629
|
+
apiKey: process.env.OPENAI_API_KEY
|
|
630
|
+
} : process.env.ANTHROPIC_API_KEY ? {
|
|
631
|
+
type: 'anthropic',
|
|
632
|
+
apiKey: process.env.ANTHROPIC_API_KEY
|
|
633
|
+
} : undefined
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function getRequestScopedDatabase(): SmrtClassOptions['db'] | undefined {
|
|
638
|
+
const getter = globalThis.__smrtGetRequestScopedDatabase;
|
|
639
|
+
return typeof getter === 'function' ? getter() : undefined;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Get configuration for a specific SMRT object
|
|
644
|
+
* Merges project defaults with per-object overrides if defined
|
|
645
|
+
*/
|
|
646
|
+
export function getSmrtConfig(className: string): SmrtClassOptions {
|
|
647
|
+
const defaults = getDefaultConfig();
|
|
648
|
+
const override = objectOverrides[className];
|
|
649
|
+
|
|
650
|
+
if (override) {
|
|
651
|
+
// Deep merge: override specific properties while keeping defaults
|
|
652
|
+
return {
|
|
653
|
+
...defaults,
|
|
654
|
+
...override,
|
|
655
|
+
// Ensure nested objects are merged properly
|
|
656
|
+
db: override.db
|
|
657
|
+
? { ...(defaults.db as any), ...(override.db as any) }
|
|
658
|
+
: defaults.db,
|
|
659
|
+
ai: override.ai !== undefined ? override.ai : defaults.ai
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
return defaults;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Helper to get a collection with centralized configuration
|
|
668
|
+
* Automatically applies project defaults or object-specific overrides
|
|
669
|
+
*/
|
|
670
|
+
export async function getCollection<
|
|
671
|
+
T extends import('@happyvertical/smrt-core').SmrtObject,
|
|
672
|
+
>(className: string, overrides: Partial<SmrtClassOptions> = {}) {
|
|
673
|
+
const config = getSmrtConfig(className);
|
|
674
|
+
const objectOverride = objectOverrides[className];
|
|
675
|
+
const requestScopedDb =
|
|
676
|
+
!overrides.db && !objectOverride?.db
|
|
677
|
+
? getRequestScopedDatabase()
|
|
678
|
+
: undefined;
|
|
679
|
+
|
|
680
|
+
return await ObjectRegistry.getCollection<T>(
|
|
681
|
+
className,
|
|
682
|
+
{
|
|
683
|
+
...config,
|
|
684
|
+
...overrides,
|
|
685
|
+
db: overrides.db
|
|
686
|
+
? { ...(config.db as any), ...(overrides.db as any) }
|
|
687
|
+
: requestScopedDb ?? config.db,
|
|
688
|
+
ai: overrides.ai !== undefined ? overrides.ai : config.ai
|
|
689
|
+
}
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
`;
|
|
693
|
+
if (!existsSync(configDir)) {
|
|
694
|
+
mkdirSync(configDir, { recursive: true });
|
|
695
|
+
}
|
|
696
|
+
writeFileSync(configFilePath, configContent, "utf-8");
|
|
697
|
+
console.log(`[smrt] Generated configuration file: ${configFilePath}`);
|
|
698
|
+
}
|
|
699
|
+
async function generateRoutesForObject(projectRoot, className, objectDef, options) {
|
|
700
|
+
const collectionName = objectDef.collection;
|
|
701
|
+
const routeDir = join(projectRoot, options.routesDir, collectionName);
|
|
702
|
+
const apiConfig = objectDef.decoratorConfig?.api;
|
|
703
|
+
if (apiConfig === false) {
|
|
704
|
+
console.log(`[smrt] Skipping ${className} - API disabled`);
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
const includedActions = resolveStandardCrudActions(apiConfig);
|
|
708
|
+
if (includedActions.includes("list") || includedActions.includes("create")) {
|
|
709
|
+
const collectionRoute = generateCollectionRouteTemplate(
|
|
710
|
+
projectRoot,
|
|
711
|
+
className,
|
|
712
|
+
objectDef,
|
|
713
|
+
includedActions
|
|
714
|
+
);
|
|
715
|
+
writeRoute(routeDir, "+server.ts", collectionRoute);
|
|
716
|
+
}
|
|
717
|
+
if (includedActions.includes("get") || includedActions.includes("update") || includedActions.includes("delete")) {
|
|
718
|
+
const itemRoute = generateItemRouteTemplate(
|
|
719
|
+
projectRoot,
|
|
720
|
+
className,
|
|
721
|
+
objectDef,
|
|
722
|
+
includedActions
|
|
723
|
+
);
|
|
724
|
+
writeRoute(join(routeDir, "[id]"), "+server.ts", itemRoute);
|
|
725
|
+
}
|
|
726
|
+
const customActions = Object.entries(objectDef.methods).filter(
|
|
727
|
+
([name, method]) => !STANDARD_API_ACTIONS.includes(name) && method.isPublic && shouldIncludeInApi(name, apiConfig)
|
|
728
|
+
);
|
|
729
|
+
const actionSpecs = [];
|
|
730
|
+
for (const [actionName, actionDef] of customActions) {
|
|
731
|
+
const routeConfig = resolveApiActionRouteConfig(
|
|
732
|
+
actionName,
|
|
733
|
+
actionDef,
|
|
734
|
+
apiConfig,
|
|
735
|
+
{ kebabRoutes: options.kebabRoutes }
|
|
736
|
+
);
|
|
737
|
+
if (routeConfig.scope === "collection" && !actionDef.isStatic) {
|
|
738
|
+
console.warn(
|
|
739
|
+
`[smrt] Skipping ${className}.${actionName} - collection API routes require a static method`
|
|
740
|
+
);
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
const actionBaseDir = routeConfig.scope === "collection" ? routeDir : join(routeDir, "[id]");
|
|
744
|
+
actionSpecs.push({
|
|
745
|
+
routeDir: join(actionBaseDir, ...routeConfig.pathSegments),
|
|
746
|
+
spec: {
|
|
747
|
+
lookupClassName: className,
|
|
748
|
+
actionName,
|
|
749
|
+
actionDef,
|
|
750
|
+
routeConfig,
|
|
751
|
+
hostType: "item"
|
|
752
|
+
}
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
for (const [actionRouteDir, routeSpecs] of groupCustomActionRoutes(
|
|
756
|
+
actionSpecs
|
|
757
|
+
)) {
|
|
758
|
+
const actionRoute = generateActionRouteTemplate(
|
|
759
|
+
projectRoot,
|
|
760
|
+
routeSpecs,
|
|
761
|
+
objectDef
|
|
762
|
+
);
|
|
763
|
+
writeRoute(actionRouteDir, "+server.ts", actionRoute);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
async function generateCollectionRoutesForObject(projectRoot, className, objectDef, manifest, options) {
|
|
767
|
+
const apiConfig = objectDef.decoratorConfig?.api;
|
|
768
|
+
if (apiConfig === false) {
|
|
769
|
+
console.log(`[smrt] Skipping ${className} - API disabled`);
|
|
770
|
+
return false;
|
|
771
|
+
}
|
|
772
|
+
const routeDir = join(projectRoot, options.routesDir, objectDef.collection);
|
|
773
|
+
const lookupClassName = findItemClassRegistryKey(
|
|
774
|
+
className,
|
|
775
|
+
objectDef,
|
|
776
|
+
manifest
|
|
777
|
+
);
|
|
778
|
+
const customActions = Object.entries(objectDef.methods).filter(
|
|
779
|
+
([name, method]) => !STANDARD_API_ACTIONS.includes(name) && method.isPublic && shouldIncludeInApi(name, apiConfig)
|
|
780
|
+
);
|
|
781
|
+
if (customActions.length === 0) {
|
|
782
|
+
return false;
|
|
783
|
+
}
|
|
784
|
+
let generatedAnyRoutes = false;
|
|
785
|
+
const actionSpecs = [];
|
|
786
|
+
for (const [actionName, actionDef] of customActions) {
|
|
787
|
+
const routeConfig = resolveApiActionRouteConfig(
|
|
788
|
+
actionName,
|
|
789
|
+
actionDef,
|
|
790
|
+
apiConfig,
|
|
791
|
+
{ kebabRoutes: options.kebabRoutes },
|
|
792
|
+
"collection"
|
|
793
|
+
);
|
|
794
|
+
if (routeConfig.scope !== "collection") {
|
|
795
|
+
console.warn(
|
|
796
|
+
`[smrt] Skipping ${className}.${actionName} - collection class methods only support collection-scoped API routes`
|
|
797
|
+
);
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
actionSpecs.push({
|
|
801
|
+
routeDir: join(routeDir, ...routeConfig.pathSegments),
|
|
802
|
+
spec: {
|
|
803
|
+
lookupClassName,
|
|
804
|
+
actionName,
|
|
805
|
+
actionDef,
|
|
806
|
+
routeConfig,
|
|
807
|
+
hostType: "collection"
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
for (const [actionRouteDir, routeSpecs] of groupCustomActionRoutes(
|
|
812
|
+
actionSpecs
|
|
813
|
+
)) {
|
|
814
|
+
const actionRoute = generateActionRouteTemplate(
|
|
815
|
+
projectRoot,
|
|
816
|
+
routeSpecs,
|
|
817
|
+
objectDef
|
|
818
|
+
);
|
|
819
|
+
writeRoute(actionRouteDir, "+server.ts", actionRoute);
|
|
820
|
+
generatedAnyRoutes = true;
|
|
821
|
+
}
|
|
822
|
+
return generatedAnyRoutes;
|
|
823
|
+
}
|
|
824
|
+
function resolveStandardCrudActions(apiConfig) {
|
|
825
|
+
if (apiConfig === false) return [];
|
|
826
|
+
if (apiConfig === true || apiConfig === void 0) {
|
|
827
|
+
return [...STANDARD_API_ACTIONS];
|
|
828
|
+
}
|
|
829
|
+
if (typeof apiConfig !== "object") return [...STANDARD_API_ACTIONS];
|
|
830
|
+
const config = apiConfig;
|
|
831
|
+
let crud = config.include ? config.include.filter((a) => STANDARD_API_ACTIONS.includes(a)) : [...STANDARD_API_ACTIONS];
|
|
832
|
+
if (Array.isArray(config.exclude)) {
|
|
833
|
+
const exclude = config.exclude;
|
|
834
|
+
crud = crud.filter((a) => !exclude.includes(a));
|
|
835
|
+
}
|
|
836
|
+
return crud;
|
|
837
|
+
}
|
|
838
|
+
function shouldIncludeInApi(actionName, apiConfig) {
|
|
839
|
+
if (apiConfig === false) return false;
|
|
840
|
+
if (apiConfig === true || apiConfig === void 0) return true;
|
|
841
|
+
if (typeof apiConfig === "object") {
|
|
842
|
+
const included = apiConfig.include ? apiConfig.include.includes(actionName) : true;
|
|
843
|
+
const excluded = apiConfig.exclude ? apiConfig.exclude.includes(actionName) : false;
|
|
844
|
+
return included && !excluded;
|
|
845
|
+
}
|
|
846
|
+
return true;
|
|
847
|
+
}
|
|
848
|
+
function resolveApiActionSet(objectDef) {
|
|
849
|
+
const apiConfig = objectDef.decoratorConfig?.api;
|
|
850
|
+
if (apiConfig === false) return /* @__PURE__ */ new Set();
|
|
851
|
+
const actions = new Set(resolveStandardCrudActions(apiConfig));
|
|
852
|
+
const objectIsCollectionClass = isCollectionClass(objectDef);
|
|
853
|
+
const config = getApiConfigObject(apiConfig);
|
|
854
|
+
for (const [name, method] of Object.entries(objectDef.methods || {})) {
|
|
855
|
+
if (STANDARD_API_ACTIONS.includes(name)) continue;
|
|
856
|
+
if (!method.isPublic) continue;
|
|
857
|
+
if (!shouldIncludeInApi(name, apiConfig)) continue;
|
|
858
|
+
const routeConfig = config?.routes?.[name];
|
|
859
|
+
const defaultScope = objectIsCollectionClass ? "collection" : method.isStatic ? "collection" : "item";
|
|
860
|
+
const scope = routeConfig?.scope || defaultScope;
|
|
861
|
+
if (objectIsCollectionClass) {
|
|
862
|
+
if (scope !== "collection") continue;
|
|
863
|
+
} else {
|
|
864
|
+
if (scope === "collection" && !method.isStatic) continue;
|
|
865
|
+
}
|
|
866
|
+
actions.add(name);
|
|
867
|
+
}
|
|
868
|
+
return actions;
|
|
869
|
+
}
|
|
870
|
+
function findCliApiCoherenceViolations(manifest) {
|
|
871
|
+
const violations = [];
|
|
872
|
+
for (const [className, objectDef] of Object.entries(manifest.objects)) {
|
|
873
|
+
const cliConfig = objectDef.decoratorConfig?.cli;
|
|
874
|
+
if (!cliConfig || typeof cliConfig !== "object") continue;
|
|
875
|
+
if (cliConfig.skipApiCheck) continue;
|
|
876
|
+
if (!cliConfig.include || cliConfig.include.length === 0) continue;
|
|
877
|
+
const cliExclude = Array.isArray(cliConfig.exclude) ? cliConfig.exclude : [];
|
|
878
|
+
const effectiveCliCommands = cliConfig.include.filter(
|
|
879
|
+
(cmd) => !cliExclude.includes(cmd)
|
|
880
|
+
);
|
|
881
|
+
if (effectiveCliCommands.length === 0) continue;
|
|
882
|
+
const apiActionSet = resolveApiActionSet(objectDef);
|
|
883
|
+
const unreachable = effectiveCliCommands.filter(
|
|
884
|
+
(action) => !apiActionSet.has(action)
|
|
885
|
+
);
|
|
886
|
+
if (unreachable.length > 0) {
|
|
887
|
+
violations.push({ className, unreachable });
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return violations;
|
|
891
|
+
}
|
|
892
|
+
function validateCliIncludeAgainstApi(manifest) {
|
|
893
|
+
const violations = findCliApiCoherenceViolations(manifest);
|
|
894
|
+
if (violations.length === 0) return;
|
|
895
|
+
const messages = violations.flatMap(
|
|
896
|
+
({ className, unreachable }) => unreachable.map(
|
|
897
|
+
(action) => `[smrt] ${className}.${action} is declared in cli.include but is not exposed via the api.
|
|
898
|
+
Either:
|
|
899
|
+
- Add '${action}' to api.include, or
|
|
900
|
+
- Remove '${action}' from cli.include.
|
|
901
|
+
The CLI invokes methods over HTTP; methods without API routes are unreachable.
|
|
902
|
+
If this CLI is intentionally invoked in-process (no HTTP), set
|
|
903
|
+
\`cli: { skipApiCheck: true }\` on the @smrt() decorator to acknowledge.`
|
|
904
|
+
)
|
|
905
|
+
);
|
|
906
|
+
throw new Error(messages.join("\n\n"));
|
|
907
|
+
}
|
|
908
|
+
function writeRoute(dir, filename, content) {
|
|
909
|
+
try {
|
|
910
|
+
if (!existsSync(dir)) {
|
|
911
|
+
mkdirSync(dir, { recursive: true });
|
|
912
|
+
}
|
|
913
|
+
const filePath = join(dir, filename);
|
|
914
|
+
writeFileSync(filePath, content, "utf-8");
|
|
915
|
+
console.log(`[smrt] Generated: ${filePath}`);
|
|
916
|
+
} catch (error) {
|
|
917
|
+
console.error(`[smrt] [ERROR] Failed to write route file:`);
|
|
918
|
+
console.error(`[smrt] [ERROR] Directory: ${dir}`);
|
|
919
|
+
console.error(`[smrt] [ERROR] Filename: ${filename}`);
|
|
920
|
+
console.error(`[smrt] [ERROR] Error:`, error);
|
|
921
|
+
throw error;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
function getSvelteKitImportPath(projectRoot, objectFilePath, objectsDir, className) {
|
|
925
|
+
const filePath = join(projectRoot, objectsDir, `${className || "Object"}.ts`);
|
|
926
|
+
const absoluteObjectsDir = objectsDir.startsWith("/") ? objectsDir : join(projectRoot, objectsDir);
|
|
927
|
+
const relativePath = relative(absoluteObjectsDir, filePath);
|
|
928
|
+
const normalizedPath = relativePath.replace(/\\/g, "/");
|
|
929
|
+
const withoutExtension = normalizedPath.replace(/\.(ts|js|tsx|jsx)$/, "");
|
|
930
|
+
if (objectsDir.includes("src/lib")) {
|
|
931
|
+
const libSubpath = objectsDir.split("src/lib")[1] || "";
|
|
932
|
+
const fullPath = libSubpath ? `${libSubpath}/${withoutExtension}` : withoutExtension;
|
|
933
|
+
return `$lib${fullPath}`.replace(/\/+/g, "/");
|
|
934
|
+
}
|
|
935
|
+
return withoutExtension.startsWith(".") ? withoutExtension : `./${withoutExtension}`;
|
|
936
|
+
}
|
|
937
|
+
function isLocalObject(projectRoot, objectDef) {
|
|
938
|
+
if (!objectDef.filePath) return false;
|
|
939
|
+
return objectDef.filePath.startsWith(projectRoot) && !objectDef.filePath.includes("/node_modules/");
|
|
940
|
+
}
|
|
941
|
+
function generateCollectionRouteTemplate(_projectRoot, className, objectDef, includedActions, _options) {
|
|
942
|
+
const hasGet = includedActions.includes("list");
|
|
943
|
+
const hasPost = includedActions.includes("create");
|
|
944
|
+
const serializers = resolveStandardRouteSerializers(
|
|
945
|
+
objectDef.decoratorConfig?.api
|
|
946
|
+
);
|
|
947
|
+
const serializerImports = serializers.importStatements.join("\n");
|
|
948
|
+
const imports = `${AUTO_GENERATED_ROUTE_HEADER}
|
|
949
|
+
// DO NOT EDIT - changes will be overwritten
|
|
950
|
+
|
|
951
|
+
import { error, json } from '@sveltejs/kit';
|
|
952
|
+
${serializerImports ? `${serializerImports}
|
|
953
|
+
` : ""}import { getCollection } from '$lib/server/smrt';
|
|
954
|
+
import type { RequestHandler } from './$types';
|
|
955
|
+
// Note: ${className} is auto-registered by the Vite plugin scanner
|
|
956
|
+
${generateAuthGuardHelper(objectDef)}${isTenantScoped(objectDef) ? generateTenantContextHelper() : ""}${hasPost ? generateWritablePolicyHelper(objectDef) : ""}`;
|
|
957
|
+
const getHandler = hasGet ? `
|
|
958
|
+
// List all ${className.toLowerCase()}s
|
|
959
|
+
export const GET: RequestHandler = async ({ locals, url }) => {
|
|
960
|
+
${routeGuardPreamble(objectDef, false)}
|
|
961
|
+
const limit = Number(url.searchParams.get('limit')) || 50;
|
|
962
|
+
const offset = Number(url.searchParams.get('offset')) || 0;
|
|
963
|
+
|
|
964
|
+
${generateCollectionLoad(className)}
|
|
965
|
+
const items = await collection.list({ limit, offset });
|
|
966
|
+
const count = await collection.count();
|
|
967
|
+
${serializers.listItemSerializerName ? `
|
|
968
|
+
const serializedItems = await Promise.all(
|
|
969
|
+
items.map((item) => ${serializers.listItemSerializerName}(item)),
|
|
970
|
+
);
|
|
971
|
+
|
|
972
|
+
return json({ items: serializedItems, count, limit, offset });` : `
|
|
973
|
+
const items_public = items.map((item) => item.toPublicJSON());
|
|
974
|
+
return json({ items: items_public, count, limit, offset });`}
|
|
975
|
+
};
|
|
976
|
+
` : "";
|
|
977
|
+
const postHandler = hasPost ? `
|
|
978
|
+
// Create new ${className.toLowerCase()}
|
|
979
|
+
export const POST: RequestHandler = async ({ locals, request }) => {
|
|
980
|
+
${routeGuardPreamble(objectDef, true)}
|
|
981
|
+
const data = applyWritablePolicy(await request.json());
|
|
982
|
+
|
|
983
|
+
${generateCollectionLoad(className)}
|
|
984
|
+
const item = await collection.create(data);
|
|
985
|
+
await item.save();
|
|
986
|
+
${serializers.itemSerializerName ? `
|
|
987
|
+
const serializedItem = await ${serializers.itemSerializerName}(item);
|
|
988
|
+
|
|
989
|
+
return json(serializedItem, { status: 201 });` : `
|
|
990
|
+
return json(item.toPublicJSON(), { status: 201 });`}
|
|
991
|
+
};
|
|
992
|
+
` : "";
|
|
993
|
+
return imports + getHandler + postHandler;
|
|
994
|
+
}
|
|
995
|
+
function generateCollectionLoad(className, options = {}) {
|
|
996
|
+
const genericSuffix = options.generic ? "<any>" : "";
|
|
997
|
+
return [
|
|
998
|
+
` const collection = await getCollection${genericSuffix}(`,
|
|
999
|
+
` '${className}',`,
|
|
1000
|
+
" );"
|
|
1001
|
+
].join("\n");
|
|
1002
|
+
}
|
|
1003
|
+
function generateNotFoundError(className) {
|
|
1004
|
+
const singleLineNotFoundError = ` if (!item) throw error(404, '${className} not found');`;
|
|
1005
|
+
if (singleLineNotFoundError.length <= 100) {
|
|
1006
|
+
return singleLineNotFoundError;
|
|
1007
|
+
}
|
|
1008
|
+
return [
|
|
1009
|
+
" if (!item)",
|
|
1010
|
+
" throw error(",
|
|
1011
|
+
" 404,",
|
|
1012
|
+
` '${className} not found',`,
|
|
1013
|
+
" );"
|
|
1014
|
+
].join("\n");
|
|
1015
|
+
}
|
|
1016
|
+
function generateCollectionNotRegisteredError(className) {
|
|
1017
|
+
return [
|
|
1018
|
+
" if (!collection)",
|
|
1019
|
+
" throw error(",
|
|
1020
|
+
" 500,",
|
|
1021
|
+
` '${className} collection is not registered',`,
|
|
1022
|
+
" );"
|
|
1023
|
+
].join("\n");
|
|
1024
|
+
}
|
|
1025
|
+
function generateClassNotRegisteredError(className) {
|
|
1026
|
+
return [
|
|
1027
|
+
" if (!registered)",
|
|
1028
|
+
" throw error(",
|
|
1029
|
+
" 500,",
|
|
1030
|
+
` '${className} is not registered',`,
|
|
1031
|
+
" );"
|
|
1032
|
+
].join("\n");
|
|
1033
|
+
}
|
|
1034
|
+
function generateItemRouteTemplate(_projectRoot, className, objectDef, includedActions, _options) {
|
|
1035
|
+
const hasGet = includedActions.includes("get");
|
|
1036
|
+
const hasPut = includedActions.includes("update");
|
|
1037
|
+
const hasDelete = includedActions.includes("delete");
|
|
1038
|
+
const simpleClassName = extractSimpleClassName(className);
|
|
1039
|
+
const serializers = resolveStandardRouteSerializers(
|
|
1040
|
+
objectDef.decoratorConfig?.api
|
|
1041
|
+
);
|
|
1042
|
+
const serializerImports = serializers.importStatements.join("\n");
|
|
1043
|
+
const imports = `${AUTO_GENERATED_ROUTE_HEADER}
|
|
1044
|
+
// DO NOT EDIT - changes will be overwritten
|
|
1045
|
+
|
|
1046
|
+
import { error, json } from '@sveltejs/kit';
|
|
1047
|
+
${serializerImports ? `${serializerImports}
|
|
1048
|
+
` : ""}import { getCollection } from '$lib/server/smrt';
|
|
1049
|
+
import type { RequestHandler } from './$types';
|
|
1050
|
+
${generateAuthGuardHelper(objectDef)}${isTenantScoped(objectDef) ? generateTenantContextHelper() : ""}${hasPut ? generateWritablePolicyHelper(objectDef) : ""}`;
|
|
1051
|
+
const getHandler = hasGet ? `
|
|
1052
|
+
// Get single ${simpleClassName.toLowerCase()}
|
|
1053
|
+
export const GET: RequestHandler = async ({ locals, params }) => {
|
|
1054
|
+
${routeGuardPreamble(objectDef, false)}
|
|
1055
|
+
${generateCollectionLoad(className, { generic: true })}
|
|
1056
|
+
const item = await collection.get(params.id);
|
|
1057
|
+
${generateNotFoundError(className)}
|
|
1058
|
+
${serializers.itemSerializerName ? `
|
|
1059
|
+
const serializedItem = await ${serializers.itemSerializerName}(item);
|
|
1060
|
+
|
|
1061
|
+
return json(serializedItem);` : `
|
|
1062
|
+
return json(item.toPublicJSON());`}
|
|
1063
|
+
};
|
|
1064
|
+
` : "";
|
|
1065
|
+
const putHandler = hasPut ? `
|
|
1066
|
+
// Update ${simpleClassName.toLowerCase()}
|
|
1067
|
+
export const PUT: RequestHandler = async ({ locals, params, request }) => {
|
|
1068
|
+
${routeGuardPreamble(objectDef, true)}
|
|
1069
|
+
${generateCollectionLoad(className, { generic: true })}
|
|
1070
|
+
const item = await collection.get(params.id);
|
|
1071
|
+
${generateNotFoundError(className)}
|
|
1072
|
+
|
|
1073
|
+
const data = applyWritablePolicy(await request.json());
|
|
1074
|
+
Object.assign(item, data);
|
|
1075
|
+
await item.save();
|
|
1076
|
+
${serializers.itemSerializerName ? `
|
|
1077
|
+
const serializedItem = await ${serializers.itemSerializerName}(item);
|
|
1078
|
+
|
|
1079
|
+
return json(serializedItem);` : `
|
|
1080
|
+
return json(item.toPublicJSON());`}
|
|
1081
|
+
};
|
|
1082
|
+
` : "";
|
|
1083
|
+
const deleteHandler = hasDelete ? `
|
|
1084
|
+
// Delete ${simpleClassName.toLowerCase()}
|
|
1085
|
+
export const DELETE: RequestHandler = async ({ locals, params }) => {
|
|
1086
|
+
${routeGuardPreamble(objectDef, true)}
|
|
1087
|
+
${generateCollectionLoad(className, { generic: true })}
|
|
1088
|
+
const item = await collection.get(params.id);
|
|
1089
|
+
${generateNotFoundError(className)}
|
|
1090
|
+
|
|
1091
|
+
await item.delete();
|
|
1092
|
+
return json({ success: true });
|
|
1093
|
+
};
|
|
1094
|
+
` : "";
|
|
1095
|
+
return imports + getHandler + putHandler + deleteHandler;
|
|
1096
|
+
}
|
|
1097
|
+
function generateActionRouteTemplate(_projectRoot, routeSpecs, objectDef, _options) {
|
|
1098
|
+
if (routeSpecs.length === 0) {
|
|
1099
|
+
throw new Error("Cannot generate a custom action route without handlers");
|
|
1100
|
+
}
|
|
1101
|
+
const [firstSpec] = routeSpecs;
|
|
1102
|
+
const { lookupClassName, routeConfig, hostType } = firstSpec;
|
|
1103
|
+
const hasMixedHosts = routeSpecs.some(
|
|
1104
|
+
(spec) => spec.hostType !== hostType || spec.lookupClassName !== lookupClassName || spec.routeConfig.scope !== routeConfig.scope
|
|
1105
|
+
);
|
|
1106
|
+
if (hasMixedHosts) {
|
|
1107
|
+
throw new Error(
|
|
1108
|
+
`Cannot generate mixed custom route handlers for ${lookupClassName}. All handlers sharing a route path must target the same host type and scope.`
|
|
1109
|
+
);
|
|
1110
|
+
}
|
|
1111
|
+
const importBlock = hostType === "collection" ? `import { error, json } from '@sveltejs/kit';
|
|
1112
|
+
import { getCollection } from '$lib/server/smrt';
|
|
1113
|
+
import type { RequestHandler } from './$types';` : routeConfig.scope === "collection" ? `import { error, json } from '@sveltejs/kit';
|
|
1114
|
+
import { ObjectRegistry } from '@happyvertical/smrt-core';
|
|
1115
|
+
import type { RequestHandler } from './$types';` : `import { error, json } from '@sveltejs/kit';
|
|
1116
|
+
import { getCollection } from '$lib/server/smrt';
|
|
1117
|
+
import type { RequestHandler } from './$types';`;
|
|
1118
|
+
const tenantScoped = isTenantScoped(objectDef);
|
|
1119
|
+
const handlers = routeSpecs.map(
|
|
1120
|
+
(spec) => generateActionRouteHandler(
|
|
1121
|
+
spec.lookupClassName,
|
|
1122
|
+
spec.actionName,
|
|
1123
|
+
spec.actionDef,
|
|
1124
|
+
spec.routeConfig,
|
|
1125
|
+
spec.hostType,
|
|
1126
|
+
tenantScoped
|
|
1127
|
+
)
|
|
1128
|
+
).join("\n");
|
|
1129
|
+
return `${AUTO_GENERATED_ROUTE_HEADER}
|
|
1130
|
+
// DO NOT EDIT - changes will be overwritten
|
|
1131
|
+
|
|
1132
|
+
${importBlock}
|
|
1133
|
+
${generateAuthGuardHelper(objectDef)}${tenantScoped ? generateTenantContextHelper() : ""}
|
|
1134
|
+
${handlers}`;
|
|
1135
|
+
}
|
|
1136
|
+
function generateActionRouteHandler(lookupClassName, actionName, actionDef, routeConfig, hostType, tenantScoped) {
|
|
1137
|
+
const handlerName = routeConfig.method;
|
|
1138
|
+
const guardLines = [
|
|
1139
|
+
` requireRouteAuth(locals, ${routeConfig.method !== "GET"});`
|
|
1140
|
+
];
|
|
1141
|
+
if (tenantScoped) {
|
|
1142
|
+
guardLines.push(" establishTenantContext(locals);");
|
|
1143
|
+
}
|
|
1144
|
+
const authGuardLine = guardLines.join("\n");
|
|
1145
|
+
const hasInput = actionDef.parameters.length > 0;
|
|
1146
|
+
const needsRequest = hasInput;
|
|
1147
|
+
const invocationArgs = buildActionInvocationArgs(actionDef);
|
|
1148
|
+
const hasPathParams = routeConfig.pathParamNames.length > 0;
|
|
1149
|
+
const pathParamsObjectLiteral = buildPathParamsObjectLiteral(
|
|
1150
|
+
routeConfig.pathParamNames
|
|
1151
|
+
);
|
|
1152
|
+
const optionsLoad = hasInput && routeConfig.method === "GET" ? hasPathParams ? [
|
|
1153
|
+
` const pathParams = ${pathParamsObjectLiteral};`,
|
|
1154
|
+
" const options = {",
|
|
1155
|
+
" ...Object.fromEntries(new URL(request.url).searchParams.entries()),",
|
|
1156
|
+
" ...pathParams,",
|
|
1157
|
+
" };",
|
|
1158
|
+
""
|
|
1159
|
+
].join("\n") : [
|
|
1160
|
+
" const options = Object.fromEntries(",
|
|
1161
|
+
" new URL(request.url).searchParams.entries(),",
|
|
1162
|
+
" );",
|
|
1163
|
+
""
|
|
1164
|
+
].join("\n") : hasInput ? hasPathParams ? [
|
|
1165
|
+
` const pathParams = ${pathParamsObjectLiteral};`,
|
|
1166
|
+
" const options = {",
|
|
1167
|
+
" ...(await request.json()),",
|
|
1168
|
+
" ...pathParams,",
|
|
1169
|
+
" };",
|
|
1170
|
+
""
|
|
1171
|
+
].join("\n") : " const options = await request.json();\n" : "";
|
|
1172
|
+
const collectionHandlerArgs = buildRouteHandlerArgs(
|
|
1173
|
+
hasPathParams,
|
|
1174
|
+
needsRequest
|
|
1175
|
+
);
|
|
1176
|
+
const itemHandlerArgs = buildRouteHandlerArgs(true, needsRequest);
|
|
1177
|
+
if (hostType === "collection") {
|
|
1178
|
+
return `// Custom collection method: ${actionName}
|
|
1179
|
+
export const ${handlerName}: RequestHandler = async (${collectionHandlerArgs}) => {
|
|
1180
|
+
${authGuardLine}
|
|
1181
|
+
${generateCollectionLoad(lookupClassName, { generic: true })}
|
|
1182
|
+
const typedCollection = collection as any;
|
|
1183
|
+
${generateCollectionNotRegisteredError(lookupClassName)}
|
|
1184
|
+
|
|
1185
|
+
${optionsLoad} const result = ${buildActionInvocationExpression(
|
|
1186
|
+
actionDef.isStatic ? "(typedCollection.constructor as any)" : "typedCollection",
|
|
1187
|
+
actionName,
|
|
1188
|
+
invocationArgs
|
|
1189
|
+
)};
|
|
1190
|
+
|
|
1191
|
+
return json({ action: '${actionName}', result: toPublicResult(result) });
|
|
1192
|
+
};
|
|
1193
|
+
`;
|
|
1194
|
+
}
|
|
1195
|
+
if (routeConfig.scope === "collection") {
|
|
1196
|
+
return `// Custom collection action: ${actionName}
|
|
1197
|
+
export const ${handlerName}: RequestHandler = async (${collectionHandlerArgs}) => {
|
|
1198
|
+
${authGuardLine}
|
|
1199
|
+
const registered = ObjectRegistry.getClass('${lookupClassName}');
|
|
1200
|
+
${generateClassNotRegisteredError(lookupClassName)}
|
|
1201
|
+
|
|
1202
|
+
${optionsLoad} const ClassRef = registered.constructor as any;
|
|
1203
|
+
const result = ${buildActionInvocationExpression(
|
|
1204
|
+
"ClassRef",
|
|
1205
|
+
actionName,
|
|
1206
|
+
invocationArgs
|
|
1207
|
+
)};
|
|
1208
|
+
|
|
1209
|
+
return json({ action: '${actionName}', result: toPublicResult(result) });
|
|
1210
|
+
};
|
|
1211
|
+
`;
|
|
1212
|
+
}
|
|
1213
|
+
return `// Custom action: ${actionName}
|
|
1214
|
+
export const ${handlerName}: RequestHandler = async (${itemHandlerArgs}) => {
|
|
1215
|
+
${authGuardLine}
|
|
1216
|
+
${generateCollectionLoad(lookupClassName, { generic: true })}
|
|
1217
|
+
const item = await collection.get(params.id);
|
|
1218
|
+
${generateNotFoundError(lookupClassName)}
|
|
1219
|
+
|
|
1220
|
+
${optionsLoad} const result = ${buildActionInvocationExpression(
|
|
1221
|
+
"item",
|
|
1222
|
+
actionName,
|
|
1223
|
+
invocationArgs
|
|
1224
|
+
)};
|
|
1225
|
+
|
|
1226
|
+
return json({ action: '${actionName}', result: toPublicResult(result) });
|
|
1227
|
+
};
|
|
1228
|
+
`;
|
|
1229
|
+
}
|
|
1230
|
+
function generateKnowledgeRouteTemplate(knowledge, artifact) {
|
|
1231
|
+
const api = knowledge.api ?? {};
|
|
1232
|
+
const includeDocs = api.includeDocs === true;
|
|
1233
|
+
const includePrompts = api.includePrompts === true;
|
|
1234
|
+
const requireAdmin = api.requireAdmin !== false;
|
|
1235
|
+
const artifactLiteral = artifact ? JSON.stringify(artifact) : "null";
|
|
1236
|
+
return `${AUTO_GENERATED_ROUTE_HEADER}
|
|
1237
|
+
// DO NOT EDIT - changes will be overwritten
|
|
1238
|
+
|
|
1239
|
+
import { dev } from '$app/environment';
|
|
1240
|
+
import { error, json } from '@sveltejs/kit';
|
|
1241
|
+
import type { RequestHandler } from './$types';
|
|
1242
|
+
|
|
1243
|
+
const INCLUDE_DOCS_BY_DEFAULT = ${JSON.stringify(includeDocs)};
|
|
1244
|
+
const INCLUDE_PROMPTS_BY_DEFAULT = ${JSON.stringify(includePrompts)};
|
|
1245
|
+
const REQUIRE_ADMIN = ${JSON.stringify(requireAdmin)};
|
|
1246
|
+
const KNOWLEDGE_ARTIFACT: Record<string, any> | null = ${artifactLiteral};
|
|
1247
|
+
|
|
1248
|
+
if (!dev && !REQUIRE_ADMIN) {
|
|
1249
|
+
console.warn('[smrt] PUBLIC knowledge API route enabled; unauthenticated responses are sanitized.');
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
export const GET: RequestHandler = async ({ locals, url, setHeaders }) => {
|
|
1253
|
+
setHeaders({ 'cache-control': 'private, no-store' });
|
|
1254
|
+
|
|
1255
|
+
if (!dev && REQUIRE_ADMIN && !isKnowledgeAdmin(locals as Record<string, any>)) {
|
|
1256
|
+
throw error(403, 'SMRT knowledge requires dev mode or admin access');
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
const artifact = readKnowledgeArtifact();
|
|
1260
|
+
const includeDocs = queryBoolean(url, 'includeDocs', INCLUDE_DOCS_BY_DEFAULT);
|
|
1261
|
+
const includePrompts = queryBoolean(
|
|
1262
|
+
url,
|
|
1263
|
+
'includePrompts',
|
|
1264
|
+
INCLUDE_PROMPTS_BY_DEFAULT,
|
|
1265
|
+
);
|
|
1266
|
+
|
|
1267
|
+
return json(sanitizeKnowledgeArtifact(artifact, {
|
|
1268
|
+
includeDocs,
|
|
1269
|
+
includePrompts,
|
|
1270
|
+
publicAccess: !REQUIRE_ADMIN,
|
|
1271
|
+
}));
|
|
1272
|
+
};
|
|
1273
|
+
|
|
1274
|
+
function readKnowledgeArtifact(): Record<string, any> {
|
|
1275
|
+
if (!KNOWLEDGE_ARTIFACT) throw error(404, 'SMRT knowledge artifact not found');
|
|
1276
|
+
return KNOWLEDGE_ARTIFACT;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
function queryBoolean(url: URL, name: string, defaultValue: boolean): boolean {
|
|
1280
|
+
const value = url.searchParams.get(name);
|
|
1281
|
+
if (value === null) return defaultValue;
|
|
1282
|
+
return value === 'true';
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
function sanitizeKnowledgeArtifact(
|
|
1286
|
+
artifact: Record<string, any>,
|
|
1287
|
+
options: { includeDocs: boolean; includePrompts: boolean; publicAccess: boolean },
|
|
1288
|
+
): Record<string, any> {
|
|
1289
|
+
const sanitized = { ...artifact };
|
|
1290
|
+
|
|
1291
|
+
if (!options.includeDocs) {
|
|
1292
|
+
delete sanitized.agentDoc;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
if (!options.includePrompts) {
|
|
1296
|
+
sanitized.prompts = [];
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
if (options.publicAccess) {
|
|
1300
|
+
delete sanitized.dependencies;
|
|
1301
|
+
delete sanitized.sourceHashes;
|
|
1302
|
+
delete sanitized.sourceManifestPath;
|
|
1303
|
+
delete sanitized.agentDocPath;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
return sanitized;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
function isKnowledgeAdmin(locals: Record<string, any>): boolean {
|
|
1310
|
+
if (locals.smrtKnowledgeAdmin === true || locals.smrtAdmin === true) {
|
|
1311
|
+
return true;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
const userRoles = rolesFrom(locals.user);
|
|
1315
|
+
const sessionRoles = rolesFrom(locals.session?.user);
|
|
1316
|
+
return [...userRoles, ...sessionRoles].some((role) =>
|
|
1317
|
+
['admin', 'owner', 'superadmin'].includes(role),
|
|
1318
|
+
);
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
function rolesFrom(value: any): string[] {
|
|
1322
|
+
if (!value || typeof value !== 'object') return [];
|
|
1323
|
+
const roles = Array.isArray(value.roles) ? value.roles : [value.role];
|
|
1324
|
+
return roles
|
|
1325
|
+
.filter((role): role is string => typeof role === 'string')
|
|
1326
|
+
.map((role) => role.toLowerCase());
|
|
1327
|
+
}
|
|
1328
|
+
`;
|
|
1329
|
+
}
|
|
1330
|
+
function updateGitignore(projectRoot, options) {
|
|
1331
|
+
const gitignorePath = join(projectRoot, ".gitignore");
|
|
1332
|
+
const patternsToAdd = [
|
|
1333
|
+
"# SMRT auto-generated routes (from Vite plugin)",
|
|
1334
|
+
`${options.routesDir}/**/+server.ts`
|
|
1335
|
+
];
|
|
1336
|
+
if (options.knowledge?.api?.enabled) {
|
|
1337
|
+
const knowledgeRoute = relative(
|
|
1338
|
+
projectRoot,
|
|
1339
|
+
join(knowledgeRouteDir(projectRoot, options), "+server.ts")
|
|
1340
|
+
).replaceAll("\\", "/");
|
|
1341
|
+
patternsToAdd.push(knowledgeRoute);
|
|
1342
|
+
}
|
|
1343
|
+
let gitignoreContent = "";
|
|
1344
|
+
if (existsSync(gitignorePath)) {
|
|
1345
|
+
gitignoreContent = readFileSync(gitignorePath, "utf-8");
|
|
1346
|
+
}
|
|
1347
|
+
let needsUpdate = false;
|
|
1348
|
+
const linesToAdd = [];
|
|
1349
|
+
for (const pattern of patternsToAdd) {
|
|
1350
|
+
if (!gitignoreContent.includes(pattern)) {
|
|
1351
|
+
linesToAdd.push(pattern);
|
|
1352
|
+
needsUpdate = true;
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
if (needsUpdate) {
|
|
1356
|
+
if (gitignoreContent.length > 0 && !gitignoreContent.endsWith("\n")) {
|
|
1357
|
+
gitignoreContent += "\n";
|
|
1358
|
+
}
|
|
1359
|
+
if (gitignoreContent.length > 0 && !gitignoreContent.endsWith("\n\n")) {
|
|
1360
|
+
gitignoreContent += "\n";
|
|
1361
|
+
}
|
|
1362
|
+
gitignoreContent += `${linesToAdd.join("\n")}
|
|
1363
|
+
`;
|
|
1364
|
+
writeFileSync(gitignorePath, gitignoreContent, "utf-8");
|
|
1365
|
+
console.log("[smrt] Updated .gitignore with generated route patterns");
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
export {
|
|
1369
|
+
findCliApiCoherenceViolations,
|
|
1370
|
+
generateSvelteKitRoutes,
|
|
1371
|
+
methodNameToKebab,
|
|
1372
|
+
resolveApiActionSet,
|
|
1373
|
+
validateCliIncludeAgainstApi
|
|
1374
|
+
};
|
|
1375
|
+
//# sourceMappingURL=sveltekit-generator.js.map
|