@happyvertical/smrt-core 0.57.41 → 0.57.43

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.
@@ -2,7 +2,7 @@
2
2
  "version": "1.0.0",
3
3
  "timestamp": 0,
4
4
  "packageName": "@happyvertical/smrt-core",
5
- "packageVersion": "0.57.41",
5
+ "packageVersion": "0.57.43",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-core:SmrtClass": {
8
8
  "name": "smrtclass",
@@ -3,12 +3,12 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-core",
6
- "packageVersion": "0.57.41",
6
+ "packageVersion": "0.57.43",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "08af712d8ae12cc79cc7477ee57b01f334f3f966eb57a2edad1f8337b7988990",
11
- "packageJson": "7c6ca8667414e89a0299c698847994c82ff2327c8db247e9cf6aa33c75a67abe",
10
+ "manifest": "f5a4cd410aea2fbc8c20ad06f4923c650df41872c4fcb099a6a0c04c334ac397",
11
+ "packageJson": "274cf98adf4bfcf4dd89d99124c337f5f324dffcb743a6df2af05e35c3c3a8b9",
12
12
  "agents": "952dd7df7461bd7657b346f3ea63f1d240a625863b6ceef7b0f6d4960f978068",
13
13
  "moduleDoc:agents/object-runtime.md": "fd2748649e820c2fe5960fa4a44c5f3e2a5afa57da89925e8b4cb3b53d67a07b",
14
14
  "moduleDoc:agents/revision-guard.md": "f6ea32a6860964c98aba14b93587f40b33c8f7d0deccb2c54d21bff45db3da42",
@@ -20,7 +20,7 @@
20
20
  "moduleDoc:agents/change-feed.md": "c3d6206758dd1d2e0475177e1938fb7d7b6cfe8a6c4d3c3b56ef62bc7bc7739d",
21
21
  "moduleDoc:agents/change-signals.md": "a2dbe7077483b5a19bd5ea9589b5a08fe847377c8e05d01d57f813c6d9cbc28b",
22
22
  "moduleDoc:agents/generators.md": "abf359fdc6fa7f0afaf4a096d52b75982084b80d539a4ed021d3d95b47714b14",
23
- "moduleDoc:agents/build-knowledge.md": "5c3a24930403f870a252e9a53699191d7e38a52373ec52f9f2b862445892a3d1",
23
+ "moduleDoc:agents/build-knowledge.md": "20be3efe6f885f33ee56db313cc3c9b342022f0e4031a9d4d38a633f0c1041be",
24
24
  "moduleDoc:agents/memory.md": "a86bdf370403d08ecad3be86e341eb322dfe6e6b7a4e8806a6f8be9bf3421da5",
25
25
  "moduleDoc:agents/system-diagnostics.md": "b7ccae18ee312bf788d5470f95fa78d3535f1789247d5372e40ca86538290772",
26
26
  "moduleDoc:agents/registry-snapshot.md": "6234aa540427396d1dda1825a4641b1072e247187d5ddf413ae46c691344ee30",
@@ -1203,7 +1203,7 @@
1203
1203
  {
1204
1204
  "path": "agents/build-knowledge.md",
1205
1205
  "module": "build-knowledge",
1206
- "content": "# Decorators, build integration, and knowledge\n\nKey options: `tableName`, `tableStrategy` ('cti'|'sti'), `conflictColumns`, `indexes` (declared multi-column indexes; see [schema-paths.md](schema-paths.md)), `api`/`mcp`/`cli` (generation config), `ai` (callable methods), `hooks` (beforeSave/afterSave/beforeDelete/afterDelete), `embeddings` (auto-generate), `tenantScoped`, `agent`, `ui` (`{ icon, label, description }` — nav/help hints round-tripped through the manifest as plain data; `description` is the object-level seed for form-level help, #2046).\n\nRegistration sets `SMRT_TABLE_NAME` static property (survives minification).\n\nAn explicit `@smrt({ collection: 'domain.records' })` names the route/permission\nnamespace independently of `tableName`, including standalone non-STI models.\nScanner output preserves it in both `collection` and `decoratorConfig.collection`.\nGenerated registration reads the merged declaration, so older manifests with a\nderived top-level collection still agree with native decorators. Explicit runtime\nregistration options win; absent declarations retain manifest/STI/default naming.\n\n## @field() UI hints (#2046)\n\n`@field({ ui: { basic, group, order, locked } })` — a static, presentation-only\nseed for the field-policy rail (epic #2045). Carried in the manifest under the\nfield's `_meta.ui` (never a top-level `FieldDefinition` key), readable at\nruntime via `getAllFields()` at `field._meta.ui`, and emitted (sanitized) with\n`description` into generated web-collection definitions and browser MCP tool\nschemas. No schema/persistence/security effect — `sensitive`/`readPermission`\nstay the security rail, and `sensitive`/`transient` fields never emit to the\nclient at all.\n\n`ui.widget` (#3599) adds a presentation widget hint:\n`'textarea' | 'currency' | 'email' | 'url' | 'phone'`. It rides the same\n`_meta.ui` channel and is also emitted (sanitized) into web-collection\ndefinitions. The manifest generator validates it against the field type and\nfails the build otherwise: every widget needs a `text` field (`currency` is a\ncurrency-CODE picker for an ISO 4217 string such as `currency = 'USD'`, not a\nmoney amount; amounts are integer minor units and take no widget); an unknown\nvalue or a non-text field is an error. `ui.widget` is a hint only — it never changes\nthe column type or validation.\n\n`ui.format` (#3718) says how a value is presented: `money` (an `integer` of\nminor units — the amount hint record pages format and total), `date` (a\nbusiness date on a `datetime`, shown without a time), `datetime`, `percent`\n(a rate on a `decimal`/`integer`). Same channel, same emission, same build-time\ncheck against the field type. `@foreignKey(Target, { role: 'items' })` (or on\n`@crossPackageRef`) marks the declaring model as the target's line items; the\nscanner keeps it at `_meta.role`, the build rejects it on a non-reference or\nan unknown value, and the generated web relationship edge carries\n`role: 'items'` (edges without a role stay byte-stable). Both are hints: no\nschema, persistence or security effect.\n\n## Presentation metadata (#3599)\n\nPure helpers live in `src/ui-metadata.ts`, published browser-safe as\n`@happyvertical/smrt-core/ui-metadata` (also re-exported from the package\nroot). `ManifestGenerator.applyUiMetadata()` runs in `applyGenerationPasses`\nafter inheritance, so every producer (Vite plugin, `ManifestBuilder`,\n`generateManifest`) validates the same way.\n\n- **Display label.** `@smrt({ display: { label: '<field>' } })` names the own\n field generic pickers and assistants use to label a record. Own fields only —\n no paths into other packages; a model whose label lives elsewhere ships a\n selector. Undeclared, it defaults to the first of `name`, `title`, `label`,\n `code` that is a usable own field. Build fails when the declared field is\n missing, `sensitive`, `transient`, or a relationship/json field. The resolved\n field is emitted as `displayLabelField` on the manifest object and in the\n knowledge artifact, and generated MCP/WebMCP `list`/`get`/`update`/`delete`\n descriptions end with ``Records are identified by their `<field>` field.``\n (`describeAction` in `generators/tool-schema.ts`; nothing is added when the\n model has no label field).\n- **Selector slots.** `ModuleUISlot.selects: '@scope/pkg:Model'`\n (`@happyvertical/smrt-types`) marks a package component as THE selector for a\n model. The scanner reads such slots statically from `src/**/ui.ts`\n (`parseUiSelectorsFile`, independent of the class glob) and the manifest\n carries them as `uiSelectors` keyed by slot id. `selects` must be a string\n literal; a duplicate slot id, two selectors for one model, or an own-package\n target that does not exist fails the build. Hosts call\n `findSelectorFor(manifests, qualifiedName)`; generic forms use the selector\n for any `@foreignKey`/`@crossPackageRef` field targeting that model and fall\n back to a generic picker labelled by `displayLabelField`. `display` is read\n per class: an STI subclass that declares none uses the default lookup rather\n than its base's declaration, and a CTI child cannot name a parent's field.\n\n## Worker registration artifact (#3117)\n\nWith `svelteKit.enabled`, a production `vite build` compiles the generated\n`smrt-register.ts` into `.smrt/runtime/register.js` after the server build\n(`src/vite-plugin/worker-registration.ts`, called once from `closeBundle`).\nadapter-node re-bundles the server from its own entries, so the web bundle is\nnever importable by a separate Node process; this artifact is how a deployed\ntask/schedule worker registers the app's local objects (and, through the\n`.smrt/register.js` it imports, consumed packages' objects) with the same\nqualified identities and isolated manifests as the web server. It is a nested\nSSR build with no consumer plugins: only aliases pointing into the project\nsources (e.g. `$lib`) apply, and bare imports stay external so the worker\nshares the installed `@happyvertical/smrt-core` registry. The sole exception is\n`@oxc-project/runtime` and its helper subpaths: Vite injects them when lowering\ndecorators, but applications do not declare that compiler support dependency,\nso the artifact bundles them. SvelteKit\nvirtual modules (`$env/*`, `$app/*`) are not available to a worker; an object\nimporting one fails at worker startup. A failure fails the build.\n\n**Register imports stay Node-loadable (#4219).** `.smrt/register.js`\n(`generateRegistrationFile` in `src/consumer-plugin/index.ts`) is evaluated by\nplain Node — `smrt db:migrate`, every CLI, and the worker artifact above — as\nwell as by Vite SSR, so it may only import modules Node can load. Each object\nis imported from its manifest `importPath` (the scanner's export-subpath match,\ne.g. `@happyvertical/smrt-products/models`). Each configured provider in\n`smrtDependencies` is also imported once so it is evaluated in Node's installed\npackage context (#4187): the package root, unless the package declares that\nroot as a Svelte entry (`isComponentBearingRoot`: a `svelte` field, or a `.`\nexport with a `svelte` condition or a `.svelte`/`.css` target — e.g.\n`@happyvertical/smrt-products`, whose root re-exports its components and so\nreaches `@happyvertical/smrt-ui`'s `.svelte` files). Such a root is never\nimported and no subpath is substituted for it (`resolveProviderContextImport`\nreturns `undefined`): its already-imported object entries establish the\ncontext, a provider contributing only nested objects needs none because the\ntransitive loader uses only `import.meta.resolve(provider)`, and a substitute\n`./models` whose JS reached `.svelte` would re-create the failure. A package that publishes Svelte\nonly under subpaths keeps its root import; the broader `isComponentBearingPackage`\ntest (any Svelte export) only selects `ssr.noExternal` packages. A `svelte`\ncondition counts even with a plain `.js` target — that is what svelte-package\nand Vite library builds emit for a root that re-exports components, and every\nSvelte root in this workspace (`smrt-products`, `smrt-ui`, `smrt-svelte`,\n`smrt-planner`) has that shape and fails under plain Node; a Node-safe root\nthat declares `svelte` anyway only loses an optional context import.\nTransitive-only object modules are loaded through `importSmrtDependency()`\nfrom their provider's context, which needs no root import.\n\nSelective SSR compilation compares a JavaScript barrel's `svelte` export target\nagainst the package instance visible from its actual importer. pnpm may install\nthe same version in multiple peer contexts; the app-root package directory is\nnot the identity of a transitive import. Read that importer's installed export\nmap and root together, preserving exact target matching. Plain server entries\nremain external; do not fix peer-context resolution by deduplicating app\npackages or bundling every server export (#4318).\n\nNot covered by contract: a package whose root is a Svelte entry AND whose\n`@smrt()` objects are exported only from that root. The object loop must then\nimport the root (it is the only module exporting the constructors) — statically\nwhen the app resolves the package, or through `importSmrtDependency()` when it\nis reached only through its provider — so Vite SSR works but plain Node can\nload the register only if that root happens to be Node-safe. Generation keeps\nthe import and warns once per package (`declares a Svelte root entry`), asking\nto verify the root under plain Node or publish the objects from `./models` or\n`./objects`. The declaration is a heuristic: a Node-safe root that declares\n`svelte` only by convention gets the same warning (a false positive, harmless —\nthe import is kept) and, when its objects come from subpaths, loses only its\noptional provider-context import. Regression: the `#4219` tests in\n`index.test.ts` (unit cases for `isComponentBearingRoot` /\n`resolveProviderContextImport`, a child-Node load of the generated register,\nthe nested-only provider that gets no import, and the root-only and transitive\nwarnings).\n\n**Gate: `pnpm check:node-roots`** (root script; run after `pnpm build`). It\nruns `scripts/survey-node-root-load.mjs --strict --allow=<packages>`, which\nspawns `node --input-type=module -e \"await import('<name>')\"` from every\nbuilt workspace package whose manifest declares `@smrt()` objects and fails on\nany root plain Node cannot evaluate that is not allow-listed. The allow-list\nis explicit and lives in the root `package.json` script: currently only\n`@happyvertical/smrt-products`, whose root re-exports its Svelte components by\ndocumented contract. A new entry needs the same justification; a model root\nthat merely starts importing `@happyvertical/smrt-ui` must be fixed, not\nallowed. `--strict` also fails when nothing was surveyed (run before\n`pnpm build`) and when an allow-listed package was not surveyed or now loads,\nso the list cannot go stale. `--all` surveys every published package,\nincluding those without objects, as a report.\n\nCI runs the gate in both Knowledge Freshness jobs of\n`.github/workflows/test-suite.yml`. In full mode (the merge queue, and every PR\nwhile `vars.CI_MERGE_QUEUE_ENABLED` is not `true`) it always runs. In affected\nmode it runs only when the `knowledge` paths filter matches, which covers\n`package.json` files and docs but not `packages/*/src/**`; a same-repo PR that\nmakes a model root import smrt-ui only through source is therefore first\ncaught in the merge queue, which still blocks the merge. The filter is not\nwidened for this, because that would run the knowledge job on every PR. Run\n`pnpm check:node-roots` locally when changing a model package's root imports.\n\n## Lightweight discovery\n\nFilesystem-only discovery, module-doc parsing, and graph freshness are owned by\n`@happyvertical/smrt-scanner/knowledge`. Core's knowledge exports remain compatible\nreexports. Runtime manifest projection and custom-action semantics stay in core.\n\n`src/knowledge-discovery.ts`, exported through `smrt-core/knowledge`, enumerates\ninstalled scope directories and reads canonical AGENTS/legacy CLAUDE docs without\nloading package code, artifacts, or scanning objects. CLI snapshots and MCP share\nthese primitives. Callers retain selection policy: snapshots resolve links and\nfall back to `packages/*` only when no installed SMRT package loads; MCP preserves\nnode_modules paths, deduplicates realpaths, excludes authored workspace links,\nand then enriches the selected packages. Workspace-root/glob discovery remains\nowned by each consumer.\n\n## Domain Knowledge Artifacts\n\n`smrtPlugin()` writes runtime manifests and agent/developer knowledge artifacts:\n\n- local dev/build: `.smrt/manifest.json` and `.smrt/smrt-knowledge.json`\n- package build: `dist/manifest.json` and `dist/smrt-knowledge.json`\n\nKeep `manifest.json` runtime-focused. `smrt-knowledge.json` is the deterministic\nagent contract for downstream review and architecture tools.\n\nThe CLI's schema-command gate reads that same merged file: a pure-consumer\nproject (0 local objects) qualifies on the consumed packages its manifest\nrecords, so a clobbered `objects` map is what made `smrt db:migrate` report\n`missing_local_manifest` (#2925).\n\n`.smrt/manifest.json` has two writers and neither owns all of it: `smrtPlugin()`\ncontributes the project's scanned objects and `smrtConsumer()` the consumed\npackages' entries. Both writes are merge-preserving, keyed on each entry's\nrecorded `packageName` — a plain overwrite in either direction silently drops\nthe other's objects, and the last pass of a multi-environment build is not\nreliably the aggregating one (#1760, #2925). `dist/manifest.json` stays\nlocal-only: a published package must not carry its dependencies' objects.\nCore's canonical generator excludes temporary `__test-*` source roots as well\nas test files and `__tests__`; prepack verifies fresh and reused manifests\ncontain no test-artifact marker or fixture source provenance (#4190).\n\nThe schema-version-1 object projection is additive and high-signal: it retains\nnormalized tenant mode/field, explicit `cti`/`sti` strategy, conflict columns,\nmethod signatures, and field defaults/constraints/readonly/transient flags.\nSensitive fields are removed before both `fields` and `relationships` are\nderived, including legacy flags stored under `_meta`; matching field and\nsnake-case column names are also removed from projected conflict columns, and a\nsensitive custom tenant field is omitted while retaining scope and mode.\nGenerated artifacts assert this boundary with `sensitiveFieldsExcluded: true`;\nthe optional marker keeps schema version 1 additive while letting readers\nidentify older artifacts that require raw-manifest corroboration.\n\nConfig precedence for knowledge is defaults → top-level `knowledge` in\n`smrt.config.ts` → `packages[packageName].knowledge` → plugin option →\nobject-level `@smrt({ knowledge })`.\n\nTest generation uses that same effective configuration and publishes knowledge\nmatching the manifest it generated. Disabling knowledge removes a previous local\nknowledge artifact, so consumers cannot mistake it for current enabled output.\n`new ManifestBuilder(projectRoot)` keeps discovery, package identity, and output\nat that explicit root, including installed providers and external base classes;\nomitting the argument retains the current-working-directory\nbehavior. Both manifest writers retain declared package dependencies when merging\ntheir local and consumed-object projections.\n\nTest manifests carry optional `artifactPurpose: 'test'`; dependency discovery\nskips those artifacts, including `.smrt` fallbacks. Unmarked runtime manifests,\nempty published providers, and source-only development providers remain eligible.\n`ManifestBuilder` accepts explicit `outputMode: 'dev' | 'build'`; omitted mode\nretains legacy filename inference. Vitest selects dev mode explicitly, so tests\nnever replace a production `dist/manifest.json`. Relative output directories are\nresolved against the builder's project root.\n\nWith `svelteKit.enabled`, `smrtPlugin()`'s transform prepends\n`import './smrt-register.js';` to the configured config module\n(`<configPath>/<configFileName>`, `src/vite-plugin/sveltekit-register-injection.ts`)\nonce the register module exists, on the same first line so line numbers hold.\nApps therefore never hand-write the guarded registration import (#3416); a\nconfig module that still has it imports the same module twice, harmlessly.\n\nTracked `src/lib/server/smrt-register.ts` belongs to SvelteKit config/type\ngeneration. Generic library builds consume it but do not cache or restore it\nas an output; otherwise a concurrent config producer's bytes can be replayed\nover a newer registration after dependency eligibility changes.\n\nObject-level `knowledge: false` excludes an object from authored context only;\nit must not change runtime manifest registration. Use\n`knowledge: { tags, summary, risks }` for review-sensitive domain objects.\n\nHTTP knowledge routes are disabled by default. If `knowledge.api.enabled` is\ntrue, generated SvelteKit routes must stay GET-only and guarded by dev mode or\nadmin auth.\n\n\n## Vite Plugin\n\n`src/vite-preset/` (`@happyvertical/smrt-core/vite`, `smrt()`) composes the Oxc\ndecorator block, `smrtConsumer`, then `smrtPlugin` with the template path\nconventions. Its package list comes only from the `smrt({ packages })` option, or\n`smrt.config` `consumer.packages` (smrt-config schema); it never sniffs\n`package.json`, and a missing or malformed list throws. Keep its output\nbyte-identical to the two-plugin form (`src/vite-preset/index.test.ts`). It\nalso dedupes `@sveltejs/kit` (`smrt:shared-runtime`): SvelteKit matches\n`redirect()`/`fail()` by class, and a linked/workspace SMRT SvelteKit entry\nwould otherwise import its own copy and turn redirects into 500s. The preset\nalso keeps the thin `@happyvertical/smrt-app-runtime/sveltekit` entry inside\nthe SSR bundle: adapter-node bundles Kit itself, and an external runtime\nwould load a separate class through Node even at the same installed version.\nVite matches `ssr.noExternal` by package name, so a pre-resolver keeps the\nexact runtime root external and `ssr.external` keeps `./sveltekit-runtime`\nexternal. That implementation subpath is an internal composition boundary;\napps keep importing `./sveltekit`. This avoids bundling native SQL dependencies.\nThis split applies only to builds; development keeps normal SSR externalization\nand Kit dedupe because Vite's dev module runner ignores resolver external flags.\nOther runtime dependencies retain their normal externalization. Do not explicitly\nexternalize app-runtime in an app config. The production regression in\n`packages/app-runtime/src/sveltekit-production.test.ts` exercises the final\nadapter-node HTTP handler using an installed copy of the published dist entry.\n\n**Package models over REST (#3749).** `consumer.packages` registers a package's\nmodels and gives `@smrt/web` (the consumer's `collectionDefinitions`, one per\nlist-capable model, endpoint `/<collection>`) their definitions, but a package\nmodel gets generated REST routes only when it is on the consumer's hosting\nallowlist (`smrtConsumer({ svelteKit: { objects } })`, #2850: an HTTP exposure\nboundary, each model's own `api` config still applies, so `api: false` and\nempty-include models stay closed, tenancy comes with the generated route). The\npreset fills that allowlist: with `smrt.cookbook.json` present it is the models\nof the cookbook's recipes plus `features`, minus models the cookbook `exposure`\nturns off for `api` or a recipe's `options[model].exposure.api === false`\n(`vite-preset/cookbook-exposure.ts`; recipe `{ exclude }` narrowing is not\napplied by the build yet). `smrt({ expose })` replaces the derived list. No\ncookbook and no `expose` keeps `svelteKit: true` (nothing hosted). The producer's\n`@happyvertical/smrt-virt-web` still covers only the app's own models; read\npackage models from `@smrt/web`. `smrt({ objectsDir })` is passed to the hosting\ncontribution too: the producer and consumer share one `routesDir`, and the\ngenerator refuses it when their settings differ.\n\n**STI siblings (#3749).** Package STI subclasses share their base's table, and\nthe manifest gives them the base's `collection` unless they declare one\n(`consumer-plugin/hosted-sti-collections.ts`). Two hosted siblings would both\nclaim `/<base collection>`, so each hosted subclass that inherits its\ncollection gets its class-derived one (`Order` -> `orders`,\n`defaultCollectionName` in the scanner), in every manifest view the consumer\nderives: routes, `@smrt/web`, the generated client and registration. This is the\nconvention the framework already has for an independent route collection\n(`@smrt({ collection })`), applied by default for hosted subclasses; an explicit\ncollection wins. The route therefore reads through the subclass's own\ncollection (rows scoped to its `_meta_type`) and the definition for that model\nadvertises that route (`objectRef` = the model, `endpoint` = `/orders`). A hosted\nbase keeps the shared name and the base-collection semantics (every row of the\ntable, any subtype). A subclass left off the allowlist keeps the shared\ncollection and folds into the base's definition, as before. A collection class\nthat inherits another's item type (`MaterialCollection extends\nProductCollection`) resolves to the same item and collection, so only one of\nthem is hosted (the class named for the item, else the first). Subclasses that\ndeclare no `api` config get the omitted-config default (full CRUD), not the\nbase's `include`; narrow them on the subclass if the base is meant to restrict\nthem.\n\n```typescript\n// vite.config.ts — required for @smrt() decorators (Vite 8+, oxc transform)\nexport default defineConfig({\n oxc: {\n decorator: {\n legacy: true,\n emitDecoratorMetadata: true,\n },\n },\n});\n```\n\nUnder Vite 8 the oxc transform does not honor the pre-Vite-8 `esbuild.tsconfigRaw`\nrecipe (or tsconfig `experimentalDecorators` reached through SvelteKit's\n`extends \"./.svelte-kit/tsconfig.json\"` chain), so that recipe throws\n`SyntaxError: Invalid or unexpected token` on the first SSR request. Configure\ndecorators through `oxc.decorator` instead. Consumers still pinned on vite<8 need\nthe legacy `esbuild.tsconfigRaw` form with `experimentalDecorators: true,\nemitDecoratorMetadata: true`.\n\nFor independent CI invocations, both `smrtPlugin()` and `smrtConsumer()` accept\nthe same `generationSnapshot: { path, sha256, provenance, sourceRoot }`. The\nschema-v1 snapshot produced by `serializeSmrtGenerationSnapshot()` contains the\nmerged project/dependency manifest, portable source paths, and source-file\ndigests; each plugin selects its own view. Reuse mode fails closed on\nbyte/provenance/path/content drift, skips scans and manifest writes, and still\ngenerates routes, types, registration, and virtual modules. Omit it for normal\nlocal development and watch mode.\n\n## Clean consumer type checks (TypeScript 6)\n\nThe consumer config hook writes `.smrt/register.js` alongside generated types\nafter route preflight succeeds. Generated SvelteKit registration imports that\nmodule, so it must exist during `svelte-kit sync`, before Vite's `buildStart`.\nBoth hosted routes and the legacy `svelteKit: true` mode follow this order.\nThe clean-consumer regression fixture ships real provider declarations and\nchecks the generated imports with `svelte-check`.\nCore's `generate:test` task depends on its own `build`: both publish the local\n`.smrt` manifest/knowledge pair, so Turbo must serialize production before test\ngeneration instead of caching an interleaved pair.\n\n## Model descriptions\n\nEach manifest object and knowledge `objects[]` entry may carry a user-facing\n`description` (for the planner Features tab and similar). The scanner\n(`extractClassJsDocDescription` in `packages/scanner/src/oxc-parser.ts`) reads it\nfrom the parsed source, in priority order: explicit `@smrt({ description })`,\nelse the first paragraph of the class JSDoc (the JSDoc may sit above `export`\nand decorators), minus a leading `ClassName - ` / `ClassName: `, tags\n(`{@link}`, backticks, HTML) and later paragraphs, capped at 200 chars on a\nsentence boundary. `@internal` classes and classes without docs emit no key. It\nis not inherited from a parent class. Generated MCP tool descriptions are\nunchanged (they stay generic).\n\n## AGENTS.md discovery in a monorepo (#3476)\n\n`buildDomainKnowledgeManifest()` takes the agent doc from the project root, else from the nearest ancestor `AGENTS.md` up to the workspace root (the nearest directory with `pnpm-workspace.yaml`, else the git root, a directory containing `.git`). It never walks past that boundary, and with no boundary above the project root it checks only the root. `agentDocPath` stays relative to the project root (so an inherited doc reads `../../AGENTS.md`), and `agentDoc` and `sourceHashes.agents` are populated from it. Module docs linked from the doc (`moduleDocs`) are only followed when `AGENTS.md` sits at the project root, since their links resolve against that directory.\n"
1206
+ "content": "# Decorators, build integration, and knowledge\n\nKey options: `tableName`, `tableStrategy` ('cti'|'sti'), `conflictColumns`, `indexes` (declared multi-column indexes; see [schema-paths.md](schema-paths.md)), `api`/`mcp`/`cli` (generation config), `ai` (callable methods), `hooks` (beforeSave/afterSave/beforeDelete/afterDelete), `embeddings` (auto-generate), `tenantScoped`, `agent`, `ui` (`{ icon, label, description }` — nav/help hints round-tripped through the manifest as plain data; `description` is the object-level seed for form-level help, #2046).\n\nRegistration sets `SMRT_TABLE_NAME` static property (survives minification).\n\nAn explicit `@smrt({ collection: 'domain.records' })` names the route/permission\nnamespace independently of `tableName`, including standalone non-STI models.\nScanner output preserves it in both `collection` and `decoratorConfig.collection`.\nGenerated registration reads the merged declaration, so older manifests with a\nderived top-level collection still agree with native decorators. Explicit runtime\nregistration options win; absent declarations retain manifest/STI/default naming.\n\n## @field() UI hints (#2046)\n\n`@field({ ui: { basic, group, order, locked } })` — a static, presentation-only\nseed for the field-policy rail (epic #2045). Carried in the manifest under the\nfield's `_meta.ui` (never a top-level `FieldDefinition` key), readable at\nruntime via `getAllFields()` at `field._meta.ui`, and emitted (sanitized) with\n`description` into generated web-collection definitions and browser MCP tool\nschemas. No schema/persistence/security effect — `sensitive`/`readPermission`\nstay the security rail, and `sensitive`/`transient` fields never emit to the\nclient at all.\n\n`ui.widget` (#3599) adds a presentation widget hint:\n`'textarea' | 'currency' | 'email' | 'url' | 'phone'`. It rides the same\n`_meta.ui` channel and is also emitted (sanitized) into web-collection\ndefinitions. The manifest generator validates it against the field type and\nfails the build otherwise: every widget needs a `text` field (`currency` is a\ncurrency-CODE picker for an ISO 4217 string such as `currency = 'USD'`, not a\nmoney amount; amounts are integer minor units and take no widget); an unknown\nvalue or a non-text field is an error. `ui.widget` is a hint only — it never changes\nthe column type or validation.\n\n`ui.format` (#3718) says how a value is presented: `money` (an `integer` of\nminor units — the amount hint record pages format and total), `date` (a\nbusiness date on a `datetime`, shown without a time), `datetime`, `percent`\n(a rate on a `decimal`/`integer`). Same channel, same emission, same build-time\ncheck against the field type. `@foreignKey(Target, { role: 'items' })` (or on\n`@crossPackageRef`) marks the declaring model as the target's line items; the\nscanner keeps it at `_meta.role`, the build rejects it on a non-reference or\nan unknown value, and the generated web relationship edge carries\n`role: 'items'` (edges without a role stay byte-stable). Both are hints: no\nschema, persistence or security effect.\n\n## Presentation metadata (#3599)\n\nPure helpers live in `src/ui-metadata.ts`, published browser-safe as\n`@happyvertical/smrt-core/ui-metadata` (also re-exported from the package\nroot). `ManifestGenerator.applyUiMetadata()` runs in `applyGenerationPasses`\nafter inheritance, so every producer (Vite plugin, `ManifestBuilder`,\n`generateManifest`) validates the same way.\n\n- **Display label.** `@smrt({ display: { label: '<field>' } })` names the own\n field generic pickers and assistants use to label a record. Own fields only —\n no paths into other packages; a model whose label lives elsewhere ships a\n selector. Undeclared, it defaults to the first of `name`, `title`, `label`,\n `code` that is a usable own field. Build fails when the declared field is\n missing, `sensitive`, `transient`, or a relationship/json field. The resolved\n field is emitted as `displayLabelField` on the manifest object and in the\n knowledge artifact, and generated MCP/WebMCP `list`/`get`/`update`/`delete`\n descriptions end with ``Records are identified by their `<field>` field.``\n (`describeAction` in `generators/tool-schema.ts`; nothing is added when the\n model has no label field).\n- **Selector slots.** `ModuleUISlot.selects: '@scope/pkg:Model'`\n (`@happyvertical/smrt-types`) marks a package component as THE selector for a\n model. The scanner reads such slots statically from `src/**/ui.ts`\n (`parseUiSelectorsFile`, independent of the class glob) and the manifest\n carries them as `uiSelectors` keyed by slot id. `selects` must be a string\n literal; a duplicate slot id, two selectors for one model, or an own-package\n target that does not exist fails the build. Hosts call\n `findSelectorFor(manifests, qualifiedName)`; generic forms use the selector\n for any `@foreignKey`/`@crossPackageRef` field targeting that model and fall\n back to a generic picker labelled by `displayLabelField`. `display` is read\n per class: an STI subclass that declares none uses the default lookup rather\n than its base's declaration, and a CTI child cannot name a parent's field.\n\n## Worker registration artifact (#3117)\n\nWith `svelteKit.enabled`, a production `vite build` compiles the generated\n`smrt-register.ts` into `.smrt/runtime/register.js` after the server build\n(`src/vite-plugin/worker-registration.ts`, called once from `closeBundle`).\nadapter-node re-bundles the server from its own entries, so the web bundle is\nnever importable by a separate Node process; this artifact is how a deployed\ntask/schedule worker registers the app's local objects (and, through the\n`.smrt/register.js` it imports, consumed packages' objects) with the same\nqualified identities and isolated manifests as the web server. It is a nested\nSSR build with no consumer plugins: only aliases pointing into the project\nsources (e.g. `$lib`) apply, and bare imports stay external so the worker\nshares the installed `@happyvertical/smrt-core` registry. The sole exception is\n`@oxc-project/runtime` and its helper subpaths: Vite injects them when lowering\ndecorators, but applications do not declare that compiler support dependency,\nso the artifact bundles them. SvelteKit\nvirtual modules (`$env/*`, `$app/*`) are not available to a worker; an object\nimporting one fails at worker startup. A failure fails the build.\n\n**Register imports stay Node-loadable (#4219).** `.smrt/register.js`\n(`generateRegistrationFile` in `src/consumer-plugin/index.ts`) is evaluated by\nplain Node — `smrt db:migrate`, every CLI, and the worker artifact above — as\nwell as by Vite SSR, so it may only import modules Node can load. Each object\nis imported from its manifest `importPath` (the scanner's export-subpath match,\ne.g. `@happyvertical/smrt-products/models`). Each configured provider in\n`smrtDependencies` is also imported once so it is evaluated in Node's installed\npackage context (#4187): the package root, unless the package declares that\nroot as a Svelte entry (`isComponentBearingRoot`: a `svelte` field, or a `.`\nexport with a `svelte` condition or a `.svelte`/`.css` target — e.g.\n`@happyvertical/smrt-products`, whose root re-exports its components and so\nreaches `@happyvertical/smrt-ui`'s `.svelte` files). Such a root is never\nimported and no subpath is substituted for it (`resolveProviderContextImport`\nreturns `undefined`): its already-imported object entries establish the\ncontext, a provider contributing only nested objects needs none because the\ntransitive loader uses only `import.meta.resolve(provider)`, and a substitute\n`./models` whose JS reached `.svelte` would re-create the failure. A package that publishes Svelte\nonly under subpaths keeps its root import; the broader `isComponentBearingPackage`\ntest (any Svelte export) only selects `ssr.noExternal` packages. A `svelte`\ncondition counts even with a plain `.js` target — that is what svelte-package\nand Vite library builds emit for a root that re-exports components, and every\nSvelte root in this workspace (`smrt-products`, `smrt-ui`, `smrt-svelte`,\n`smrt-planner`) has that shape and fails under plain Node; a Node-safe root\nthat declares `svelte` anyway only loses an optional context import.\nTransitive-only object modules are loaded through `importSmrtDependency()`\nfrom their provider's context, which needs no root import.\n\nSelective SSR compilation compares a JavaScript barrel's `svelte` export target\nagainst the package instance visible from its actual importer. pnpm may install\nthe same version in multiple peer contexts; the app-root package directory is\nnot the identity of a transitive import. Read that importer's installed export\nmap and root together, preserving exact target matching. Plain server entries\nremain external; do not fix peer-context resolution by deduplicating app\npackages or bundling every server export (#4318).\n\nThe same server/component split applies to consumer development SSR (#4475).\nVite's dev import analysis ignores a resolver's `external` flag, so broad\n`noExternal` alone creates another module instance even when native Node and\nVite resolve the identical file. That duplicates module-local enablement and\ninterceptors while an AsyncLocalStorage singleton can still appear shared.\nThe consumer resolver records only exact server entry files selected through\nthe importer's installed export map. Its dev SSR environment `fetchModule`\ntransport returns those files as native external modules; component barrels,\nSvelte and CSS continue through Vite. Production keeps bare external imports.\nNever externalize the complete component-bearing package or switch its mutable\nruntime flags to globals to mask duplicate execution. The real `createServer` /\n`ssrLoadModule` regression in `selective-ssr-dev.test.ts` initializes native\nserver state and verifies root/subpath reads plus direct and transitive peer\nSvelte/CSS compilation in one dev request.\n\nNot covered by contract: a package whose root is a Svelte entry AND whose\n`@smrt()` objects are exported only from that root. The object loop must then\nimport the root (it is the only module exporting the constructors) — statically\nwhen the app resolves the package, or through `importSmrtDependency()` when it\nis reached only through its provider — so Vite SSR works but plain Node can\nload the register only if that root happens to be Node-safe. Generation keeps\nthe import and warns once per package (`declares a Svelte root entry`), asking\nto verify the root under plain Node or publish the objects from `./models` or\n`./objects`. The declaration is a heuristic: a Node-safe root that declares\n`svelte` only by convention gets the same warning (a false positive, harmless —\nthe import is kept) and, when its objects come from subpaths, loses only its\noptional provider-context import. Regression: the `#4219` tests in\n`index.test.ts` (unit cases for `isComponentBearingRoot` /\n`resolveProviderContextImport`, a child-Node load of the generated register,\nthe nested-only provider that gets no import, and the root-only and transitive\nwarnings).\n\n**Gate: `pnpm check:node-roots`** (root script; run after `pnpm build`). It\nruns `scripts/survey-node-root-load.mjs --strict --allow=<packages>`, which\nspawns `node --input-type=module -e \"await import('<name>')\"` from every\nbuilt workspace package whose manifest declares `@smrt()` objects and fails on\nany root plain Node cannot evaluate that is not allow-listed. The allow-list\nis explicit and lives in the root `package.json` script: currently only\n`@happyvertical/smrt-products`, whose root re-exports its Svelte components by\ndocumented contract. A new entry needs the same justification; a model root\nthat merely starts importing `@happyvertical/smrt-ui` must be fixed, not\nallowed. `--strict` also fails when nothing was surveyed (run before\n`pnpm build`) and when an allow-listed package was not surveyed or now loads,\nso the list cannot go stale. `--all` surveys every published package,\nincluding those without objects, as a report.\n\nCI runs the gate in both Knowledge Freshness jobs of\n`.github/workflows/test-suite.yml`. In full mode (the merge queue, and every PR\nwhile `vars.CI_MERGE_QUEUE_ENABLED` is not `true`) it always runs. In affected\nmode it runs only when the `knowledge` paths filter matches, which covers\n`package.json` files and docs but not `packages/*/src/**`; a same-repo PR that\nmakes a model root import smrt-ui only through source is therefore first\ncaught in the merge queue, which still blocks the merge. The filter is not\nwidened for this, because that would run the knowledge job on every PR. Run\n`pnpm check:node-roots` locally when changing a model package's root imports.\n\n## Lightweight discovery\n\nFilesystem-only discovery, module-doc parsing, and graph freshness are owned by\n`@happyvertical/smrt-scanner/knowledge`. Core's knowledge exports remain compatible\nreexports. Runtime manifest projection and custom-action semantics stay in core.\n\n`src/knowledge-discovery.ts`, exported through `smrt-core/knowledge`, enumerates\ninstalled scope directories and reads canonical AGENTS/legacy CLAUDE docs without\nloading package code, artifacts, or scanning objects. CLI snapshots and MCP share\nthese primitives. Callers retain selection policy: snapshots resolve links and\nfall back to `packages/*` only when no installed SMRT package loads; MCP preserves\nnode_modules paths, deduplicates realpaths, excludes authored workspace links,\nand then enriches the selected packages. Workspace-root/glob discovery remains\nowned by each consumer.\n\n## Domain Knowledge Artifacts\n\n`smrtPlugin()` writes runtime manifests and agent/developer knowledge artifacts:\n\n- local dev/build: `.smrt/manifest.json` and `.smrt/smrt-knowledge.json`\n- package build: `dist/manifest.json` and `dist/smrt-knowledge.json`\n\nKeep `manifest.json` runtime-focused. `smrt-knowledge.json` is the deterministic\nagent contract for downstream review and architecture tools.\n\nThe CLI's schema-command gate reads that same merged file: a pure-consumer\nproject (0 local objects) qualifies on the consumed packages its manifest\nrecords, so a clobbered `objects` map is what made `smrt db:migrate` report\n`missing_local_manifest` (#2925).\n\n`.smrt/manifest.json` has two writers and neither owns all of it: `smrtPlugin()`\ncontributes the project's scanned objects and `smrtConsumer()` the consumed\npackages' entries. Both writes are merge-preserving, keyed on each entry's\nrecorded `packageName` — a plain overwrite in either direction silently drops\nthe other's objects, and the last pass of a multi-environment build is not\nreliably the aggregating one (#1760, #2925). `dist/manifest.json` stays\nlocal-only: a published package must not carry its dependencies' objects.\nCore's canonical generator excludes temporary `__test-*` source roots as well\nas test files and `__tests__`; prepack verifies fresh and reused manifests\ncontain no test-artifact marker or fixture source provenance (#4190).\n\nThe schema-version-1 object projection is additive and high-signal: it retains\nnormalized tenant mode/field, explicit `cti`/`sti` strategy, conflict columns,\nmethod signatures, and field defaults/constraints/readonly/transient flags.\nSensitive fields are removed before both `fields` and `relationships` are\nderived, including legacy flags stored under `_meta`; matching field and\nsnake-case column names are also removed from projected conflict columns, and a\nsensitive custom tenant field is omitted while retaining scope and mode.\nGenerated artifacts assert this boundary with `sensitiveFieldsExcluded: true`;\nthe optional marker keeps schema version 1 additive while letting readers\nidentify older artifacts that require raw-manifest corroboration.\n\nConfig precedence for knowledge is defaults → top-level `knowledge` in\n`smrt.config.ts` → `packages[packageName].knowledge` → plugin option →\nobject-level `@smrt({ knowledge })`.\n\nTest generation uses that same effective configuration and publishes knowledge\nmatching the manifest it generated. Disabling knowledge removes a previous local\nknowledge artifact, so consumers cannot mistake it for current enabled output.\n`new ManifestBuilder(projectRoot)` keeps discovery, package identity, and output\nat that explicit root, including installed providers and external base classes;\nomitting the argument retains the current-working-directory\nbehavior. Both manifest writers retain declared package dependencies when merging\ntheir local and consumed-object projections.\n\nTest manifests carry optional `artifactPurpose: 'test'`; dependency discovery\nskips those artifacts, including `.smrt` fallbacks. Unmarked runtime manifests,\nempty published providers, and source-only development providers remain eligible.\n`ManifestBuilder` accepts explicit `outputMode: 'dev' | 'build'`; omitted mode\nretains legacy filename inference. Vitest selects dev mode explicitly, so tests\nnever replace a production `dist/manifest.json`. Relative output directories are\nresolved against the builder's project root.\n\nWith `svelteKit.enabled`, `smrtPlugin()`'s transform prepends\n`import './smrt-register.js';` to the configured config module\n(`<configPath>/<configFileName>`, `src/vite-plugin/sveltekit-register-injection.ts`)\nonce the register module exists, on the same first line so line numbers hold.\nApps therefore never hand-write the guarded registration import (#3416); a\nconfig module that still has it imports the same module twice, harmlessly.\n\nTracked `src/lib/server/smrt-register.ts` belongs to SvelteKit config/type\ngeneration. Generic library builds consume it but do not cache or restore it\nas an output; otherwise a concurrent config producer's bytes can be replayed\nover a newer registration after dependency eligibility changes.\n\nObject-level `knowledge: false` excludes an object from authored context only;\nit must not change runtime manifest registration. Use\n`knowledge: { tags, summary, risks }` for review-sensitive domain objects.\n\nHTTP knowledge routes are disabled by default. If `knowledge.api.enabled` is\ntrue, generated SvelteKit routes must stay GET-only and guarded by dev mode or\nadmin auth.\n\n\n## Vite Plugin\n\n`_resources` and application-runtime config detection resolve their exact\n`@happyvertical/smrt-users/sveltekit` and `@happyvertical/smrt-app-runtime/sveltekit`\nimports with ESM import conditions anchored\nto the consumer package URL, using `import-meta-resolve`. A `types`/`import`-only\nexport is valid; a CommonJS `createRequire.resolve` probe would falsely skip it.\nVerify the selected target is an existing file because import-meta resolution\ncan return URLs for missing files/directories. Missing/unexported dependencies\nremain skipped, ancestor-hoisted installs resolve normally, and handwritten\n`+server.ts`/`+server.js` ownership and explicit disablement remain unchanged.\nDo not change a package's export map to accommodate a mismatched detector.\n\n\n`src/vite-preset/` (`@happyvertical/smrt-core/vite`, `smrt()`) composes the Oxc\ndecorator block, `smrtConsumer`, then `smrtPlugin` with the template path\nconventions. Its package list comes only from the `smrt({ packages })` option, or\n`smrt.config` `consumer.packages` (smrt-config schema); it never sniffs\n`package.json`, and a missing or malformed list throws. Keep its output\nbyte-identical to the two-plugin form (`src/vite-preset/index.test.ts`). It\nalso dedupes `@sveltejs/kit` (`smrt:shared-runtime`): SvelteKit matches\n`redirect()`/`fail()` by class, and a linked/workspace SMRT SvelteKit entry\nwould otherwise import its own copy and turn redirects into 500s. The preset\nalso keeps the thin `@happyvertical/smrt-app-runtime/sveltekit` entry inside\nthe SSR bundle: adapter-node bundles Kit itself, and an external runtime\nwould load a separate class through Node even at the same installed version.\nVite matches `ssr.noExternal` by package name, so a pre-resolver keeps the\nexact runtime root external and `ssr.external` keeps `./sveltekit-runtime`\nexternal. That implementation subpath is an internal composition boundary;\napps keep importing `./sveltekit`. This avoids bundling native SQL dependencies.\nThis split applies only to builds; development keeps normal SSR externalization\nand Kit dedupe because Vite's dev module runner ignores resolver external flags.\nOther runtime dependencies retain their normal externalization. Do not explicitly\nexternalize app-runtime in an app config. The production regression in\n`packages/app-runtime/src/sveltekit-production.test.ts` exercises the final\nadapter-node HTTP handler using an installed copy of the published dist entry.\n\n**Package models over REST (#3749).** `consumer.packages` registers a package's\nmodels and gives `@smrt/web` (the consumer's `collectionDefinitions`, one per\nlist-capable model, endpoint `/<collection>`) their definitions, but a package\nmodel gets generated REST routes only when it is on the consumer's hosting\nallowlist (`smrtConsumer({ svelteKit: { objects } })`, #2850: an HTTP exposure\nboundary, each model's own `api` config still applies, so `api: false` and\nempty-include models stay closed, tenancy comes with the generated route). The\npreset fills that allowlist: with `smrt.cookbook.json` present it is the models\nof the cookbook's recipes plus `features`, minus models the cookbook `exposure`\nturns off for `api` or a recipe's `options[model].exposure.api === false`\n(`vite-preset/cookbook-exposure.ts`; recipe `{ exclude }` narrowing is not\napplied by the build yet). `smrt({ expose })` replaces the derived list. No\ncookbook and no `expose` keeps `svelteKit: true` (nothing hosted). The producer's\n`@happyvertical/smrt-virt-web` still covers only the app's own models; read\npackage models from `@smrt/web`. `smrt({ objectsDir })` is passed to the hosting\ncontribution too: the producer and consumer share one `routesDir`, and the\ngenerator refuses it when their settings differ.\n\n**STI siblings (#3749).** Package STI subclasses share their base's table, and\nthe manifest gives them the base's `collection` unless they declare one\n(`consumer-plugin/hosted-sti-collections.ts`). Two hosted siblings would both\nclaim `/<base collection>`, so each hosted subclass that inherits its\ncollection gets its class-derived one (`Order` -> `orders`,\n`defaultCollectionName` in the scanner), in every manifest view the consumer\nderives: routes, `@smrt/web`, the generated client and registration. This is the\nconvention the framework already has for an independent route collection\n(`@smrt({ collection })`), applied by default for hosted subclasses; an explicit\ncollection wins. The route therefore reads through the subclass's own\ncollection (rows scoped to its `_meta_type`) and the definition for that model\nadvertises that route (`objectRef` = the model, `endpoint` = `/orders`). A hosted\nbase keeps the shared name and the base-collection semantics (every row of the\ntable, any subtype). A subclass left off the allowlist keeps the shared\ncollection and folds into the base's definition, as before. A collection class\nthat inherits another's item type (`MaterialCollection extends\nProductCollection`) resolves to the same item and collection, so only one of\nthem is hosted (the class named for the item, else the first). Subclasses that\ndeclare no `api` config get the omitted-config default (full CRUD), not the\nbase's `include`; narrow them on the subclass if the base is meant to restrict\nthem.\n\n```typescript\n// vite.config.ts — required for @smrt() decorators (Vite 8+, oxc transform)\nexport default defineConfig({\n oxc: {\n decorator: {\n legacy: true,\n emitDecoratorMetadata: true,\n },\n },\n});\n```\n\nUnder Vite 8 the oxc transform does not honor the pre-Vite-8 `esbuild.tsconfigRaw`\nrecipe (or tsconfig `experimentalDecorators` reached through SvelteKit's\n`extends \"./.svelte-kit/tsconfig.json\"` chain), so that recipe throws\n`SyntaxError: Invalid or unexpected token` on the first SSR request. Configure\ndecorators through `oxc.decorator` instead. Consumers still pinned on vite<8 need\nthe legacy `esbuild.tsconfigRaw` form with `experimentalDecorators: true,\nemitDecoratorMetadata: true`.\n\nFor independent CI invocations, both `smrtPlugin()` and `smrtConsumer()` accept\nthe same `generationSnapshot: { path, sha256, provenance, sourceRoot }`. The\nschema-v1 snapshot produced by `serializeSmrtGenerationSnapshot()` contains the\nmerged project/dependency manifest, portable source paths, and source-file\ndigests; each plugin selects its own view. Reuse mode fails closed on\nbyte/provenance/path/content drift, skips scans and manifest writes, and still\ngenerates routes, types, registration, and virtual modules. Omit it for normal\nlocal development and watch mode.\n\n## Clean consumer type checks (TypeScript 6)\n\nThe consumer config hook writes `.smrt/register.js` alongside generated types\nafter route preflight succeeds. Generated SvelteKit registration imports that\nmodule, so it must exist during `svelte-kit sync`, before Vite's `buildStart`.\nBoth hosted routes and the legacy `svelteKit: true` mode follow this order.\nThe clean-consumer regression fixture ships real provider declarations and\nchecks the generated imports with `svelte-check`.\nCore's `generate:test` task depends on its own `build`: both publish the local\n`.smrt` manifest/knowledge pair, so Turbo must serialize production before test\ngeneration instead of caching an interleaved pair.\n\n## Model descriptions\n\nEach manifest object and knowledge `objects[]` entry may carry a user-facing\n`description` (for the planner Features tab and similar). The scanner\n(`extractClassJsDocDescription` in `packages/scanner/src/oxc-parser.ts`) reads it\nfrom the parsed source, in priority order: explicit `@smrt({ description })`,\nelse the first paragraph of the class JSDoc (the JSDoc may sit above `export`\nand decorators), minus a leading `ClassName - ` / `ClassName: `, tags\n(`{@link}`, backticks, HTML) and later paragraphs, capped at 200 chars on a\nsentence boundary. `@internal` classes and classes without docs emit no key. It\nis not inherited from a parent class. Generated MCP tool descriptions are\nunchanged (they stay generic).\n\n## AGENTS.md discovery in a monorepo (#3476)\n\n`buildDomainKnowledgeManifest()` takes the agent doc from the project root, else from the nearest ancestor `AGENTS.md` up to the workspace root (the nearest directory with `pnpm-workspace.yaml`, else the git root, a directory containing `.git`). It never walks past that boundary, and with no boundary above the project root it checks only the root. `agentDocPath` stays relative to the project root (so an inherited doc reads `../../AGENTS.md`), and `agentDoc` and `sourceHashes.agents` are populated from it. Module docs linked from the doc (`moduleDocs`) are only followed when `AGENTS.md` sits at the project root, since their links resolve against that directory.\n"
1207
1207
  },
1208
1208
  {
1209
1209
  "path": "agents/memory.md",
@@ -123,6 +123,14 @@ export interface SmrtDevModule {
123
123
  id: string | null;
124
124
  }
125
125
  export interface SmrtDevServer {
126
+ /** Vite 6+ SSR native-loading transport; older servers do not expose environments. */
127
+ environments?: Record<string, {
128
+ fetchModule?: (id: string, importer?: string, options?: {
129
+ cached?: boolean;
130
+ startOffset?: number;
131
+ inlineSourceMap?: boolean;
132
+ }) => Promise<unknown>;
133
+ }>;
126
134
  config: {
127
135
  root: string;
128
136
  };
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-contract.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/plugin-contract.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plugin-contract.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/plugin-contract.ts"],"names":[],"mappings":"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"}
@@ -34,8 +34,8 @@
34
34
  *
35
35
  * So this route uses a **resolvable-dependency check**: does the exact
36
36
  * subpath the generated file imports — `@happyvertical/smrt-users/sveltekit`
37
- * — resolve from the consumer's `projectRoot`, via a `createRequire` rooted
38
- * at the consumer's own `package.json`? That is stronger than reading
37
+ * — resolve under ESM import conditions from the consumer's `projectRoot`,
38
+ * anchored to its own `package.json` URL? That is stronger than reading
39
39
  * `package.json`'s `dependencies` block (which can list a package that was
40
40
  * never installed) and, unlike a single hardcoded
41
41
  * `node_modules/@happyvertical/smrt-users/package.json` path, it uses
@@ -48,7 +48,11 @@
48
48
  * does not list `./package.json`, so resolving that path would throw
49
49
  * `ERR_PACKAGE_PATH_NOT_EXPORTED` even when the package is genuinely
50
50
  * installed — resolving the subpath the route actually imports sidesteps
51
- * that and checks precisely the thing that must succeed at build time.
51
+ * that and checks precisely the thing that must succeed at build time. Use
52
+ * import conditions because the published subpath may expose only `types` and
53
+ * `import`, without a CommonJS `require` or `default` branch. The ESM resolver
54
+ * can return a URL for a missing file or directory, so verify that the selected
55
+ * target is an existing file before emitting a route that imports it.
52
56
  *
53
57
  * An explicit `sveltekit: { resourcesRoute: { enabled: false } }` escape
54
58
  * hatch is also available, matching `changesRoute`/`eventsRoute`, for a
@@ -1 +1 @@
1
- {"version":3,"file":"resources-route.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/resources-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgFG;AAQH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAUjE;AAED,qEAAqE;AACrE,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAgBT;AAED;;iFAEiF;AACjF,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAUT;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAqDT"}
1
+ {"version":3,"file":"resources-route.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/resources-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoFG;AAcH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAUjE;AAED,qEAAqE;AACrE,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAgBT;AAED;;iFAEiF;AACjF,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAUT;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAqDT"}
@@ -1,9 +1,9 @@
1
1
  import { AUTO_GENERATED_ROUTE_HEADER } from "./route-header.js";
2
2
  import { resolveSvelteKitConfigImport } from "./sveltekit-config-import.js";
3
3
  import { join } from "node:path";
4
- import { pathToFileURL } from "node:url";
5
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
6
- import { createRequire } from "node:module";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
5
+ import { resolve as resolve$1 } from "import-meta-resolve";
6
+ import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
7
7
  //#region src/vite-plugin/resources-route.ts
8
8
  /**
9
9
  * SvelteKit `_resources` route generation — CLI discovery endpoint (#2663;
@@ -41,8 +41,8 @@ import { createRequire } from "node:module";
41
41
  *
42
42
  * So this route uses a **resolvable-dependency check**: does the exact
43
43
  * subpath the generated file imports — `@happyvertical/smrt-users/sveltekit`
44
- * — resolve from the consumer's `projectRoot`, via a `createRequire` rooted
45
- * at the consumer's own `package.json`? That is stronger than reading
44
+ * — resolve under ESM import conditions from the consumer's `projectRoot`,
45
+ * anchored to its own `package.json` URL? That is stronger than reading
46
46
  * `package.json`'s `dependencies` block (which can list a package that was
47
47
  * never installed) and, unlike a single hardcoded
48
48
  * `node_modules/@happyvertical/smrt-users/package.json` path, it uses
@@ -55,7 +55,11 @@ import { createRequire } from "node:module";
55
55
  * does not list `./package.json`, so resolving that path would throw
56
56
  * `ERR_PACKAGE_PATH_NOT_EXPORTED` even when the package is genuinely
57
57
  * installed — resolving the subpath the route actually imports sidesteps
58
- * that and checks precisely the thing that must succeed at build time.
58
+ * that and checks precisely the thing that must succeed at build time. Use
59
+ * import conditions because the published subpath may expose only `types` and
60
+ * `import`, without a CommonJS `require` or `default` branch. The ESM resolver
61
+ * can return a URL for a missing file or directory, so verify that the selected
62
+ * target is an existing file before emitting a route that imports it.
59
63
  *
60
64
  * An explicit `sveltekit: { resourcesRoute: { enabled: false } }` escape
61
65
  * hatch is also available, matching `changesRoute`/`eventsRoute`, for a
@@ -95,8 +99,7 @@ import { createRequire } from "node:module";
95
99
  */
96
100
  function consumerHasSmrtUsers(projectRoot) {
97
101
  try {
98
- createRequire(pathToFileURL(join(projectRoot, "package.json")).href).resolve("@happyvertical/smrt-users/sveltekit");
99
- return true;
102
+ return statSync(fileURLToPath(resolve$1("@happyvertical/smrt-users/sveltekit", pathToFileURL(join(projectRoot, "package.json")).href))).isFile();
100
103
  } catch {
101
104
  return false;
102
105
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resources-route.js","names":[],"sources":["../../src/vite-plugin/resources-route.ts"],"sourcesContent":["/**\n * SvelteKit `_resources` route generation — CLI discovery endpoint (#2663;\n * parent epic #2655).\n *\n * Emits `{routesDir}/_resources/+server.ts`: `GET /api/_resources`, the\n * endpoint the distributable CLI (`@happyvertical/smrt-app-cli`) reads to\n * learn which resources and commands it can invoke. Every other generated\n * route lives in `sveltekit-generator.ts` or a sibling emitter; this one is\n * kept in its own module for the same reason `changes-route.ts` and\n * `events-route.ts` are — a one-line registration hook in the shared\n * generator file.\n *\n * ## The dependency constraint\n *\n * The handler this route delegates to, `createResourceListHandler`, lives in\n * `@happyvertical/smrt-users` — and `smrt-users` depends on `smrt-core`, not\n * the reverse, so core cannot import it directly. That is not a problem for\n * *emitting* the import: the generated file is written into the consumer's\n * own `src/routes/`, where `@happyvertical/smrt-users` resolves through the\n * consumer's own dependency graph, exactly like the `@happyvertical/smrt-tenancy`\n * import `sveltekit-generator.ts`'s `generateTenantContextHelper` already\n * emits into tenant-scoped routes (see the comment above that function). The\n * only question is *when* it is safe to emit — a consumer that never took a\n * dependency on `smrt-users` must never receive a route whose import cannot\n * resolve, or a working build turns red.\n *\n * ## Detection mechanism\n *\n * Unlike the tenancy guard — which reads a semantic signal already present in\n * the manifest (whether any object is `@TenantScoped`, which itself implies\n * the app depends on `smrt-tenancy`) — nothing in the manifest indicates\n * whether a consumer has taken on `smrt-users`. Objects don't declare an\n * auth dependency; the app does, at the package level.\n *\n * So this route uses a **resolvable-dependency check**: does the exact\n * subpath the generated file imports — `@happyvertical/smrt-users/sveltekit`\n * — resolve from the consumer's `projectRoot`, via a `createRequire` rooted\n * at the consumer's own `package.json`? That is stronger than reading\n * `package.json`'s `dependencies` block (which can list a package that was\n * never installed) and, unlike a single hardcoded\n * `node_modules/@happyvertical/smrt-users/package.json` path, it uses\n * Node's real module resolution — so it also finds the package when a\n * workspace hoists shared dependencies to an ancestor `node_modules` (npm/\n * yarn workspaces, or pnpm with `node-linker=hoisted`) rather than\n * installing them directly under the consumer's own `projectRoot`.\n * Resolving the `/sveltekit` subpath specifically, rather than the\n * package's bare `package.json`, matters too: `smrt-users`'s `exports` map\n * does not list `./package.json`, so resolving that path would throw\n * `ERR_PACKAGE_PATH_NOT_EXPORTED` even when the package is genuinely\n * installed — resolving the subpath the route actually imports sidesteps\n * that and checks precisely the thing that must succeed at build time.\n *\n * An explicit `sveltekit: { resourcesRoute: { enabled: false } }` escape\n * hatch is also available, matching `changesRoute`/`eventsRoute`, for a\n * consumer that has `smrt-users` installed but wants to keep hand-writing\n * (or omit) `_resources`.\n *\n * ## Preserving a hand-written route\n *\n * Every consumer today hand-writes this route (the reference implementation\n * in the issue). `clearGeneratedRouteFiles` (in `sveltekit-generator.ts`)\n * already only deletes `+server.ts` files that start with\n * {@link AUTO_GENERATED_ROUTE_HEADER}, so a hand-written `_resources` route —\n * which never carries that header — survives the pre-generation sweep\n * untouched. This emitter additionally checks `existsSync` on the target\n * path before writing: by the time it runs, anything still there is\n * necessarily hand-written (a generator-owned copy would already have been\n * swept), so its mere presence is sufficient to skip generation and leave it\n * alone.\n *\n * Once a consumer's `_resources` route IS generator-owned (no hand-written\n * file present, `smrt-users` resolvable), its path is generator-owned for\n * `.gitignore` purposes too — it is included in `generatedRoutePaths` like\n * any other emitted route, so `updateGitignore` puts it in the managed\n * block. A project that migrated from a hand-written route to a fully\n * generated one may still carry a stale `# Application-owned rule` comment\n * above the same literal path from before the migration; `updateGitignore`\n * only ever touches its own managed block, so that stale comment is left as\n * project history for the consumer to clean up — the same way any other\n * manual `.gitignore` edit outside the managed block is preserved.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { AUTO_GENERATED_ROUTE_HEADER } from './route-header.js';\nimport { resolveSvelteKitConfigImport } from './sveltekit-config-import.js';\nimport type { SvelteKitOptions } from './sveltekit-generator.js';\n\n/**\n * Whether `@happyvertical/smrt-users/sveltekit` — the exact subpath the\n * generated route imports — is resolvable from the consumer at\n * `projectRoot`. See the module doc for why this check (rather than reading\n * `package.json`'s declared `dependencies`, or checking one hardcoded\n * physical path) is the chosen signal.\n */\nexport function consumerHasSmrtUsers(projectRoot: string): boolean {\n try {\n const consumerRequire = createRequire(\n pathToFileURL(join(projectRoot, 'package.json')).href,\n );\n consumerRequire.resolve('@happyvertical/smrt-users/sveltekit');\n return true;\n } catch {\n return false;\n }\n}\n\n/** Whether this invocation will own the `_resources` output file. */\nexport function willGenerateResourcesRoute(\n projectRoot: string,\n options: SvelteKitOptions,\n): boolean {\n if (\n options.resourcesRoute?.enabled === false ||\n !consumerHasSmrtUsers(projectRoot)\n ) {\n return false;\n }\n const routeDir = join(projectRoot, options.routesDir, '_resources');\n for (const name of ['+server.ts', '+server.js']) {\n const file = join(routeDir, name);\n if (!existsSync(file)) continue;\n if (!readFileSync(file, 'utf-8').startsWith(AUTO_GENERATED_ROUTE_HEADER)) {\n return false;\n }\n }\n return true;\n}\n\n/** Whether `_resources` is occupied by its generated utility or a preserved\n * handwritten SvelteKit server module. This is used only by route collision\n * preflight: the emitter remains file-aware and never overwrites handwriting. */\nexport function reservesResourcesRoute(\n projectRoot: string,\n options: SvelteKitOptions,\n): boolean {\n if (willGenerateResourcesRoute(projectRoot, options)) return true;\n const routeDir = join(projectRoot, options.routesDir, '_resources');\n return ['+server.ts', '+server.js'].some((name) => {\n const file = join(routeDir, name);\n return (\n existsSync(file) &&\n !readFileSync(file, 'utf-8').startsWith(AUTO_GENERATED_ROUTE_HEADER)\n );\n });\n}\n\n/**\n * Generate the `_resources/+server.ts` route. Returns true when a route was\n * written. Disabled with `sveltekit: { resourcesRoute: { enabled: false } }`;\n * skipped (with a log line) when `@happyvertical/smrt-users` is not\n * resolvable from the consumer, or when a hand-written route already\n * occupies the path.\n */\nexport function generateResourcesRoute(\n projectRoot: string,\n options: SvelteKitOptions,\n): boolean {\n if (options.resourcesRoute?.enabled === false) {\n return false;\n }\n\n if (!consumerHasSmrtUsers(projectRoot)) {\n console.log(\n '[smrt] Skipping _resources route - @happyvertical/smrt-users is not ' +\n 'resolvable; createResourceListHandler would not resolve in the ' +\n 'consumer build',\n );\n return false;\n }\n\n const routeDir = join(projectRoot, options.routesDir, '_resources');\n const filePath = join(routeDir, '+server.ts');\n\n // By this point in generation, clearGeneratedRouteFiles has already swept\n // any previously generator-owned copy at this path — anything still here\n // is necessarily hand-written. Preserve it rather than clobbering a\n // consumer's customized discovery route. Check every server-module\n // extension SvelteKit resolves by default (`.js` and `.ts` — see\n // https://svelte.dev/docs/kit/configuration#moduleExtensions), not only\n // `.ts`: a hand-written `+server.js` is invisible to a `.ts`-only check,\n // so the generator would write `+server.ts` alongside it and SvelteKit\n // would then refuse the route with \"Multiple endpoint files found\".\n const existingServerFile = ['+server.ts', '+server.js'].find((name) =>\n existsSync(join(routeDir, name)),\n );\n if (existingServerFile) {\n console.log(\n `[smrt] Preserving hand-written _resources route (${existingServerFile}, not generator-owned)`,\n );\n return false;\n }\n\n if (!existsSync(routeDir)) {\n mkdirSync(routeDir, { recursive: true });\n }\n writeFileSync(\n filePath,\n generateResourcesRouteTemplate(\n resolveSvelteKitConfigImport(projectRoot, routeDir, options),\n resolveSvelteKitConfigImport(projectRoot, routeDir, {\n configPath: options.configPath,\n configFileName: 'smrt-register.ts',\n }),\n options,\n ),\n 'utf-8',\n );\n console.log(`[smrt] Generated: ${filePath}`);\n return true;\n}\n\nfunction generateResourcesRouteTemplate(\n configImport: string,\n registerImport: string,\n options: SvelteKitOptions,\n): string {\n const kebabRoutesOption = options.kebabRoutes ? '\\n kebabRoutes: true,' : '';\n\n return `${AUTO_GENERATED_ROUTE_HEADER}\n// DO NOT EDIT - changes will be overwritten\n//\n// GET /api/_resources — CLI discovery endpoint (#2663). Returns the auth-aware\n// list of resources and commands the distributable CLI\n// (@happyvertical/smrt-app-cli) can invoke, derived from ObjectRegistry by\n// createResourceListHandler. Subsumes the route every consumer previously\n// hand-wrote (see the issue for the reference implementation this replaces).\n\nimport { createResourceListHandler } from '@happyvertical/smrt-users/sveltekit';\n// Side effect: registers @smrt() classes in ObjectRegistry before the\n// handler walks it (the generated registration, then the app's SMRT config\n// module, as the CRUD/changes/events routes import them).\nimport '${registerImport}';\nimport '${configImport}';\n\nexport const GET = createResourceListHandler({\n ensureRegistry: async () => {\n await import('${registerImport}');\n await import('${configImport}');\n },${kebabRoutesOption}\n});\n`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiGA,SAAgB,qBAAqB,aAA8B;CACjE,IAAI;EAIF,cAFE,cAAc,KAAK,aAAa,cAAc,CAAC,CAAC,CAAC,IAEnD,CAAA,CAAgB,QAAQ,qCAAqC;EAC7D,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,SAAgB,2BACd,aACA,SACS;CACT,IACE,QAAQ,gBAAgB,YAAY,SACpC,CAAC,qBAAqB,WAAW,GAEjC,OAAO;CAET,MAAM,WAAW,KAAK,aAAa,QAAQ,WAAW,YAAY;CAClE,KAAK,MAAM,QAAQ,CAAC,cAAc,YAAY,GAAG;EAC/C,MAAM,OAAO,KAAK,UAAU,IAAI;EAChC,IAAI,CAAC,WAAW,IAAI,GAAG;EACvB,IAAI,CAAC,aAAa,MAAM,OAAO,CAAC,CAAC,WAAA,6CAAsC,GACrE,OAAO;CAEX;CACA,OAAO;AACT;;;;AAKA,SAAgB,uBACd,aACA,SACS;CACT,IAAI,2BAA2B,aAAa,OAAO,GAAG,OAAO;CAC7D,MAAM,WAAW,KAAK,aAAa,QAAQ,WAAW,YAAY;CAClE,OAAO,CAAC,cAAc,YAAY,CAAC,CAAC,MAAM,SAAS;EACjD,MAAM,OAAO,KAAK,UAAU,IAAI;EAChC,OACE,WAAW,IAAI,KACf,CAAC,aAAa,MAAM,OAAO,CAAC,CAAC,WAAA,6CAAsC;CAEvE,CAAC;AACH;;;;;;;;AASA,SAAgB,uBACd,aACA,SACS;CACT,IAAI,QAAQ,gBAAgB,YAAY,OACtC,OAAO;CAGT,IAAI,CAAC,qBAAqB,WAAW,GAAG;EACtC,QAAQ,IACN,mJAGF;EACA,OAAO;CACT;CAEA,MAAM,WAAW,KAAK,aAAa,QAAQ,WAAW,YAAY;CAClE,MAAM,WAAW,KAAK,UAAU,YAAY;CAW5C,MAAM,qBAAqB,CAAC,cAAc,YAAY,CAAC,CAAC,MAAM,SAC5D,WAAW,KAAK,UAAU,IAAI,CAAC,CACjC;CACA,IAAI,oBAAoB;EACtB,QAAQ,IACN,oDAAoD,mBAAmB,uBACzE;EACA,OAAO;CACT;CAEA,IAAI,CAAC,WAAW,QAAQ,GACtB,UAAU,UAAU,EAAE,WAAW,KAAK,CAAC;CAEzC,cACE,UACA,+BACE,6BAA6B,aAAa,UAAU,OAAO,GAC3D,6BAA6B,aAAa,UAAU;EAClD,YAAY,QAAQ;EACpB,gBAAgB;CAClB,CAAC,GACD,OACF,GACA,OACF;CACA,QAAQ,IAAI,qBAAqB,UAAU;CAC3C,OAAO;AACT;AAEA,SAAS,+BACP,cACA,gBACA,SACQ;CAGR,OAAO,GAAG,4BAA4B;;;;;;;;;;;;;UAa9B,eAAe;UACf,aAAa;;;;oBAIH,eAAe;oBACf,aAAa;MArBL,QAAQ,cAAc,2BAA2B,GAsBrD;;;AAGxB"}
1
+ {"version":3,"file":"resources-route.js","names":[],"sources":["../../src/vite-plugin/resources-route.ts"],"sourcesContent":["/**\n * SvelteKit `_resources` route generation — CLI discovery endpoint (#2663;\n * parent epic #2655).\n *\n * Emits `{routesDir}/_resources/+server.ts`: `GET /api/_resources`, the\n * endpoint the distributable CLI (`@happyvertical/smrt-app-cli`) reads to\n * learn which resources and commands it can invoke. Every other generated\n * route lives in `sveltekit-generator.ts` or a sibling emitter; this one is\n * kept in its own module for the same reason `changes-route.ts` and\n * `events-route.ts` are — a one-line registration hook in the shared\n * generator file.\n *\n * ## The dependency constraint\n *\n * The handler this route delegates to, `createResourceListHandler`, lives in\n * `@happyvertical/smrt-users` — and `smrt-users` depends on `smrt-core`, not\n * the reverse, so core cannot import it directly. That is not a problem for\n * *emitting* the import: the generated file is written into the consumer's\n * own `src/routes/`, where `@happyvertical/smrt-users` resolves through the\n * consumer's own dependency graph, exactly like the `@happyvertical/smrt-tenancy`\n * import `sveltekit-generator.ts`'s `generateTenantContextHelper` already\n * emits into tenant-scoped routes (see the comment above that function). The\n * only question is *when* it is safe to emit — a consumer that never took a\n * dependency on `smrt-users` must never receive a route whose import cannot\n * resolve, or a working build turns red.\n *\n * ## Detection mechanism\n *\n * Unlike the tenancy guard — which reads a semantic signal already present in\n * the manifest (whether any object is `@TenantScoped`, which itself implies\n * the app depends on `smrt-tenancy`) — nothing in the manifest indicates\n * whether a consumer has taken on `smrt-users`. Objects don't declare an\n * auth dependency; the app does, at the package level.\n *\n * So this route uses a **resolvable-dependency check**: does the exact\n * subpath the generated file imports — `@happyvertical/smrt-users/sveltekit`\n * — resolve under ESM import conditions from the consumer's `projectRoot`,\n * anchored to its own `package.json` URL? That is stronger than reading\n * `package.json`'s `dependencies` block (which can list a package that was\n * never installed) and, unlike a single hardcoded\n * `node_modules/@happyvertical/smrt-users/package.json` path, it uses\n * Node's real module resolution — so it also finds the package when a\n * workspace hoists shared dependencies to an ancestor `node_modules` (npm/\n * yarn workspaces, or pnpm with `node-linker=hoisted`) rather than\n * installing them directly under the consumer's own `projectRoot`.\n * Resolving the `/sveltekit` subpath specifically, rather than the\n * package's bare `package.json`, matters too: `smrt-users`'s `exports` map\n * does not list `./package.json`, so resolving that path would throw\n * `ERR_PACKAGE_PATH_NOT_EXPORTED` even when the package is genuinely\n * installed — resolving the subpath the route actually imports sidesteps\n * that and checks precisely the thing that must succeed at build time. Use\n * import conditions because the published subpath may expose only `types` and\n * `import`, without a CommonJS `require` or `default` branch. The ESM resolver\n * can return a URL for a missing file or directory, so verify that the selected\n * target is an existing file before emitting a route that imports it.\n *\n * An explicit `sveltekit: { resourcesRoute: { enabled: false } }` escape\n * hatch is also available, matching `changesRoute`/`eventsRoute`, for a\n * consumer that has `smrt-users` installed but wants to keep hand-writing\n * (or omit) `_resources`.\n *\n * ## Preserving a hand-written route\n *\n * Every consumer today hand-writes this route (the reference implementation\n * in the issue). `clearGeneratedRouteFiles` (in `sveltekit-generator.ts`)\n * already only deletes `+server.ts` files that start with\n * {@link AUTO_GENERATED_ROUTE_HEADER}, so a hand-written `_resources` route —\n * which never carries that header — survives the pre-generation sweep\n * untouched. This emitter additionally checks `existsSync` on the target\n * path before writing: by the time it runs, anything still there is\n * necessarily hand-written (a generator-owned copy would already have been\n * swept), so its mere presence is sufficient to skip generation and leave it\n * alone.\n *\n * Once a consumer's `_resources` route IS generator-owned (no hand-written\n * file present, `smrt-users` resolvable), its path is generator-owned for\n * `.gitignore` purposes too — it is included in `generatedRoutePaths` like\n * any other emitted route, so `updateGitignore` puts it in the managed\n * block. A project that migrated from a hand-written route to a fully\n * generated one may still carry a stale `# Application-owned rule` comment\n * above the same literal path from before the migration; `updateGitignore`\n * only ever touches its own managed block, so that stale comment is left as\n * project history for the consumer to clean up — the same way any other\n * manual `.gitignore` edit outside the managed block is preserved.\n */\n\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n statSync,\n writeFileSync,\n} from 'node:fs';\nimport { join } from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { resolve as resolveImport } from 'import-meta-resolve';\nimport { AUTO_GENERATED_ROUTE_HEADER } from './route-header.js';\nimport { resolveSvelteKitConfigImport } from './sveltekit-config-import.js';\nimport type { SvelteKitOptions } from './sveltekit-generator.js';\n\n/**\n * Whether `@happyvertical/smrt-users/sveltekit` — the exact subpath the\n * generated route imports — is resolvable from the consumer at\n * `projectRoot`. See the module doc for why this check (rather than reading\n * `package.json`'s declared `dependencies`, or checking one hardcoded\n * physical path) is the chosen signal.\n */\nexport function consumerHasSmrtUsers(projectRoot: string): boolean {\n try {\n const target = resolveImport(\n '@happyvertical/smrt-users/sveltekit',\n pathToFileURL(join(projectRoot, 'package.json')).href,\n );\n return statSync(fileURLToPath(target)).isFile();\n } catch {\n return false;\n }\n}\n\n/** Whether this invocation will own the `_resources` output file. */\nexport function willGenerateResourcesRoute(\n projectRoot: string,\n options: SvelteKitOptions,\n): boolean {\n if (\n options.resourcesRoute?.enabled === false ||\n !consumerHasSmrtUsers(projectRoot)\n ) {\n return false;\n }\n const routeDir = join(projectRoot, options.routesDir, '_resources');\n for (const name of ['+server.ts', '+server.js']) {\n const file = join(routeDir, name);\n if (!existsSync(file)) continue;\n if (!readFileSync(file, 'utf-8').startsWith(AUTO_GENERATED_ROUTE_HEADER)) {\n return false;\n }\n }\n return true;\n}\n\n/** Whether `_resources` is occupied by its generated utility or a preserved\n * handwritten SvelteKit server module. This is used only by route collision\n * preflight: the emitter remains file-aware and never overwrites handwriting. */\nexport function reservesResourcesRoute(\n projectRoot: string,\n options: SvelteKitOptions,\n): boolean {\n if (willGenerateResourcesRoute(projectRoot, options)) return true;\n const routeDir = join(projectRoot, options.routesDir, '_resources');\n return ['+server.ts', '+server.js'].some((name) => {\n const file = join(routeDir, name);\n return (\n existsSync(file) &&\n !readFileSync(file, 'utf-8').startsWith(AUTO_GENERATED_ROUTE_HEADER)\n );\n });\n}\n\n/**\n * Generate the `_resources/+server.ts` route. Returns true when a route was\n * written. Disabled with `sveltekit: { resourcesRoute: { enabled: false } }`;\n * skipped (with a log line) when `@happyvertical/smrt-users` is not\n * resolvable from the consumer, or when a hand-written route already\n * occupies the path.\n */\nexport function generateResourcesRoute(\n projectRoot: string,\n options: SvelteKitOptions,\n): boolean {\n if (options.resourcesRoute?.enabled === false) {\n return false;\n }\n\n if (!consumerHasSmrtUsers(projectRoot)) {\n console.log(\n '[smrt] Skipping _resources route - @happyvertical/smrt-users is not ' +\n 'resolvable; createResourceListHandler would not resolve in the ' +\n 'consumer build',\n );\n return false;\n }\n\n const routeDir = join(projectRoot, options.routesDir, '_resources');\n const filePath = join(routeDir, '+server.ts');\n\n // By this point in generation, clearGeneratedRouteFiles has already swept\n // any previously generator-owned copy at this path — anything still here\n // is necessarily hand-written. Preserve it rather than clobbering a\n // consumer's customized discovery route. Check every server-module\n // extension SvelteKit resolves by default (`.js` and `.ts` — see\n // https://svelte.dev/docs/kit/configuration#moduleExtensions), not only\n // `.ts`: a hand-written `+server.js` is invisible to a `.ts`-only check,\n // so the generator would write `+server.ts` alongside it and SvelteKit\n // would then refuse the route with \"Multiple endpoint files found\".\n const existingServerFile = ['+server.ts', '+server.js'].find((name) =>\n existsSync(join(routeDir, name)),\n );\n if (existingServerFile) {\n console.log(\n `[smrt] Preserving hand-written _resources route (${existingServerFile}, not generator-owned)`,\n );\n return false;\n }\n\n if (!existsSync(routeDir)) {\n mkdirSync(routeDir, { recursive: true });\n }\n writeFileSync(\n filePath,\n generateResourcesRouteTemplate(\n resolveSvelteKitConfigImport(projectRoot, routeDir, options),\n resolveSvelteKitConfigImport(projectRoot, routeDir, {\n configPath: options.configPath,\n configFileName: 'smrt-register.ts',\n }),\n options,\n ),\n 'utf-8',\n );\n console.log(`[smrt] Generated: ${filePath}`);\n return true;\n}\n\nfunction generateResourcesRouteTemplate(\n configImport: string,\n registerImport: string,\n options: SvelteKitOptions,\n): string {\n const kebabRoutesOption = options.kebabRoutes ? '\\n kebabRoutes: true,' : '';\n\n return `${AUTO_GENERATED_ROUTE_HEADER}\n// DO NOT EDIT - changes will be overwritten\n//\n// GET /api/_resources — CLI discovery endpoint (#2663). Returns the auth-aware\n// list of resources and commands the distributable CLI\n// (@happyvertical/smrt-app-cli) can invoke, derived from ObjectRegistry by\n// createResourceListHandler. Subsumes the route every consumer previously\n// hand-wrote (see the issue for the reference implementation this replaces).\n\nimport { createResourceListHandler } from '@happyvertical/smrt-users/sveltekit';\n// Side effect: registers @smrt() classes in ObjectRegistry before the\n// handler walks it (the generated registration, then the app's SMRT config\n// module, as the CRUD/changes/events routes import them).\nimport '${registerImport}';\nimport '${configImport}';\n\nexport const GET = createResourceListHandler({\n ensureRegistry: async () => {\n await import('${registerImport}');\n await import('${configImport}');\n },${kebabRoutesOption}\n});\n`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2GA,SAAgB,qBAAqB,aAA8B;CACjE,IAAI;EAKF,OAAO,SAAS,cAJD,UACb,uCACA,cAAc,KAAK,aAAa,cAAc,CAAC,CAAC,CAAC,IAErB,CAAM,CAAC,CAAC,CAAC,OAAO;CAChD,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,SAAgB,2BACd,aACA,SACS;CACT,IACE,QAAQ,gBAAgB,YAAY,SACpC,CAAC,qBAAqB,WAAW,GAEjC,OAAO;CAET,MAAM,WAAW,KAAK,aAAa,QAAQ,WAAW,YAAY;CAClE,KAAK,MAAM,QAAQ,CAAC,cAAc,YAAY,GAAG;EAC/C,MAAM,OAAO,KAAK,UAAU,IAAI;EAChC,IAAI,CAAC,WAAW,IAAI,GAAG;EACvB,IAAI,CAAC,aAAa,MAAM,OAAO,CAAC,CAAC,WAAA,6CAAsC,GACrE,OAAO;CAEX;CACA,OAAO;AACT;;;;AAKA,SAAgB,uBACd,aACA,SACS;CACT,IAAI,2BAA2B,aAAa,OAAO,GAAG,OAAO;CAC7D,MAAM,WAAW,KAAK,aAAa,QAAQ,WAAW,YAAY;CAClE,OAAO,CAAC,cAAc,YAAY,CAAC,CAAC,MAAM,SAAS;EACjD,MAAM,OAAO,KAAK,UAAU,IAAI;EAChC,OACE,WAAW,IAAI,KACf,CAAC,aAAa,MAAM,OAAO,CAAC,CAAC,WAAA,6CAAsC;CAEvE,CAAC;AACH;;;;;;;;AASA,SAAgB,uBACd,aACA,SACS;CACT,IAAI,QAAQ,gBAAgB,YAAY,OACtC,OAAO;CAGT,IAAI,CAAC,qBAAqB,WAAW,GAAG;EACtC,QAAQ,IACN,mJAGF;EACA,OAAO;CACT;CAEA,MAAM,WAAW,KAAK,aAAa,QAAQ,WAAW,YAAY;CAClE,MAAM,WAAW,KAAK,UAAU,YAAY;CAW5C,MAAM,qBAAqB,CAAC,cAAc,YAAY,CAAC,CAAC,MAAM,SAC5D,WAAW,KAAK,UAAU,IAAI,CAAC,CACjC;CACA,IAAI,oBAAoB;EACtB,QAAQ,IACN,oDAAoD,mBAAmB,uBACzE;EACA,OAAO;CACT;CAEA,IAAI,CAAC,WAAW,QAAQ,GACtB,UAAU,UAAU,EAAE,WAAW,KAAK,CAAC;CAEzC,cACE,UACA,+BACE,6BAA6B,aAAa,UAAU,OAAO,GAC3D,6BAA6B,aAAa,UAAU;EAClD,YAAY,QAAQ;EACpB,gBAAgB;CAClB,CAAC,GACD,OACF,GACA,OACF;CACA,QAAQ,IAAI,qBAAqB,UAAU;CAC3C,OAAO;AACT;AAEA,SAAS,+BACP,cACA,gBACA,SACQ;CAGR,OAAO,GAAG,4BAA4B;;;;;;;;;;;;;UAa9B,eAAe;UACf,aAAa;;;;oBAIH,eAAe;oBACf,aAAa;MArBL,QAAQ,cAAc,2BAA2B,GAsBrD;;;AAGxB"}
@@ -1 +1 @@
1
- {"version":3,"file":"sveltekit-generator.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/sveltekit-generator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sveltekit-generator.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/sveltekit-generator.ts"],"names":[],"mappings":"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"}
@@ -13,9 +13,9 @@ import { canonicalSvelteKitPath } from "./sveltekit-path.js";
13
13
  import { collectSyncApplyTargets, generateSyncApplyRoute } from "./sync-apply-route.js";
14
14
  import { computeWebManifestHash, findManifestObjectByName, isCollectionManifestClass, orderedManifestObjectEntries, resolveCollectionItemObject, resolveCollectionItemTypeName } from "./web-collections.js";
15
15
  import { isAbsolute, join, relative, resolve, sep } from "node:path";
16
- import { pathToFileURL } from "node:url";
16
+ import { fileURLToPath, pathToFileURL } from "node:url";
17
+ import { resolve as resolve$1 } from "import-meta-resolve";
17
18
  import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
18
- import { createRequire } from "node:module";
19
19
  //#region src/vite-plugin/sveltekit-generator.ts
20
20
  /**
21
21
  * SvelteKit route auto-generation from SMRT objects
@@ -1229,12 +1229,12 @@ function formatRegistrationImport(importedObject) {
1229
1229
  }
1230
1230
  /**
1231
1231
  * Whether `@happyvertical/smrt-app-runtime/sveltekit` resolves from the
1232
- * consumer at `projectRoot` (same signal as `consumerHasSmrtUsers`).
1232
+ * consumer at `projectRoot` under ESM import conditions (same signal as
1233
+ * `consumerHasSmrtUsers`), with an existing file target.
1233
1234
  */
1234
1235
  function consumerHasSmrtAppRuntime(projectRoot) {
1235
1236
  try {
1236
- createRequire(pathToFileURL(join(projectRoot, "package.json")).href).resolve("@happyvertical/smrt-app-runtime/sveltekit");
1237
- return true;
1237
+ return statSync(fileURLToPath(resolve$1("@happyvertical/smrt-app-runtime/sveltekit", pathToFileURL(join(projectRoot, "package.json")).href))).isFile();
1238
1238
  } catch {
1239
1239
  return false;
1240
1240
  }