@happyvertical/smrt-core 0.57.8 → 0.57.10
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 +7 -1
- package/agents/build-knowledge.md +19 -3
- package/dist/knowledge.js +1 -0
- package/dist/knowledge.js.map +1 -1
- package/dist/manifest/browser-capability.generated.js +8 -1
- package/dist/manifest/browser-capability.generated.js.map +1 -1
- package/dist/manifest/static-manifest.js +1 -1
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest.json +1 -1
- package/dist/registry/class-registration.d.ts.map +1 -1
- package/dist/registry/class-registration.js +18 -2
- package/dist/registry/class-registration.js.map +1 -1
- package/dist/registry/inheritance-resolver.d.ts.map +1 -1
- package/dist/registry/inheritance-resolver.js +19 -1
- package/dist/registry/inheritance-resolver.js.map +1 -1
- package/dist/scanner/manifest-generator.d.ts +30 -0
- package/dist/scanner/manifest-generator.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.js +76 -30
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/schema/generator.d.ts.map +1 -1
- package/dist/schema/generator.js +1 -1
- package/dist/schema/generator.js.map +1 -1
- package/dist/smrt-knowledge.json +7 -7
- package/dist/vite-plugin/worker-registration.d.ts +5 -3
- package/dist/vite-plugin/worker-registration.d.ts.map +1 -1
- package/dist/vite-plugin/worker-registration.js +6 -3
- package/dist/vite-plugin/worker-registration.js.map +1 -1
- package/dist/vite-preset/index.d.ts +4 -3
- package/dist/vite-preset/index.d.ts.map +1 -1
- package/dist/vite-preset/index.js +21 -5
- package/dist/vite-preset/index.js.map +1 -1
- package/package.json +10 -10
package/dist/smrt-knowledge.json
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
"sensitiveFieldsExcluded": true,
|
|
4
4
|
"generatedAt": "1970-01-01T00:00:00.000Z",
|
|
5
5
|
"packageName": "@happyvertical/smrt-core",
|
|
6
|
-
"packageVersion": "0.57.
|
|
6
|
+
"packageVersion": "0.57.10",
|
|
7
7
|
"sourceManifestPath": "dist/manifest.json",
|
|
8
8
|
"agentDocPath": "AGENTS.md",
|
|
9
9
|
"sourceHashes": {
|
|
10
|
-
"manifest": "
|
|
11
|
-
"packageJson": "
|
|
12
|
-
"agents": "
|
|
10
|
+
"manifest": "3e772d5d004dd2b9828d66b016d31b6ef7b77963c290d2db75e9392b30161ae6",
|
|
11
|
+
"packageJson": "8189534266016058db87e21dbebc310ef8e7aa1ebdd950af837ecd193b3d10bc",
|
|
12
|
+
"agents": "3a78a0babd2884ac22e4b376a3c9618037ab818ad4bdd762751f82fa53c21d04",
|
|
13
13
|
"moduleDoc:agents/object-runtime.md": "fd2748649e820c2fe5960fa4a44c5f3e2a5afa57da89925e8b4cb3b53d67a07b",
|
|
14
14
|
"moduleDoc:agents/revision-guard.md": "f6ea32a6860964c98aba14b93587f40b33c8f7d0deccb2c54d21bff45db3da42",
|
|
15
15
|
"moduleDoc:agents/collection-reads.md": "39acea7faf3505d38bfb999e414f4c4c6f964ec7c3d19cc52993302c29223029",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"moduleDoc:agents/change-feed.md": "c3d6206758dd1d2e0475177e1938fb7d7b6cfe8a6c4d3c3b56ef62bc7bc7739d",
|
|
20
20
|
"moduleDoc:agents/change-signals.md": "a2dbe7077483b5a19bd5ea9589b5a08fe847377c8e05d01d57f813c6d9cbc28b",
|
|
21
21
|
"moduleDoc:agents/generators.md": "86822b7aa7cb254dcda1a378e1f41881eff48df0d99c5daac50b416a10f81c79",
|
|
22
|
-
"moduleDoc:agents/build-knowledge.md": "
|
|
22
|
+
"moduleDoc:agents/build-knowledge.md": "ebf431321e0014f24df4e8b8b6f3cfc4e38626e40d5e1e37cebe802766d97964",
|
|
23
23
|
"moduleDoc:agents/memory.md": "a86bdf370403d08ecad3be86e341eb322dfe6e6b7a4e8806a6f8be9bf3421da5",
|
|
24
24
|
"moduleDoc:agents/system-diagnostics.md": "b7ccae18ee312bf788d5470f95fa78d3535f1789247d5372e40ca86538290772",
|
|
25
25
|
"moduleDoc:agents/registry-snapshot.md": "6234aa540427396d1dda1825a4641b1072e247187d5ddf413ae46c691344ee30",
|
|
@@ -1147,7 +1147,7 @@
|
|
|
1147
1147
|
"polymorphicAssociations": 1,
|
|
1148
1148
|
"uuidColumns": 3
|
|
1149
1149
|
},
|
|
1150
|
-
"agentDoc": "# @happyvertical/smrt-core\n\nFoundation ORM, registry, schema/code generation, AI integration, and DispatchBus.\nRead the module for the subsystem being edited; root AGENTS covers shared model\nand repository rules.\n\n## Modules\n\n| Source | Scope | Module doc |\n|---|---|---|\n| `src/object.ts`, `src/collection.ts`, `src/child-accessors.ts` | Lifecycle, hydration, operators, STI, child accessors, dispatch | [agents/object-runtime.md](agents/object-runtime.md) |\n| `src/revision-guard.ts`, `src/unchanged-indexed.ts`, `src/native-duckdb.ts` | Guarded writes, PostgreSQL revision precision, unchanged indexed columns on referenced DuckDB rows (#3737) | [agents/revision-guard.md](agents/revision-guard.md) |\n| `src/collection.ts` | Projections, latest-related, facets, counts, read plans | [agents/collection-reads.md](agents/collection-reads.md) |\n| `src/collection.ts` | Limits, sort whitelist, generated list order | [agents/query-bounds.md](agents/query-bounds.md) |\n| `src/data-query.ts` | Transport-neutral bounded query normalization | [agents/data-query.md](agents/data-query.md) |\n| `src/schema/`, `src/migrations/`, `src/cascade.ts`, `src/system/` | DDL parity, indexes, migrations, delete integrity, retention | [agents/schema-paths.md](agents/schema-paths.md) |\n| `src/postgres-permissions.ts` | Explicit PostgreSQL ACL plans and atomic reconciliation | [deployment permission contract](../../docs/content/postgres-permissions.md) |\n| `src/change-feed.ts` | Durable changes, cursors, table versions, retention | [agents/change-feed.md](agents/change-feed.md) |\n| `src/change-feed-authz.ts` | Consumer table/row change-feed authorization hooks (#3020) | [agents/change-feed.md](agents/change-feed.md) |\n| `src/change-signals.ts` | Signal bus, replica fan-out, SSE | [agents/change-signals.md](agents/change-signals.md) |\n| `src/generators/`, `src/vite-plugin/web-collections.ts` | REST/CLI/MCP generation, manifest hashes, ETags | [agents/generators.md](agents/generators.md) |\n| `src/vite-plugin/`, `src/consumer-plugin/`, `src/knowledge.ts` | Decorator UI hints, knowledge projection, generation snapshots | [agents/build-knowledge.md](agents/build-knowledge.md) |\n| `src/knowledge-graph.ts` | Cross-package knowledge graph merge and freshness check (#2863) — see [standards](../../docs/content/standards.md) | — |\n| `src/object.ts`, `src/collection.ts`, `src/learning/memory.ts` | Context memory and semantic search | [agents/memory.md](agents/memory.md) |\n| `src/system/diagnostics.ts` | SELECT-only `_smrt_*` diagnostics reader behind smrt-dev-mcp runtime tools (#1824) | [agents/system-diagnostics.md](agents/system-diagnostics.md) |\n| `src/system/registry-snapshot.ts` | Sanitized plain-JSON projection of the booted `ObjectRegistry` for the smrt-dev-mcp runtime dev-plane (#1831); never constructors, validators, values, or absolute paths | [agents/registry-snapshot.md](agents/registry-snapshot.md) |\n| `src/registry/qualified-name-aliases.ts`, `src/migrations/qualified-name-aliases.ts` | Model moves: `previousQualifiedNames` aliases, deprecation, doctor counts, opt-in backfill (#3338) | [agents/model-moves.md](agents/model-moves.md) |\n| `src/run-once.ts` | Shared idempotency seam: insert-only claim + caller work in one transaction, replay on retry, typed in-flight/unknown-outcome answers (#3080) | [agents/run-once.md](agents/run-once.md) |\n| `src/recipe.ts`, `../scanner/src/recipes.ts` | `SmrtRecipe`: declared user-facing units (models, nav, `requires`, curation `options`) collected into manifest and knowledge `recipes` (#3590) | [agents/recipes.md](agents/recipes.md) |\n| `src/cookbook/` | Cookbook v1 document: browser-safe validate/migrate, JSON Schema (`./cookbook`, `./cookbook/v1.schema.json`; #3748) | [agents/cookbook.md](agents/cookbook.md) |\n| `src/cookbook/engine/` | Headless cookbook command engine: typed commands, idempotent retries, atomic batches, receipts, tool schemas (`./cookbook/engine`; #3753) | [agents/cookbook-engine.md](agents/cookbook-engine.md) |\n| `src/browser.ts`, `src/host.ts`, `src/host.browser.ts` | Browser entry that builds and evaluates without Node-only modules: host seam, `package.json#browser` swaps, `pnpm test:browser` (#2838) | [agents/browser-entry.md](agents/browser-entry.md) |\n\n## Cross-module invariants\n\n- `ObjectRegistry` is a `globalThis` singleton so registration survives HMR.\n- Any output that names a database URL (CLI banners, logs, errors) renders it\n with `redactDatabaseUrl` / `redactDatabaseUrlsInText`\n (`src/utils/database-url.ts`, #3527): never the password or query string,\n and never the raw string when it cannot be parsed.\n- `SmrtError` lives in import-free `src/smrt-error.ts`, published as\n `./smrt-error` (#4100) for browser-safe code that must not load the root\n entry; `errors.ts` re-exports that one class. Keep it import-free.\n- Per-class registry work on the hydration path (manifest reconciliation in\n `ensureManifestLoaded()`, the simple-name index behind `findClass()`) is\n memoized per registry generation (`src/registry/generation.ts`, #3047). Any\n new writer of registered classes, their field/method metadata, or loaded\n manifests must go through the tracked maps or call\n `bumpRegistryGeneration()`; `issue-3047-hydration-registry-work.test.ts`\n guards that hydration does not repeat this work per row.\n\n- Production DDL uses manifest generators; `getTestDatabase()` uses registry\n generators. Keep columns, indexes, FK actions, and runtime conflict targets in\n parity (`src/schema/schema-path-parity.test.ts`). Every new query predicate\n needs its index or an explicit reason none is needed.\n- Tenant scoping covers every read and every unique/conflict key. Explicit\n conflict columns are not rewritten; their author must include tenant scope.\n A `tenantId` field makes rows tenant-OWNED without any tenancy declaration:\n the default key leads with `tenant_id`. A new object's natural-key save reads\n the row its upsert would hit and refuses a different owner\n (`TENANT_ISOLATION_VIOLATION`) or adopts a same-owner row's id, so\n `DO UPDATE` never rewrites a primary key or owner. Only an explicit natural\n key adopts: a slug `getSlug()` derived from `name`/`title`/`label` moves to\n a free `-2`, `-3`, … slug and INSERTs. See `agents/schema-paths.md`\n (\"Tenant OWNERSHIP\").\n- Owning `CollectionInterceptor.writeGuard` predicates must reach final writes,\n including natural-key adoption and deletes; zero matched rows fail closed.\n See [object runtime](agents/object-runtime.md).\n- Persisted saves use `id` and loaded `updated_at`; new saves use natural keys.\n Preserve revision compare-and-swap ordering through public `save()`,\n `claimRevision()`, and transaction APIs. Embedded saves, deletes, and complete\n `withTransaction()` callbacks share a process-local write queue.\n- Collection model hydration is serial in result order: initialization may query\n the same transaction-bound PostgreSQL client. Use projections for plain rows.\n- Native DuckDB UUIDs must be cast coherently on read before identity reuse;\n custom embedded revision paths use `getCanonicalPersistedRow()`. Never replay\n a mutation to discover result types.\n- `withDatabase(db, callback)` restores only database bindings (including public\n `options.db`); `withTransaction(callback)` also restores identity/revision\n metadata after rollback. Do not use a bound instance concurrently.\n- `runOnce({ db, tenantId, actor, token, content }, work)` (`src/run-once.ts`,\n #3080) claims a key derived from all four inputs — never the token alone,\n which collapses distinct submissions — via `INSERT ... ON CONFLICT\n (claim_key) DO NOTHING RETURNING claim_key` into the hand-DDL\n `_smrt_run_once_claims` system table, and runs `work` in the SAME\n transaction; a completed claim replays its stored result instead of\n re-running, a failed `work` rolls the claim back with the transaction, and\n an unresolved claim raises typed `RunOnceClaimError`\n (`RUN_ONCE_IN_FLIGHT` / `RUN_ONCE_OUTCOME_UNKNOWN`) rather than guessing.\n The claim insert must never raise on conflict: PostgreSQL aborts the whole\n transaction on any raised error, which would take the recovery `SELECT`\n down with it too — this is why it is `DO NOTHING`, not a plain `INSERT`\n caught for `isUniqueViolationError`. Completed claims are bounded by\n `runRetentionSweep()`'s `run-once-claims` task (`system/retention.ts`,\n `DEFAULT_RETENTION_POLICY.runOnceClaims`, default 30 days on\n `completed_at`; `in_progress` rows are never matched) — the window is a\n replay deadline, not just disk hygiene: a token replayed after its claim\n is swept runs `work()` again. See `agents/run-once.md`.\n- `ensureSystemTables(db, typeHint?)` provisions framework tables idempotently;\n call it on a base PostgreSQL connection before caller-owned transactions.\n Bootstrap uses a transaction-scoped advisory lock, taken only when the\n PostgreSQL schema version is not yet stamped: inside a caller's transaction it\n is held until that transaction ends (#2868). Application tables still require migrations;\n runtime table verification checks existence only.\n- Manifest generation fails closed on scanner errors, including unresolved\n decorator spreads. Never emit partial, default-open registration.\n- Generated registration repairs bundled class identity using the exact imported\n constructor, explicit package, and isolated one-object manifest. Never infer\n ownership from paths, simple names, or table names; packages can share names.\n Consumer regression gate: `packages/bundle-gate/src/__tests__/registry-identity.spec.ts`.\n- Decorator registration trusts a class's stack-derived package only when that\n package declares the class (stub, own manifest entry, or explicit\n `packageName`); it then matches the qualified key and never adopts another\n package's same-named registration. Otherwise a loaded (or the cwd project's)\n manifest entry describing the declaring file supplies the identity — apps\n scan workspace-package sources under the app's name (#3110). A class of\n another known package coexists under its qualified key\n (`decorator-different-packages-qualified-coexist`); bundled output needs\n a resolved table that differs first (#3106). Stack\n attribution skips smrt-core (including source-mapped installed\n `smrt-core/src` frames, #3109), decorator-helper, and module-runner frames,\n and drops a frame's Vite `?v=` query (#3490). Library builds stamp each\n decorated class with `static __smrtPackage__` (own property, read at\n decoration as its explicit package) and publish `stampsConstructors`; an\n unstamped class never takes a stamping package's entry by name and table,\n in either order (#3490, `issue-3490-*.test.ts`, `package-stamp.test.ts`).\n Qualified names are `@scope/pkg:Class`; the scope is a deliberate\n discriminator, so manifest generation (`generateManifest`,\n `applyGenerationPasses`, hence `pnpm build`) throws\n `CONFIG_UNSCOPED_PACKAGE_NAME` for an unscoped `package.json` name (#3408).\n Instance and collection registry lookups pass the qualified name. The schema\n planner rejects a table claimed by two unrelated classes\n (`CONFIG_TABLE_NAME_COLLISION`): only one STI family shares a table (#3098);\n collections (including `SmrtReportCollection`) never claim one. Consumer\n gates: `registry/__tests__/issue-3110-*.test.ts`, `issue-3109-*.test.ts`,\n `packages/bundle-gate/src/__tests__/consumer-same-name.spec.ts`.\n- A collection class never publishes more than its model: unset surfaces\n inherit the model's, and the build rejects a collection that opens a CRUD\n write its model closes. See `agents/generators.md` (Collection exposure).\n- A model subclass (STI child or CTI subclass) never publishes more than its\n parent model: unset surfaces and `sensitive` inherit the parent's, and the\n build rejects a subclass that widens them unless it names each operation in\n `widenParentExposure`. See `agents/generators.md` (Subclass exposure).\n- API custom-action eligibility has ONE resolver, `resolveApiMethodExposure()`\n in `generators/custom-action.ts`: both SvelteKit route emitters,\n `resolveApiActionSet`, and `knowledge.ts` read it, and a new consumer must too\n — a local mirror is how a method gets reported unavailable while its route\n file is still written. A public method routes by default only when every\n parameter is JSON-shaped; `@method({ expose })` overrides in both directions,\n `expose: true` bypasses the heuristic ALONE, and an explicit `api.include` or\n `api.routes` entry keeps its pre-#2686 route. Fail closed on scanner\n uncertainty: read `parameters[].typeUnresolved`, never the `'any'` it\n substitutes. Accepting `Date` as wire-able and hydrating it\n (`toCustomActionDate`) are one decision — changing either breaks the other.\n The runtime `APIGenerator` transport stays declaration-gated: `rest.ts`\n dispatch and `preflight-route.ts` prediction both read\n `declaresRuntimeRestRoute()`, which must accept every `ApiCustomRouteConfig`\n option so a sweep moving one onto its method cannot delete the endpoint. Its\n twin `declaresRuntimeRestRouteShape()` deliberately ignores `expose: false`:\n the dispatcher must still SEE a withheld declaration to answer 404, because\n `POST /<collection>/<segment>` resolves to `create` when nothing claims the\n segment. Split unions and type arguments with `splitTopLevel()` — a naive\n `split('|')` truncates `Record<string, Asset | null>` into fragments that\n match no rule and are then accepted, widening the gate. `extractTypeName`\n returns `null` for a generic with an unresolvable ARGUMENT or a union with an\n unresolvable BRANCH so those reach that fail-closed path instead of arriving\n as a bare `'Array'`. Runtime transports read\n `ObjectRegistry.resolveRuntimeMethod()`, which tries the item class's manifest\n entry, then the COLLECTION class's (where a collection-hosted action's\n parameters live), then the live `@method()` store — keyed by CONSTRUCTOR,\n never by simple name (`Account` exists in two packages), and recording\n `isStatic` because an unscanned runtime has no manifest to recover the\n receiver from. `isRestActionRoutable` ANDs \"declared\" with that receiver, so\n it never predicts `allow` for an action dispatch refuses. A declared action\n with no receiver is refused (501 collection-scoped, 404 item-scoped), never\n allowed to fall through into `create`. All eight consumers read the resolver,\n including `packages/smrt-workbench/src/discovery.ts`; it imports the\n `./generators/custom-action` LEAF subpath, not `./generators`, because that\n barrel value-re-exports `MCPGenerator` and so drags `@happyvertical/ai` and\n `@happyvertical/sql` into a build-time helper (2 modules vs 109). Keep\n `custom-action.ts` free of value imports outside `tools/tool-generator`.\n\n## Gotchas\n\n- Optional filesystem support stays lazy: use `createFilesystemAdapter()` in\n `src/filesystem-loader.ts`, not a static files-SDK import. Fully bundled apps\n import `@happyvertical/smrt-core/filesystem` at startup. Use\n `importOptionalDependency()` for similarly heavy optional dependencies.\n- `src/browser.ts` is the `browser`-condition entry of the package root: the\n Node entry (`index.ts`) minus HTTP servers, manifest/knowledge discovery, run-once,\n the Vite plugin, and the test helper. A new export added to `index.ts` must also\n go to `browser.ts` unless it is Node-only; `src/__tests__/browser-entry-parity.test.ts`\n fails otherwise (allowlist: `NODE_ONLY_GROUPS` in that test, each with a reason; the browser\n entry may not publish a name the Node entry lacks), and also\n fails when a workspace package imports a root value the browser entry lacks.\n- `exports['.'].browser` (`dist/browser.js`) is the browser surface. Model\n package roots are bundled for browsers by the bundle-gate browser fixture\n (#3621): anything a model root imports from core must be exported by the\n browser entry and must not reach `node:` built-ins, `pg`, `cosmiconfig`, or\n `jiti`. Keep Node-only code behind a subpath or a lazy import; when a fix\n makes a package pass, delete its entry in\n `packages/bundle-gate/src/browser-gate/expected-failures.ts`.\n- Database retries are transient-only, four attempts total for `get`/`upsert`.\n Use `src/db-errors.ts` classifiers through the cause chain, never message\n matching (SDK driver text can live in `context.originalError`). Constraints,\n bad input, missing tables, and aborted PostgreSQL transactions fail immediately.\n Unique/PK violations become `VALIDATION_UNIQUE_CONSTRAINT`, NOT NULL becomes\n `VALIDATION_REQUIRED_FIELD`; other failures keep the driver error as `cause`.\n- Property initializers precede option values; options win. Arrays/objects are\n shallow-cloned. Collection creation caches fields; table verification caches\n by DB URL and table. Preserve these scopes when changing initialization.\n- The Vite plugin loads scanner/schema code from `dist/` when present. Rebuild\n core after editing those sources before testing consumer manifest generation.\n Vite 8 requires `oxc.decorator: { legacy: true, emitDecoratorMetadata: true }`.\n\n## Validation\n\n`typecheck` checks production source first, then the test project in\n`tsconfig.test.json`. Test coverage and the shared relaxed config\ncontract are documented in [agents/test-types.md](agents/test-types.md).\n\nRun focused tests first, then applicable package checks:\n\n```bash\npnpm --filter @happyvertical/smrt-core test\npnpm --filter @happyvertical/smrt-core typecheck\npnpm --filter @happyvertical/smrt-core build\npnpm --filter @happyvertical/smrt-core test:postgres\npnpm check:agents-chain\npnpm smrt dev:knowledge-check\n```\n\nThe PostgreSQL lane is required for numeric types, UUID casts, conflict targets,\ntimestamps, and migrations. Tests generate their manifest before Vitest; restart\nwatch mode after adding decorated classes. Documentation-only changes need\ninstruction-chain and knowledge freshness checks, not the runtime test suite.\n",
|
|
1150
|
+
"agentDoc": "# @happyvertical/smrt-core\n\nFoundation ORM, registry, schema/code generation, AI integration, and DispatchBus.\nRead the module for the subsystem being edited; root AGENTS covers shared model\nand repository rules.\n\n## Modules\n\n| Source | Scope | Module doc |\n|---|---|---|\n| `src/object.ts`, `src/collection.ts`, `src/child-accessors.ts` | Lifecycle, hydration, operators, STI, child accessors, dispatch | [agents/object-runtime.md](agents/object-runtime.md) |\n| `src/revision-guard.ts`, `src/unchanged-indexed.ts`, `src/native-duckdb.ts` | Guarded writes, PostgreSQL revision precision, unchanged indexed columns on referenced DuckDB rows (#3737) | [agents/revision-guard.md](agents/revision-guard.md) |\n| `src/collection.ts` | Projections, latest-related, facets, counts, read plans | [agents/collection-reads.md](agents/collection-reads.md) |\n| `src/collection.ts` | Limits, sort whitelist, generated list order | [agents/query-bounds.md](agents/query-bounds.md) |\n| `src/data-query.ts` | Transport-neutral bounded query normalization | [agents/data-query.md](agents/data-query.md) |\n| `src/schema/`, `src/migrations/`, `src/cascade.ts`, `src/system/` | DDL parity, indexes, migrations, delete integrity, retention | [agents/schema-paths.md](agents/schema-paths.md) |\n| `src/postgres-permissions.ts` | Explicit PostgreSQL ACL plans and atomic reconciliation | [deployment permission contract](../../docs/content/postgres-permissions.md) |\n| `src/change-feed.ts` | Durable changes, cursors, table versions, retention | [agents/change-feed.md](agents/change-feed.md) |\n| `src/change-feed-authz.ts` | Consumer table/row change-feed authorization hooks (#3020) | [agents/change-feed.md](agents/change-feed.md) |\n| `src/change-signals.ts` | Signal bus, replica fan-out, SSE | [agents/change-signals.md](agents/change-signals.md) |\n| `src/generators/`, `src/vite-plugin/web-collections.ts` | REST/CLI/MCP generation, manifest hashes, ETags | [agents/generators.md](agents/generators.md) |\n| `src/vite-plugin/`, `src/consumer-plugin/`, `src/knowledge.ts` | Decorator UI hints, knowledge projection, generation snapshots | [agents/build-knowledge.md](agents/build-knowledge.md) |\n| `src/knowledge-graph.ts` | Cross-package knowledge graph merge and freshness check (#2863) — see [standards](../../docs/content/standards.md) | — |\n| `src/object.ts`, `src/collection.ts`, `src/learning/memory.ts` | Context memory and semantic search | [agents/memory.md](agents/memory.md) |\n| `src/system/diagnostics.ts` | SELECT-only `_smrt_*` diagnostics reader behind smrt-dev-mcp runtime tools (#1824) | [agents/system-diagnostics.md](agents/system-diagnostics.md) |\n| `src/system/registry-snapshot.ts` | Sanitized plain-JSON projection of the booted `ObjectRegistry` for the smrt-dev-mcp runtime dev-plane (#1831); never constructors, validators, values, or absolute paths | [agents/registry-snapshot.md](agents/registry-snapshot.md) |\n| `src/registry/qualified-name-aliases.ts`, `src/migrations/qualified-name-aliases.ts` | Model moves: `previousQualifiedNames` aliases, deprecation, doctor counts, opt-in backfill (#3338) | [agents/model-moves.md](agents/model-moves.md) |\n| `src/run-once.ts` | Shared idempotency seam: insert-only claim + caller work in one transaction, replay on retry, typed in-flight/unknown-outcome answers (#3080) | [agents/run-once.md](agents/run-once.md) |\n| `src/recipe.ts`, `../scanner/src/recipes.ts` | `SmrtRecipe`: declared user-facing units (models, nav, `requires`, curation `options`) collected into manifest and knowledge `recipes` (#3590) | [agents/recipes.md](agents/recipes.md) |\n| `src/cookbook/` | Cookbook v1 document: browser-safe validate/migrate, JSON Schema (`./cookbook`, `./cookbook/v1.schema.json`; #3748) | [agents/cookbook.md](agents/cookbook.md) |\n| `src/cookbook/engine/` | Headless cookbook command engine: typed commands, idempotent retries, atomic batches, receipts, tool schemas (`./cookbook/engine`; #3753) | [agents/cookbook-engine.md](agents/cookbook-engine.md) |\n| `src/browser.ts`, `src/host.ts`, `src/host.browser.ts` | Browser entry that builds and evaluates without Node-only modules: host seam, `package.json#browser` swaps, `pnpm test:browser` (#2838) | [agents/browser-entry.md](agents/browser-entry.md) |\n\n## Cross-module invariants\n\n- `ObjectRegistry` is a `globalThis` singleton so registration survives HMR.\n- Any output that names a database URL (CLI banners, logs, errors) renders it\n with `redactDatabaseUrl` / `redactDatabaseUrlsInText`\n (`src/utils/database-url.ts`, #3527): never the password or query string,\n and never the raw string when it cannot be parsed.\n- `SmrtError` lives in import-free `src/smrt-error.ts`, published as\n `./smrt-error` (#4100) for browser-safe code that must not load the root\n entry; `errors.ts` re-exports that one class. Keep it import-free.\n- Per-class registry work on the hydration path (manifest reconciliation in\n `ensureManifestLoaded()`, the simple-name index behind `findClass()`) is\n memoized per registry generation (`src/registry/generation.ts`, #3047). Any\n new writer of registered classes, their field/method metadata, or loaded\n manifests must go through the tracked maps or call\n `bumpRegistryGeneration()`; `issue-3047-hydration-registry-work.test.ts`\n guards that hydration does not repeat this work per row.\n\n- Production DDL uses manifest generators; `getTestDatabase()` uses registry\n generators. Keep columns, indexes, FK actions, and runtime conflict targets in\n parity (`src/schema/schema-path-parity.test.ts`). Every new query predicate\n needs its index or an explicit reason none is needed.\n- Tenant scoping covers every read and every unique/conflict key. Explicit\n conflict columns are not rewritten; their author must include tenant scope.\n A `tenantId` field makes rows tenant-OWNED without any tenancy declaration:\n the default key leads with `tenant_id`. A new object's natural-key save reads\n the row its upsert would hit and refuses a different owner\n (`TENANT_ISOLATION_VIOLATION`) or adopts a same-owner row's id, so\n `DO UPDATE` never rewrites a primary key or owner. Only an explicit natural\n key adopts: a slug `getSlug()` derived from `name`/`title`/`label` moves to\n a free `-2`, `-3`, … slug and INSERTs. See `agents/schema-paths.md`\n (\"Tenant OWNERSHIP\").\n- Owning `CollectionInterceptor.writeGuard` predicates must reach final writes,\n including natural-key adoption and deletes; zero matched rows fail closed.\n See [object runtime](agents/object-runtime.md).\n- Persisted saves use `id` and loaded `updated_at`; new saves use natural keys.\n Preserve revision compare-and-swap ordering through public `save()`,\n `claimRevision()`, and transaction APIs. Embedded saves, deletes, and complete\n `withTransaction()` callbacks share a process-local write queue.\n- Collection model hydration is serial in result order: initialization may query\n the same transaction-bound PostgreSQL client. Use projections for plain rows.\n- Native DuckDB UUIDs must be cast coherently on read before identity reuse;\n custom embedded revision paths use `getCanonicalPersistedRow()`. Never replay\n a mutation to discover result types.\n- `withDatabase(db, callback)` restores only database bindings (including public\n `options.db`); `withTransaction(callback)` also restores identity/revision\n metadata after rollback. Do not use a bound instance concurrently.\n- `runOnce({ db, tenantId, actor, token, content }, work)` (`src/run-once.ts`,\n #3080) claims a key derived from all four inputs — never the token alone,\n which collapses distinct submissions — via `INSERT ... ON CONFLICT\n (claim_key) DO NOTHING RETURNING claim_key` into the hand-DDL\n `_smrt_run_once_claims` system table, and runs `work` in the SAME\n transaction; a completed claim replays its stored result instead of\n re-running, a failed `work` rolls the claim back with the transaction, and\n an unresolved claim raises typed `RunOnceClaimError`\n (`RUN_ONCE_IN_FLIGHT` / `RUN_ONCE_OUTCOME_UNKNOWN`) rather than guessing.\n The claim insert must never raise on conflict: PostgreSQL aborts the whole\n transaction on any raised error, which would take the recovery `SELECT`\n down with it too — this is why it is `DO NOTHING`, not a plain `INSERT`\n caught for `isUniqueViolationError`. Completed claims are bounded by\n `runRetentionSweep()`'s `run-once-claims` task (`system/retention.ts`,\n `DEFAULT_RETENTION_POLICY.runOnceClaims`, default 30 days on\n `completed_at`; `in_progress` rows are never matched) — the window is a\n replay deadline, not just disk hygiene: a token replayed after its claim\n is swept runs `work()` again. See `agents/run-once.md`.\n- `ensureSystemTables(db, typeHint?)` provisions framework tables idempotently;\n call it on a base PostgreSQL connection before caller-owned transactions.\n Bootstrap uses a transaction-scoped advisory lock, taken only when the\n PostgreSQL schema version is not yet stamped: inside a caller's transaction it\n is held until that transaction ends (#2868). Application tables still require migrations;\n runtime table verification checks existence only.\n- Manifest generation fails closed on scanner errors, including unresolved\n decorator spreads. Never emit partial, default-open registration.\n- Generated registration repairs bundled class identity using the exact imported\n constructor, explicit package, and isolated one-object manifest. Never infer\n ownership from paths, simple names, or table names; packages can share names.\n Consumer regression gate: `packages/bundle-gate/src/__tests__/registry-identity.spec.ts`.\n- Decorator registration trusts a class's stack-derived package only when that\n package declares the class (stub, own manifest entry, or explicit\n `packageName`); it then matches the qualified key and never adopts another\n package's same-named registration. Otherwise a loaded (or the cwd project's)\n manifest entry describing the declaring file supplies the identity — apps\n scan workspace-package sources under the app's name (#3110). A class of\n another known package coexists under its qualified key\n (`decorator-different-packages-qualified-coexist`); bundled output needs\n a resolved table that differs first (#3106). Stack\n attribution skips smrt-core (including source-mapped installed\n `smrt-core/src` frames, #3109), decorator-helper, and module-runner frames,\n and drops a frame's Vite `?v=` query (#3490). Library builds stamp each\n decorated class with `static __smrtPackage__` (own property, read at\n decoration as its explicit package) and publish `stampsConstructors`; an\n unstamped class never takes a stamping package's entry by name and table,\n in either order (#3490, `issue-3490-*.test.ts`, `package-stamp.test.ts`).\n Qualified names are `@scope/pkg:Class`; the scope is a deliberate\n discriminator, so manifest generation (`generateManifest`,\n `applyGenerationPasses`, hence `pnpm build`) throws\n `CONFIG_UNSCOPED_PACKAGE_NAME` for an unscoped `package.json` name (#3408).\n Instance and collection registry lookups pass the qualified name. A parent\n imported from another scoped package is emitted as `extendsQualified`\n (scanner traces named, aliased, and namespace imports; `extends` stays the\n simple name), kept only when that package's manifest declares it\n (`verifyImportedParents`); decorator registration takes the parent from the\n real constructor's registration, which a stale simple manifest parent never\n overwrites (#4145, `issue-4145-*.test.ts`). The schema\n planner rejects a table claimed by two unrelated classes\n (`CONFIG_TABLE_NAME_COLLISION`): only one STI family shares a table (#3098);\n collections (including `SmrtReportCollection`) never claim one. Consumer\n gates: `registry/__tests__/issue-3110-*.test.ts`, `issue-3109-*.test.ts`,\n `packages/bundle-gate/src/__tests__/consumer-same-name.spec.ts`.\n- A collection class never publishes more than its model: unset surfaces\n inherit the model's, and the build rejects a collection that opens a CRUD\n write its model closes. See `agents/generators.md` (Collection exposure).\n- A model subclass (STI child or CTI subclass) never publishes more than its\n parent model: unset surfaces and `sensitive` inherit the parent's, and the\n build rejects a subclass that widens them unless it names each operation in\n `widenParentExposure`. See `agents/generators.md` (Subclass exposure).\n- API custom-action eligibility has ONE resolver, `resolveApiMethodExposure()`\n in `generators/custom-action.ts`: both SvelteKit route emitters,\n `resolveApiActionSet`, and `knowledge.ts` read it, and a new consumer must too\n — a local mirror is how a method gets reported unavailable while its route\n file is still written. A public method routes by default only when every\n parameter is JSON-shaped; `@method({ expose })` overrides in both directions,\n `expose: true` bypasses the heuristic ALONE, and an explicit `api.include` or\n `api.routes` entry keeps its pre-#2686 route. Fail closed on scanner\n uncertainty: read `parameters[].typeUnresolved`, never the `'any'` it\n substitutes. Accepting `Date` as wire-able and hydrating it\n (`toCustomActionDate`) are one decision — changing either breaks the other.\n The runtime `APIGenerator` transport stays declaration-gated: `rest.ts`\n dispatch and `preflight-route.ts` prediction both read\n `declaresRuntimeRestRoute()`, which must accept every `ApiCustomRouteConfig`\n option so a sweep moving one onto its method cannot delete the endpoint. Its\n twin `declaresRuntimeRestRouteShape()` deliberately ignores `expose: false`:\n the dispatcher must still SEE a withheld declaration to answer 404, because\n `POST /<collection>/<segment>` resolves to `create` when nothing claims the\n segment. Split unions and type arguments with `splitTopLevel()` — a naive\n `split('|')` truncates `Record<string, Asset | null>` into fragments that\n match no rule and are then accepted, widening the gate. `extractTypeName`\n returns `null` for a generic with an unresolvable ARGUMENT or a union with an\n unresolvable BRANCH so those reach that fail-closed path instead of arriving\n as a bare `'Array'`. Runtime transports read\n `ObjectRegistry.resolveRuntimeMethod()`, which tries the item class's manifest\n entry, then the COLLECTION class's (where a collection-hosted action's\n parameters live), then the live `@method()` store — keyed by CONSTRUCTOR,\n never by simple name (`Account` exists in two packages), and recording\n `isStatic` because an unscanned runtime has no manifest to recover the\n receiver from. `isRestActionRoutable` ANDs \"declared\" with that receiver, so\n it never predicts `allow` for an action dispatch refuses. A declared action\n with no receiver is refused (501 collection-scoped, 404 item-scoped), never\n allowed to fall through into `create`. All eight consumers read the resolver,\n including `packages/smrt-workbench/src/discovery.ts`; it imports the\n `./generators/custom-action` LEAF subpath, not `./generators`, because that\n barrel value-re-exports `MCPGenerator` and so drags `@happyvertical/ai` and\n `@happyvertical/sql` into a build-time helper (2 modules vs 109). Keep\n `custom-action.ts` free of value imports outside `tools/tool-generator`.\n\n## Gotchas\n\n- Optional filesystem support stays lazy: use `createFilesystemAdapter()` in\n `src/filesystem-loader.ts`, not a static files-SDK import. Fully bundled apps\n import `@happyvertical/smrt-core/filesystem` at startup. Use\n `importOptionalDependency()` for similarly heavy optional dependencies.\n- `src/browser.ts` is the `browser`-condition entry of the package root: the\n Node entry (`index.ts`) minus HTTP servers, manifest/knowledge discovery, run-once,\n the Vite plugin, and the test helper. A new export added to `index.ts` must also\n go to `browser.ts` unless it is Node-only; `src/__tests__/browser-entry-parity.test.ts`\n fails otherwise (allowlist: `NODE_ONLY_GROUPS` in that test, each with a reason; the browser\n entry may not publish a name the Node entry lacks), and also\n fails when a workspace package imports a root value the browser entry lacks.\n- `exports['.'].browser` (`dist/browser.js`) is the browser surface. Model\n package roots are bundled for browsers by the bundle-gate browser fixture\n (#3621): anything a model root imports from core must be exported by the\n browser entry and must not reach `node:` built-ins, `pg`, `cosmiconfig`, or\n `jiti`. Keep Node-only code behind a subpath or a lazy import; when a fix\n makes a package pass, delete its entry in\n `packages/bundle-gate/src/browser-gate/expected-failures.ts`.\n- Database retries are transient-only, four attempts total for `get`/`upsert`.\n Use `src/db-errors.ts` classifiers through the cause chain, never message\n matching (SDK driver text can live in `context.originalError`). Constraints,\n bad input, missing tables, and aborted PostgreSQL transactions fail immediately.\n Unique/PK violations become `VALIDATION_UNIQUE_CONSTRAINT`, NOT NULL becomes\n `VALIDATION_REQUIRED_FIELD`; other failures keep the driver error as `cause`.\n- Property initializers precede option values; options win. Arrays/objects are\n shallow-cloned. Collection creation caches fields; table verification caches\n by DB URL and table. Preserve these scopes when changing initialization.\n- The Vite plugin loads scanner/schema code from `dist/` when present. Rebuild\n core after editing those sources before testing consumer manifest generation.\n Vite 8 requires `oxc.decorator: { legacy: true, emitDecoratorMetadata: true }`.\n\n## Validation\n\n`typecheck` checks production source first, then the test project in\n`tsconfig.test.json`. Test coverage and the shared relaxed config\ncontract are documented in [agents/test-types.md](agents/test-types.md).\n\nRun focused tests first, then applicable package checks:\n\n```bash\npnpm --filter @happyvertical/smrt-core test\npnpm --filter @happyvertical/smrt-core typecheck\npnpm --filter @happyvertical/smrt-core build\npnpm --filter @happyvertical/smrt-core test:postgres\npnpm check:agents-chain\npnpm smrt dev:knowledge-check\n```\n\nThe PostgreSQL lane is required for numeric types, UUID casts, conflict targets,\ntimestamps, and migrations. Tests generate their manifest before Vitest; restart\nwatch mode after adding decorated classes. Documentation-only changes need\ninstruction-chain and knowledge freshness checks, not the runtime test suite.\n",
|
|
1151
1151
|
"moduleDocs": [
|
|
1152
1152
|
{
|
|
1153
1153
|
"path": "agents/object-runtime.md",
|
|
@@ -1197,7 +1197,7 @@
|
|
|
1197
1197
|
{
|
|
1198
1198
|
"path": "agents/build-knowledge.md",
|
|
1199
1199
|
"module": "build-knowledge",
|
|
1200
|
-
"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 every other bare import stays external so the\nworker shares the installed `@happyvertical/smrt-core` registry. 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## 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.\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.\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"
|
|
1200
|
+
"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## 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.\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"
|
|
1201
1201
|
},
|
|
1202
1202
|
{
|
|
1203
1203
|
"path": "agents/memory.md",
|
|
@@ -14,9 +14,11 @@
|
|
|
14
14
|
* objects AND its consumed packages' objects (via the `.smrt/register.js` it
|
|
15
15
|
* imports), with each class's package-qualified identity and isolated
|
|
16
16
|
* manifest exactly as the web server registers them. Application sources are
|
|
17
|
-
* bundled;
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* bundled; package imports stay external and are resolved from `node_modules`
|
|
18
|
+
* at runtime, except Oxc's decorator helper. Oxc injects that helper into
|
|
19
|
+
* transformed application classes but applications do not declare it, so the
|
|
20
|
+
* artifact bundles it. `@happyvertical/*` packages stay external so the worker
|
|
21
|
+
* and `@happyvertical/smrt-core` share one registry.
|
|
20
22
|
*/
|
|
21
23
|
import type { Alias, ResolvedConfig } from 'vite';
|
|
22
24
|
/** Directory (under the project root) the compiled registration is written to. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-registration.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/worker-registration.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"worker-registration.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/worker-registration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAGlD,kFAAkF;AAClF,eAAO,MAAM,uBAAuB,kBAAkB,CAAC;AAEvD,8DAA8D;AAC9D,eAAO,MAAM,yBAAyB,gBAAgB,CAAC;AAEvD,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,8BAA4D,CAAC;AASlG;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,SAAS,KAAK,EAAE,GAAG,SAAS,GACpC,KAAK,EAAE,CAOT;AASD;;;GAGG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,SAAS,KAAK,EAAE,GACxB,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAiBzB;AAED,MAAM,WAAW,8BAA8B;IAC7C,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,gBAAgB,EAAE,MAAM,CAAC;IACzB,iFAAiF;IACjF,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC,CAAC;CAClE;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,8BAA8B,GACtC,OAAO,CAAC,MAAM,CAAC,CA2CjB"}
|
|
@@ -17,9 +17,11 @@ import { isAbsolute, join, relative, resolve } from "node:path";
|
|
|
17
17
|
* objects AND its consumed packages' objects (via the `.smrt/register.js` it
|
|
18
18
|
* imports), with each class's package-qualified identity and isolated
|
|
19
19
|
* manifest exactly as the web server registers them. Application sources are
|
|
20
|
-
* bundled;
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* bundled; package imports stay external and are resolved from `node_modules`
|
|
21
|
+
* at runtime, except Oxc's decorator helper. Oxc injects that helper into
|
|
22
|
+
* transformed application classes but applications do not declare it, so the
|
|
23
|
+
* artifact bundles it. `@happyvertical/*` packages stay external so the worker
|
|
24
|
+
* and `@happyvertical/smrt-core` share one registry.
|
|
23
25
|
*/
|
|
24
26
|
/** Directory (under the project root) the compiled registration is written to. */
|
|
25
27
|
var WORKER_REGISTRATION_DIR = ".smrt/runtime";
|
|
@@ -55,6 +57,7 @@ function createWorkerRegistrationExternal(aliases) {
|
|
|
55
57
|
return (id) => {
|
|
56
58
|
if (id.startsWith(".") || id.startsWith("\0") || isAbsolute(id)) return false;
|
|
57
59
|
if (aliases.some((alias) => matchesAlias(id, alias))) return false;
|
|
60
|
+
if (id === "@oxc-project/runtime" || id.startsWith("@oxc-project/runtime/")) return false;
|
|
58
61
|
return true;
|
|
59
62
|
};
|
|
60
63
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-registration.js","names":[],"sources":["../../src/vite-plugin/worker-registration.ts"],"sourcesContent":["/**\n * Compiled application registration for out-of-bundle Node processes (#3117).\n *\n * A SvelteKit app registers its own `@smrt()` objects through the generated\n * `smrt-register.ts`, which only the Vite build compiles — and adapter-node\n * re-bundles the server from its own entries, so that module never exists as\n * an importable file in `build/`. A deployed task or schedule worker is plain\n * ESM run by `node`, so without this artifact `ObjectRegistry.getClass()`\n * cannot resolve any application object and every app-defined job throws\n * `Unknown object type` before its method runs.\n *\n * After a production build, this compiles that same generated module into\n * `.smrt/runtime/register.js`: one ESM entry that registers the app's local\n * objects AND its consumed packages' objects (via the `.smrt/register.js` it\n * imports), with each class's package-qualified identity and isolated\n * manifest exactly as the web server registers them. Application sources are\n * bundled;
|
|
1
|
+
{"version":3,"file":"worker-registration.js","names":[],"sources":["../../src/vite-plugin/worker-registration.ts"],"sourcesContent":["/**\n * Compiled application registration for out-of-bundle Node processes (#3117).\n *\n * A SvelteKit app registers its own `@smrt()` objects through the generated\n * `smrt-register.ts`, which only the Vite build compiles — and adapter-node\n * re-bundles the server from its own entries, so that module never exists as\n * an importable file in `build/`. A deployed task or schedule worker is plain\n * ESM run by `node`, so without this artifact `ObjectRegistry.getClass()`\n * cannot resolve any application object and every app-defined job throws\n * `Unknown object type` before its method runs.\n *\n * After a production build, this compiles that same generated module into\n * `.smrt/runtime/register.js`: one ESM entry that registers the app's local\n * objects AND its consumed packages' objects (via the `.smrt/register.js` it\n * imports), with each class's package-qualified identity and isolated\n * manifest exactly as the web server registers them. Application sources are\n * bundled; package imports stay external and are resolved from `node_modules`\n * at runtime, except Oxc's decorator helper. Oxc injects that helper into\n * transformed application classes but applications do not declare it, so the\n * artifact bundles it. `@happyvertical/*` packages stay external so the worker\n * and `@happyvertical/smrt-core` share one registry.\n */\n\nimport { isAbsolute, join, relative, resolve } from 'node:path';\nimport type { Alias, ResolvedConfig } from 'vite';\nimport { importProjectVite } from '../utils/import-project-vite.js';\n\n/** Directory (under the project root) the compiled registration is written to. */\nexport const WORKER_REGISTRATION_DIR = '.smrt/runtime';\n\n/** Entry file name inside {@link WORKER_REGISTRATION_DIR}. */\nexport const WORKER_REGISTRATION_ENTRY = 'register.js';\n\n/** Project-relative path a worker imports: `.smrt/runtime/register.js`. */\nexport const WORKER_REGISTRATION_PATH = `${WORKER_REGISTRATION_DIR}/${WORKER_REGISTRATION_ENTRY}`;\n\n/** Is `child` the project root or inside it (and not under node_modules)? */\nfunction isProjectSource(projectRoot: string, child: string): boolean {\n const rel = relative(projectRoot, child);\n if (rel.startsWith('..') || isAbsolute(rel)) return false;\n return !rel.split(/[\\\\/]/).includes('node_modules');\n}\n\n/**\n * The resolved aliases that point at the application's own sources (e.g.\n * SvelteKit's `$lib` → `src/lib`). Aliases that map a package name elsewhere\n * are dropped: the compiled registration must import packages exactly as the\n * deployed process resolves them, from `node_modules`.\n */\nexport function projectSourceAliases(\n projectRoot: string,\n aliases: readonly Alias[] | undefined,\n): Alias[] {\n return (aliases ?? []).filter(\n (alias) =>\n typeof alias.replacement === 'string' &&\n isAbsolute(alias.replacement) &&\n isProjectSource(projectRoot, alias.replacement),\n );\n}\n\nfunction matchesAlias(id: string, alias: Alias): boolean {\n const { find } = alias;\n if (typeof find === 'string') return id === find || id.startsWith(`${find}/`);\n find.lastIndex = 0;\n return find.test(id);\n}\n\n/**\n * Rollup `external` predicate: bundle relative, absolute (resolved), virtual,\n * and project-alias imports; keep every other bare specifier external.\n */\nexport function createWorkerRegistrationExternal(\n aliases: readonly Alias[],\n): (id: string) => boolean {\n return (id) => {\n if (id.startsWith('.') || id.startsWith('\\0') || isAbsolute(id)) {\n return false;\n }\n if (aliases.some((alias) => matchesAlias(id, alias))) return false;\n // Vite/Oxc lowers legacy decorators to imports from this package. It is a\n // core build dependency, not a consumer dependency, so bundle its root and\n // helper subpaths into the standalone worker artifact.\n if (\n id === '@oxc-project/runtime' ||\n id.startsWith('@oxc-project/runtime/')\n ) {\n return false;\n }\n return true;\n };\n}\n\nexport interface BuildWorkerRegistrationOptions {\n /** The application root (where `.smrt/` lives). */\n projectRoot: string;\n /** The generated `smrt-register.ts` to compile. */\n registrationFile: string;\n /** The consumer build's resolved config (aliases, decorator transform, mode). */\n resolvedConfig: Pick<ResolvedConfig, 'mode' | 'oxc' | 'resolve'>;\n}\n\n/**\n * Compile the generated registration into `.smrt/runtime/register.js`.\n * Returns the absolute output path. Throws on failure: a worker without it\n * cannot run any application-defined job, so the build must fail closed.\n */\nexport async function buildWorkerRegistration(\n options: BuildWorkerRegistrationOptions,\n): Promise<string> {\n const { projectRoot, registrationFile, resolvedConfig } = options;\n const outDir = resolve(projectRoot, WORKER_REGISTRATION_DIR);\n const aliases = projectSourceAliases(\n projectRoot,\n resolvedConfig.resolve.alias,\n );\n const { build } = await importProjectVite(projectRoot, 'Worker registration');\n await build({\n configFile: false,\n envFile: false,\n root: projectRoot,\n mode: resolvedConfig.mode,\n logLevel: 'warn',\n clearScreen: false,\n publicDir: false,\n // No consumer plugins: the generated module is plain TypeScript over the\n // app's decorated classes, and re-running smrtPlugin here would recurse.\n plugins: [],\n resolve: { alias: aliases },\n oxc: resolvedConfig.oxc,\n // Externalization is decided by `external` below, not Vite's SSR\n // heuristics, so a package import can never be inlined into the bundle.\n ssr: { target: 'node', noExternal: true },\n build: {\n ssr: registrationFile,\n outDir,\n emptyOutDir: true,\n copyPublicDir: false,\n minify: false,\n sourcemap: true,\n reportCompressedSize: false,\n rollupOptions: {\n external: createWorkerRegistrationExternal(aliases),\n output: {\n format: 'es',\n entryFileNames: WORKER_REGISTRATION_ENTRY,\n chunkFileNames: 'chunks/[name]-[hash].js',\n },\n },\n },\n });\n return join(outDir, WORKER_REGISTRATION_ENTRY);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,IAAa,0BAA0B;;AAGvC,IAAa,4BAA4B;AAGD,GAAG,wBAAH,EAA8B,0BAA9B;;AAGxC,SAAS,gBAAgB,aAAqB,OAAwB;CACpE,MAAM,MAAM,SAAS,aAAa,KAAK;CACvC,IAAI,IAAI,WAAW,IAAI,KAAK,WAAW,GAAG,GAAG,OAAO;CACpD,OAAO,CAAC,IAAI,MAAM,OAAO,CAAC,CAAC,SAAS,cAAc;AACpD;;;;;;;AAQA,SAAgB,qBACd,aACA,SACS;CACT,QAAQ,WAAW,CAAC,EAAA,CAAG,QACpB,UACC,OAAO,MAAM,gBAAgB,YAC7B,WAAW,MAAM,WAAW,KAC5B,gBAAgB,aAAa,MAAM,WAAW,CAClD;AACF;AAEA,SAAS,aAAa,IAAY,OAAuB;CACvD,MAAM,EAAE,SAAS;CACjB,IAAI,OAAO,SAAS,UAAU,OAAO,OAAO,QAAQ,GAAG,WAAW,GAAG,KAAK,EAAE;CAC5E,KAAK,YAAY;CACjB,OAAO,KAAK,KAAK,EAAE;AACrB;;;;;AAMA,SAAgB,iCACd,SACyB;CACzB,QAAQ,OAAO;EACb,IAAI,GAAG,WAAW,GAAG,KAAK,GAAG,WAAW,IAAI,KAAK,WAAW,EAAE,GAC5D,OAAO;EAET,IAAI,QAAQ,MAAM,UAAU,aAAa,IAAI,KAAK,CAAC,GAAG,OAAO;EAI7D,IACE,OAAO,0BACP,GAAG,WAAW,uBAAuB,GAErC,OAAO;EAET,OAAO;CACT;AACF;;;;;;AAgBA,eAAsB,wBACpB,SACiB;CACjB,MAAM,EAAE,aAAa,kBAAkB,mBAAmB;CAC1D,MAAM,SAAS,QAAQ,aAAa,uBAAuB;CAC3D,MAAM,UAAU,qBACd,aACA,eAAe,QAAQ,KACzB;CACA,MAAM,EAAE,UAAU,MAAM,kBAAkB,aAAa,qBAAqB;CAC5E,MAAM,MAAM;EACV,YAAY;EACZ,SAAS;EACT,MAAM;EACN,MAAM,eAAe;EACrB,UAAU;EACV,aAAa;EACb,WAAW;EAGX,SAAS,CAAC;EACV,SAAS,EAAE,OAAO,QAAQ;EAC1B,KAAK,eAAe;EAGpB,KAAK;GAAE,QAAQ;GAAQ,YAAY;EAAK;EACxC,OAAO;GACL,KAAK;GACL;GACA,aAAa;GACb,eAAe;GACf,QAAQ;GACR,WAAW;GACX,sBAAsB;GACtB,eAAe;IACb,UAAU,iCAAiC,OAAO;IAClD,QAAQ;KACN,QAAQ;KACR,gBAAgB;KAChB,gBAAgB;IAClB;GACF;EACF;CACF,CAAC;CACD,OAAO,KAAK,QAAQ,yBAAyB;AAC/C"}
|
|
@@ -60,9 +60,10 @@ export declare const SMRT_PRESET_DEFAULTS: {
|
|
|
60
60
|
* Packages SMRT's SvelteKit entries share with the app by identity. SvelteKit
|
|
61
61
|
* recognizes `redirect()`/`fail()` results by class, so a server entry such as
|
|
62
62
|
* `@happyvertical/smrt-app-runtime/sveltekit` must import the app's own copy.
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
63
|
+
* Deduping selects the app peer for linked/workspace packages. The runtime
|
|
64
|
+
* must also enter the SSR bundle: adapter-node bundles Kit itself, so leaving
|
|
65
|
+
* runtime external would import a second Kit class through Node even when
|
|
66
|
+
* both resolve to the same installed version.
|
|
66
67
|
*/
|
|
67
68
|
export declare const SMRT_SHARED_RUNTIME_PACKAGES: readonly ["@sveltejs/kit"];
|
|
68
69
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite-preset/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,MAAM,CAAC;AAK/C,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,oBAAoB;;;;;;;CAOvB,CAAC;AAmEX
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite-preset/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,MAAM,CAAC;AAK/C,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,oBAAoB;;;;;;;CAOvB,CAAC;AAmEX;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,4BAA6B,CAAC;AAiCvE;;;;GAIG;AACH,wBAAsB,IAAI,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAgE7E"}
|
|
@@ -46,16 +46,32 @@ function decoratorPlugin() {
|
|
|
46
46
|
* Packages SMRT's SvelteKit entries share with the app by identity. SvelteKit
|
|
47
47
|
* recognizes `redirect()`/`fail()` results by class, so a server entry such as
|
|
48
48
|
* `@happyvertical/smrt-app-runtime/sveltekit` must import the app's own copy.
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
49
|
+
* Deduping selects the app peer for linked/workspace packages. The runtime
|
|
50
|
+
* must also enter the SSR bundle: adapter-node bundles Kit itself, so leaving
|
|
51
|
+
* runtime external would import a second Kit class through Node even when
|
|
52
|
+
* both resolve to the same installed version.
|
|
52
53
|
*/
|
|
53
54
|
var SMRT_SHARED_RUNTIME_PACKAGES = ["@sveltejs/kit"];
|
|
54
55
|
function sharedRuntimePlugin() {
|
|
56
|
+
let isBuild = false;
|
|
55
57
|
return {
|
|
56
58
|
name: "smrt:shared-runtime",
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
enforce: "pre",
|
|
60
|
+
resolveId(id, _importer, options) {
|
|
61
|
+
if (isBuild && options.ssr && id === "@happyvertical/smrt-app-runtime") return {
|
|
62
|
+
id,
|
|
63
|
+
external: true
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
config(_user, { command }) {
|
|
67
|
+
isBuild = command === "build";
|
|
68
|
+
return {
|
|
69
|
+
resolve: { dedupe: [...SMRT_SHARED_RUNTIME_PACKAGES] },
|
|
70
|
+
...isBuild ? { ssr: {
|
|
71
|
+
noExternal: ["@happyvertical/smrt-app-runtime"],
|
|
72
|
+
external: ["@happyvertical/smrt-app-runtime/sveltekit-runtime"]
|
|
73
|
+
} } : {}
|
|
74
|
+
};
|
|
59
75
|
}
|
|
60
76
|
};
|
|
61
77
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/vite-preset/index.ts"],"sourcesContent":["/**\n * Single-plugin Vite preset for SMRT applications.\n *\n * `plugins: [sveltekit(), smrt()]` replaces the hand-copied glue every app\n * carried: the Oxc legacy-decorator block, `smrtConsumer({...})` with an\n * explicit package list, and `smrtPlugin({...})` with the SvelteKit path\n * options. `smrtConsumer` and `smrtPlugin` stay public and unchanged.\n */\nimport type { Plugin, UserConfig } from 'vite';\nimport { smrtConsumer } from '../consumer-plugin/index.js';\nimport { type SmrtPluginOptions, smrtPlugin } from '../vite-plugin/index.js';\nimport { resolveCookbookHostedObjects } from './cookbook-exposure.js';\n\nexport interface SmrtPresetOptions {\n /**\n * SMRT packages whose object manifests this app consumes. Takes precedence\n * over `consumer.packages` in `smrt.config.ts`. An empty array declares that\n * the app consumes none. Tooling/UI packages must not be listed.\n */\n packages?: readonly string[];\n /**\n * Consumed package models to host over generated REST routes, as\n * provider-qualified refs (`@scope/pkg:Model`). This is an HTTP exposure\n * boundary: each model's own `api` config still applies, so an `api: false`\n * model stays closed. Omit to derive it from `smrt.cookbook.json` (the models\n * of its recipes and `features`, minus those its `exposure` turns off for\n * `api`); without a cookbook nothing is hosted. `[]` hosts nothing.\n */\n expose?: readonly string[];\n /**\n * Project root. Omit to follow the root Vite/SvelteKit resolve (the launch\n * cwd); `smrt.config.ts` is searched from this value or `process.cwd()`.\n */\n projectRoot?: string;\n /** Directory of the app's own `@smrt()` objects. Default `src/lib/objects`. */\n objectsDir?: string;\n /** Generated type declarations. Default `src/lib/types/smrt-generated`. */\n typesDir?: string;\n /** Generated API routes. Default `src/routes/api`. */\n routesDir?: string;\n /** Directory holding the generated runtime config module. Default `src/lib/server`. */\n configPath?: string;\n /** File name of that module. Default `smrt.ts`. */\n configFileName?: string;\n /** Source globs scanned for objects. Default `<objectsDir>/**\\/*.ts`. */\n include?: string[];\n /** Source globs excluded from the scan. Default test and spec files. */\n exclude?: string[];\n /**\n * Apply the Oxc legacy-decorator config. Default `true`. Values the app sets\n * under `oxc.decorator` itself are never overwritten.\n */\n decorators?: boolean;\n}\n\nexport const SMRT_PRESET_DEFAULTS = {\n objectsDir: 'src/lib/objects',\n typesDir: 'src/lib/types/smrt-generated',\n routesDir: 'src/routes/api',\n configPath: 'src/lib/server',\n configFileName: 'smrt.ts',\n exclude: ['**/*.test.ts', '**/*.spec.ts'],\n} as const;\n\nfunction validatePackages(value: unknown, source: string): string[] {\n if (\n !Array.isArray(value) ||\n value.some((p) => typeof p !== 'string' || p.trim() === '')\n ) {\n throw new Error(\n `[smrt] ${source} must be an array of non-empty package names, got ${JSON.stringify(value)}.`,\n );\n }\n return [...new Set(value.map((p: string) => p.trim()))];\n}\n\n/** Resolve the declared consumer package list: option first, then smrt.config. */\nasync function resolvePackages(\n options: SmrtPresetOptions,\n searchFrom: string,\n): Promise<string[]> {\n if (options.packages !== undefined) {\n return validatePackages(options.packages, 'smrt({ packages })');\n }\n\n let declared: unknown;\n try {\n const { loadConfig } = await import('@happyvertical/smrt-config');\n const config = await loadConfig({ cache: false, searchFrom });\n declared = config.consumer?.packages;\n } catch (error) {\n throw new Error(\n `[smrt] Could not read smrt.config to resolve consumer packages: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n\n if (declared === undefined) {\n throw new Error(\n '[smrt] No consumer package list declared. Set `consumer: { packages: [...] }` in smrt.config.ts ' +\n '(use [] when the app consumes no SMRT packages) or pass `smrt({ packages })`. ' +\n 'The list is explicit so runtime registration is deterministic; it is never inferred from package.json.',\n );\n }\n return validatePackages(declared, 'smrt.config `consumer.packages`');\n}\n\nfunction decoratorPlugin(): Plugin {\n return {\n name: 'smrt:decorators',\n // Vite merges a hook's result over the user config, so fill only the\n // values the app has not set itself.\n config(user: UserConfig) {\n const decorator = (\n user as { oxc?: { decorator?: Record<string, unknown> } }\n ).oxc?.decorator;\n const fill: Record<string, boolean> = {};\n if (decorator?.legacy === undefined) fill.legacy = true;\n if (decorator?.emitDecoratorMetadata === undefined) {\n fill.emitDecoratorMetadata = true;\n }\n return Object.keys(fill).length\n ? ({ oxc: { decorator: fill } } as UserConfig)\n : undefined;\n },\n };\n}\n\n/**\n * Packages SMRT's SvelteKit entries share with the app by identity. SvelteKit\n * recognizes `redirect()`/`fail()` results by class, so a server entry such as\n * `@happyvertical/smrt-app-runtime/sveltekit` must import the app's own copy.\n * A registry install already resolves that peer from the app; a linked or\n * workspace package resolves it from its own directory instead, and its\n * redirects then surface as 500s. Deduping makes both layouts identical.\n */\nexport const SMRT_SHARED_RUNTIME_PACKAGES = ['@sveltejs/kit'] as const;\n\nfunction sharedRuntimePlugin(): Plugin {\n return {\n name: 'smrt:shared-runtime',\n config() {\n return { resolve: { dedupe: [...SMRT_SHARED_RUNTIME_PACKAGES] } };\n },\n };\n}\n\n/**\n * SMRT Vite preset: decorators, dependency manifests, and app object\n * generation in one entry. Returns a promise because the package list is read\n * from `smrt.config.ts`; Vite accepts promised plugins.\n */\nexport async function smrt(options: SmrtPresetOptions = {}): Promise<Plugin[]> {\n const D = SMRT_PRESET_DEFAULTS;\n const objectsDir = options.objectsDir ?? D.objectsDir;\n const typesDir = options.typesDir ?? D.typesDir;\n const routesDir = options.routesDir ?? D.routesDir;\n const configPath = options.configPath ?? D.configPath;\n const configFileName = options.configFileName ?? D.configFileName;\n const rootOption = options.projectRoot\n ? { projectRoot: options.projectRoot }\n : {};\n\n const packages = await resolvePackages(\n options,\n options.projectRoot ?? process.cwd(),\n );\n\n const plugins: Plugin[] = [];\n if (options.decorators !== false) plugins.push(decoratorPlugin());\n plugins.push(sharedRuntimePlugin());\n // smrtConsumer must precede smrtPlugin, as in the template's hand-written form.\n // With no declared packages there is nothing to consume, and an empty list\n // would otherwise trigger dependency discovery.\n if (packages.length > 0) {\n const hosted = options.expose\n ? validatePackages(options.expose, 'smrt({ expose })')\n : resolveCookbookHostedObjects(\n options.projectRoot ?? process.cwd(),\n packages,\n );\n plugins.push(\n smrtConsumer({\n ...rootOption,\n packages,\n generateTypes: true,\n typesDir,\n svelteKit:\n hosted.length > 0\n ? {\n objects: hosted,\n routesDir,\n objectsDir,\n configPath,\n configFileName,\n }\n : true,\n }),\n );\n }\n const pluginOptions: SmrtPluginOptions = {\n ...rootOption,\n include: options.include ?? [`${objectsDir}/**/*.ts`],\n exclude: options.exclude ?? [...D.exclude],\n generateTypes: true,\n typeDeclarationsPath: typesDir,\n svelteKit: {\n enabled: true,\n routesDir,\n objectsDir,\n configPath,\n configFileName,\n },\n };\n plugins.push(smrtPlugin(pluginOptions));\n return plugins;\n}\n"],"mappings":";;;;AAuDA,IAAa,uBAAuB;CAClC,YAAY;CACZ,UAAU;CACV,WAAW;CACX,YAAY;CACZ,gBAAgB;CAChB,SAAS,CAAC,gBAAgB,cAAc;AAC1C;AAEA,SAAS,iBAAiB,OAAgB,QAA0B;CAClE,IACE,CAAC,MAAM,QAAQ,KAAK,KACpB,MAAM,MAAM,MAAM,OAAO,MAAM,YAAY,EAAE,KAAK,MAAM,EAAE,GAE1D,MAAM,IAAI,MACR,UAAU,OAAO,oDAAoD,KAAK,UAAU,KAAK,EAAE,EAC7F;CAEF,OAAO,CAAC,GAAG,IAAI,IAAI,MAAM,KAAK,MAAc,EAAE,KAAK,CAAC,CAAC,CAAC;AACxD;;AAGA,eAAe,gBACb,SACA,YACmB;CACnB,IAAI,QAAQ,aAAa,KAAA,GACvB,OAAO,iBAAiB,QAAQ,UAAU,oBAAoB;CAGhE,IAAI;CACJ,IAAI;EACF,MAAM,EAAE,eAAe,MAAM,OAAO;EAEpC,YAAW,MADU,WAAW;GAAE,OAAO;GAAO;EAAW,CAAC,EAAA,CAC1C,UAAU;CAC9B,SAAS,OAAO;EACd,MAAM,IAAI,MACR,mEACE,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAEzD;CACF;CAEA,IAAI,aAAa,KAAA,GACf,MAAM,IAAI,MACR,sRAGF;CAEF,OAAO,iBAAiB,UAAU,iCAAiC;AACrE;AAEA,SAAS,kBAA0B;CACjC,OAAO;EACL,MAAM;EAGN,OAAO,MAAkB;GACvB,MAAM,YACJ,KACA,KAAK;GACP,MAAM,OAAgC,CAAC;GACvC,IAAI,WAAW,WAAW,KAAA,GAAW,KAAK,SAAS;GACnD,IAAI,WAAW,0BAA0B,KAAA,GACvC,KAAK,wBAAwB;GAE/B,OAAO,OAAO,KAAK,IAAI,CAAC,CAAC,SACpB,EAAE,KAAK,EAAE,WAAW,KAAK,EAAE,IAC5B,KAAA;EACN;CACF;AACF;;;;;;;;;AAUA,IAAa,+BAA+B,CAAC,eAAe;AAE5D,SAAS,sBAA8B;CACrC,OAAO;EACL,MAAM;EACN,SAAS;GACP,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,GAAG,4BAA4B,EAAE,EAAE;EAClE;CACF;AACF;;;;;;AAOA,eAAsB,KAAK,UAA6B,CAAC,GAAsB;CAC7E,MAAM,IAAI;CACV,MAAM,aAAa,QAAQ,cAAc,EAAE;CAC3C,MAAM,WAAW,QAAQ,YAAY,EAAE;CACvC,MAAM,YAAY,QAAQ,aAAa,EAAE;CACzC,MAAM,aAAa,QAAQ,cAAc,EAAE;CAC3C,MAAM,iBAAiB,QAAQ,kBAAkB,EAAE;CACnD,MAAM,aAAa,QAAQ,cACvB,EAAE,aAAa,QAAQ,YAAY,IACnC,CAAC;CAEL,MAAM,WAAW,MAAM,gBACrB,SACA,QAAQ,eAAe,QAAQ,IAAI,CACrC;CAEA,MAAM,UAAoB,CAAC;CAC3B,IAAI,QAAQ,eAAe,OAAO,QAAQ,KAAK,gBAAgB,CAAC;CAChE,QAAQ,KAAK,oBAAoB,CAAC;CAIlC,IAAI,SAAS,SAAS,GAAG;EACvB,MAAM,SAAS,QAAQ,SACnB,iBAAiB,QAAQ,QAAQ,kBAAkB,IACnD,6BACE,QAAQ,eAAe,QAAQ,IAAI,GACnC,QACF;EACJ,QAAQ,KACN,aAAa;GACX,GAAG;GACH;GACA,eAAe;GACf;GACA,WACE,OAAO,SAAS,IACZ;IACE,SAAS;IACT;IACA;IACA;IACA;GACF,IACA;EACR,CAAC,CACH;CACF;CACA,MAAM,gBAAmC;EACvC,GAAG;EACH,SAAS,QAAQ,WAAW,CAAC,GAAG,WAAW,SAAS;EACpD,SAAS,QAAQ,WAAW,CAAC,GAAG,EAAE,OAAO;EACzC,eAAe;EACf,sBAAsB;EACtB,WAAW;GACT,SAAS;GACT;GACA;GACA;GACA;EACF;CACF;CACA,QAAQ,KAAK,WAAW,aAAa,CAAC;CACtC,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/vite-preset/index.ts"],"sourcesContent":["/**\n * Single-plugin Vite preset for SMRT applications.\n *\n * `plugins: [sveltekit(), smrt()]` replaces the hand-copied glue every app\n * carried: the Oxc legacy-decorator block, `smrtConsumer({...})` with an\n * explicit package list, and `smrtPlugin({...})` with the SvelteKit path\n * options. `smrtConsumer` and `smrtPlugin` stay public and unchanged.\n */\nimport type { Plugin, UserConfig } from 'vite';\nimport { smrtConsumer } from '../consumer-plugin/index.js';\nimport { type SmrtPluginOptions, smrtPlugin } from '../vite-plugin/index.js';\nimport { resolveCookbookHostedObjects } from './cookbook-exposure.js';\n\nexport interface SmrtPresetOptions {\n /**\n * SMRT packages whose object manifests this app consumes. Takes precedence\n * over `consumer.packages` in `smrt.config.ts`. An empty array declares that\n * the app consumes none. Tooling/UI packages must not be listed.\n */\n packages?: readonly string[];\n /**\n * Consumed package models to host over generated REST routes, as\n * provider-qualified refs (`@scope/pkg:Model`). This is an HTTP exposure\n * boundary: each model's own `api` config still applies, so an `api: false`\n * model stays closed. Omit to derive it from `smrt.cookbook.json` (the models\n * of its recipes and `features`, minus those its `exposure` turns off for\n * `api`); without a cookbook nothing is hosted. `[]` hosts nothing.\n */\n expose?: readonly string[];\n /**\n * Project root. Omit to follow the root Vite/SvelteKit resolve (the launch\n * cwd); `smrt.config.ts` is searched from this value or `process.cwd()`.\n */\n projectRoot?: string;\n /** Directory of the app's own `@smrt()` objects. Default `src/lib/objects`. */\n objectsDir?: string;\n /** Generated type declarations. Default `src/lib/types/smrt-generated`. */\n typesDir?: string;\n /** Generated API routes. Default `src/routes/api`. */\n routesDir?: string;\n /** Directory holding the generated runtime config module. Default `src/lib/server`. */\n configPath?: string;\n /** File name of that module. Default `smrt.ts`. */\n configFileName?: string;\n /** Source globs scanned for objects. Default `<objectsDir>/**\\/*.ts`. */\n include?: string[];\n /** Source globs excluded from the scan. Default test and spec files. */\n exclude?: string[];\n /**\n * Apply the Oxc legacy-decorator config. Default `true`. Values the app sets\n * under `oxc.decorator` itself are never overwritten.\n */\n decorators?: boolean;\n}\n\nexport const SMRT_PRESET_DEFAULTS = {\n objectsDir: 'src/lib/objects',\n typesDir: 'src/lib/types/smrt-generated',\n routesDir: 'src/routes/api',\n configPath: 'src/lib/server',\n configFileName: 'smrt.ts',\n exclude: ['**/*.test.ts', '**/*.spec.ts'],\n} as const;\n\nfunction validatePackages(value: unknown, source: string): string[] {\n if (\n !Array.isArray(value) ||\n value.some((p) => typeof p !== 'string' || p.trim() === '')\n ) {\n throw new Error(\n `[smrt] ${source} must be an array of non-empty package names, got ${JSON.stringify(value)}.`,\n );\n }\n return [...new Set(value.map((p: string) => p.trim()))];\n}\n\n/** Resolve the declared consumer package list: option first, then smrt.config. */\nasync function resolvePackages(\n options: SmrtPresetOptions,\n searchFrom: string,\n): Promise<string[]> {\n if (options.packages !== undefined) {\n return validatePackages(options.packages, 'smrt({ packages })');\n }\n\n let declared: unknown;\n try {\n const { loadConfig } = await import('@happyvertical/smrt-config');\n const config = await loadConfig({ cache: false, searchFrom });\n declared = config.consumer?.packages;\n } catch (error) {\n throw new Error(\n `[smrt] Could not read smrt.config to resolve consumer packages: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n\n if (declared === undefined) {\n throw new Error(\n '[smrt] No consumer package list declared. Set `consumer: { packages: [...] }` in smrt.config.ts ' +\n '(use [] when the app consumes no SMRT packages) or pass `smrt({ packages })`. ' +\n 'The list is explicit so runtime registration is deterministic; it is never inferred from package.json.',\n );\n }\n return validatePackages(declared, 'smrt.config `consumer.packages`');\n}\n\nfunction decoratorPlugin(): Plugin {\n return {\n name: 'smrt:decorators',\n // Vite merges a hook's result over the user config, so fill only the\n // values the app has not set itself.\n config(user: UserConfig) {\n const decorator = (\n user as { oxc?: { decorator?: Record<string, unknown> } }\n ).oxc?.decorator;\n const fill: Record<string, boolean> = {};\n if (decorator?.legacy === undefined) fill.legacy = true;\n if (decorator?.emitDecoratorMetadata === undefined) {\n fill.emitDecoratorMetadata = true;\n }\n return Object.keys(fill).length\n ? ({ oxc: { decorator: fill } } as UserConfig)\n : undefined;\n },\n };\n}\n\n/**\n * Packages SMRT's SvelteKit entries share with the app by identity. SvelteKit\n * recognizes `redirect()`/`fail()` results by class, so a server entry such as\n * `@happyvertical/smrt-app-runtime/sveltekit` must import the app's own copy.\n * Deduping selects the app peer for linked/workspace packages. The runtime\n * must also enter the SSR bundle: adapter-node bundles Kit itself, so leaving\n * runtime external would import a second Kit class through Node even when\n * both resolve to the same installed version.\n */\nexport const SMRT_SHARED_RUNTIME_PACKAGES = ['@sveltejs/kit'] as const;\n\nfunction sharedRuntimePlugin(): Plugin {\n let isBuild = false;\n return {\n name: 'smrt:shared-runtime',\n enforce: 'pre',\n resolveId(id, _importer, options) {\n // ssr.external on the package root would externalize every subpath.\n // Keep only this exact entry external, preserving native dependencies.\n if (isBuild && options.ssr && id === '@happyvertical/smrt-app-runtime') {\n return { id, external: true };\n }\n },\n config(_user, { command }) {\n // Only adapter builds need this split. Dev import analysis ignores a\n // resolver's external flag, so it must retain normal externalization.\n isBuild = command === 'build';\n return {\n resolve: { dedupe: [...SMRT_SHARED_RUNTIME_PACKAGES] },\n ...(isBuild\n ? {\n ssr: {\n noExternal: ['@happyvertical/smrt-app-runtime'],\n external: ['@happyvertical/smrt-app-runtime/sveltekit-runtime'],\n },\n }\n : {}),\n };\n },\n };\n}\n\n/**\n * SMRT Vite preset: decorators, dependency manifests, and app object\n * generation in one entry. Returns a promise because the package list is read\n * from `smrt.config.ts`; Vite accepts promised plugins.\n */\nexport async function smrt(options: SmrtPresetOptions = {}): Promise<Plugin[]> {\n const D = SMRT_PRESET_DEFAULTS;\n const objectsDir = options.objectsDir ?? D.objectsDir;\n const typesDir = options.typesDir ?? D.typesDir;\n const routesDir = options.routesDir ?? D.routesDir;\n const configPath = options.configPath ?? D.configPath;\n const configFileName = options.configFileName ?? D.configFileName;\n const rootOption = options.projectRoot\n ? { projectRoot: options.projectRoot }\n : {};\n\n const packages = await resolvePackages(\n options,\n options.projectRoot ?? process.cwd(),\n );\n\n const plugins: Plugin[] = [];\n if (options.decorators !== false) plugins.push(decoratorPlugin());\n plugins.push(sharedRuntimePlugin());\n // smrtConsumer must precede smrtPlugin, as in the template's hand-written form.\n // With no declared packages there is nothing to consume, and an empty list\n // would otherwise trigger dependency discovery.\n if (packages.length > 0) {\n const hosted = options.expose\n ? validatePackages(options.expose, 'smrt({ expose })')\n : resolveCookbookHostedObjects(\n options.projectRoot ?? process.cwd(),\n packages,\n );\n plugins.push(\n smrtConsumer({\n ...rootOption,\n packages,\n generateTypes: true,\n typesDir,\n svelteKit:\n hosted.length > 0\n ? {\n objects: hosted,\n routesDir,\n objectsDir,\n configPath,\n configFileName,\n }\n : true,\n }),\n );\n }\n const pluginOptions: SmrtPluginOptions = {\n ...rootOption,\n include: options.include ?? [`${objectsDir}/**/*.ts`],\n exclude: options.exclude ?? [...D.exclude],\n generateTypes: true,\n typeDeclarationsPath: typesDir,\n svelteKit: {\n enabled: true,\n routesDir,\n objectsDir,\n configPath,\n configFileName,\n },\n };\n plugins.push(smrtPlugin(pluginOptions));\n return plugins;\n}\n"],"mappings":";;;;AAuDA,IAAa,uBAAuB;CAClC,YAAY;CACZ,UAAU;CACV,WAAW;CACX,YAAY;CACZ,gBAAgB;CAChB,SAAS,CAAC,gBAAgB,cAAc;AAC1C;AAEA,SAAS,iBAAiB,OAAgB,QAA0B;CAClE,IACE,CAAC,MAAM,QAAQ,KAAK,KACpB,MAAM,MAAM,MAAM,OAAO,MAAM,YAAY,EAAE,KAAK,MAAM,EAAE,GAE1D,MAAM,IAAI,MACR,UAAU,OAAO,oDAAoD,KAAK,UAAU,KAAK,EAAE,EAC7F;CAEF,OAAO,CAAC,GAAG,IAAI,IAAI,MAAM,KAAK,MAAc,EAAE,KAAK,CAAC,CAAC,CAAC;AACxD;;AAGA,eAAe,gBACb,SACA,YACmB;CACnB,IAAI,QAAQ,aAAa,KAAA,GACvB,OAAO,iBAAiB,QAAQ,UAAU,oBAAoB;CAGhE,IAAI;CACJ,IAAI;EACF,MAAM,EAAE,eAAe,MAAM,OAAO;EAEpC,YAAW,MADU,WAAW;GAAE,OAAO;GAAO;EAAW,CAAC,EAAA,CAC1C,UAAU;CAC9B,SAAS,OAAO;EACd,MAAM,IAAI,MACR,mEACE,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAEzD;CACF;CAEA,IAAI,aAAa,KAAA,GACf,MAAM,IAAI,MACR,sRAGF;CAEF,OAAO,iBAAiB,UAAU,iCAAiC;AACrE;AAEA,SAAS,kBAA0B;CACjC,OAAO;EACL,MAAM;EAGN,OAAO,MAAkB;GACvB,MAAM,YACJ,KACA,KAAK;GACP,MAAM,OAAgC,CAAC;GACvC,IAAI,WAAW,WAAW,KAAA,GAAW,KAAK,SAAS;GACnD,IAAI,WAAW,0BAA0B,KAAA,GACvC,KAAK,wBAAwB;GAE/B,OAAO,OAAO,KAAK,IAAI,CAAC,CAAC,SACpB,EAAE,KAAK,EAAE,WAAW,KAAK,EAAE,IAC5B,KAAA;EACN;CACF;AACF;;;;;;;;;;AAWA,IAAa,+BAA+B,CAAC,eAAe;AAE5D,SAAS,sBAA8B;CACrC,IAAI,UAAU;CACd,OAAO;EACL,MAAM;EACN,SAAS;EACT,UAAU,IAAI,WAAW,SAAS;GAGhC,IAAI,WAAW,QAAQ,OAAO,OAAO,mCACnC,OAAO;IAAE;IAAI,UAAU;GAAK;EAEhC;EACA,OAAO,OAAO,EAAE,WAAW;GAGzB,UAAU,YAAY;GACtB,OAAO;IACL,SAAS,EAAE,QAAQ,CAAC,GAAG,4BAA4B,EAAE;IACrD,GAAI,UACA,EACE,KAAK;KACH,YAAY,CAAC,iCAAiC;KAC9C,UAAU,CAAC,mDAAmD;IAChE,EACF,IACA,CAAC;GACP;EACF;CACF;AACF;;;;;;AAOA,eAAsB,KAAK,UAA6B,CAAC,GAAsB;CAC7E,MAAM,IAAI;CACV,MAAM,aAAa,QAAQ,cAAc,EAAE;CAC3C,MAAM,WAAW,QAAQ,YAAY,EAAE;CACvC,MAAM,YAAY,QAAQ,aAAa,EAAE;CACzC,MAAM,aAAa,QAAQ,cAAc,EAAE;CAC3C,MAAM,iBAAiB,QAAQ,kBAAkB,EAAE;CACnD,MAAM,aAAa,QAAQ,cACvB,EAAE,aAAa,QAAQ,YAAY,IACnC,CAAC;CAEL,MAAM,WAAW,MAAM,gBACrB,SACA,QAAQ,eAAe,QAAQ,IAAI,CACrC;CAEA,MAAM,UAAoB,CAAC;CAC3B,IAAI,QAAQ,eAAe,OAAO,QAAQ,KAAK,gBAAgB,CAAC;CAChE,QAAQ,KAAK,oBAAoB,CAAC;CAIlC,IAAI,SAAS,SAAS,GAAG;EACvB,MAAM,SAAS,QAAQ,SACnB,iBAAiB,QAAQ,QAAQ,kBAAkB,IACnD,6BACE,QAAQ,eAAe,QAAQ,IAAI,GACnC,QACF;EACJ,QAAQ,KACN,aAAa;GACX,GAAG;GACH;GACA,eAAe;GACf;GACA,WACE,OAAO,SAAS,IACZ;IACE,SAAS;IACT;IACA;IACA;IACA;GACF,IACA;EACR,CAAC,CACH;CACF;CACA,MAAM,gBAAmC;EACvC,GAAG;EACH,SAAS,QAAQ,WAAW,CAAC,GAAG,WAAW,SAAS;EACpD,SAAS,QAAQ,WAAW,CAAC,GAAG,EAAE,OAAO;EACzC,eAAe;EACf,sBAAsB;EACtB,WAAW;GACT,SAAS;GACT;GACA;GACA;GACA;EACF;CACF;CACA,QAAQ,KAAK,WAAW,aAAa,CAAC;CACtC,OAAO;AACT"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-core",
|
|
3
|
-
"version": "0.57.
|
|
3
|
+
"version": "0.57.10",
|
|
4
4
|
"description": "Core AI agent framework with standardized collections, object-relational mapping, and code generators",
|
|
5
5
|
"author": "HappyVertical",
|
|
6
6
|
"type": "module",
|
|
@@ -195,15 +195,15 @@
|
|
|
195
195
|
"./dist/utils/json.js": "./dist/utils/json.browser.js"
|
|
196
196
|
},
|
|
197
197
|
"dependencies": {
|
|
198
|
-
"@happyvertical/ai": "^0.105.
|
|
199
|
-
"@happyvertical/files": "^0.105.
|
|
200
|
-
"@happyvertical/json": "^0.105.
|
|
201
|
-
"@happyvertical/logger": "^0.105.
|
|
202
|
-
"@happyvertical/smrt-config": "0.57.
|
|
203
|
-
"@happyvertical/smrt-scanner": "0.57.
|
|
204
|
-
"@happyvertical/smrt-types": "0.57.
|
|
205
|
-
"@happyvertical/sql": "^0.105.
|
|
206
|
-
"@happyvertical/utils": "^0.105.
|
|
198
|
+
"@happyvertical/ai": "^0.105.3",
|
|
199
|
+
"@happyvertical/files": "^0.105.3",
|
|
200
|
+
"@happyvertical/json": "^0.105.3",
|
|
201
|
+
"@happyvertical/logger": "^0.105.3",
|
|
202
|
+
"@happyvertical/smrt-config": "0.57.10",
|
|
203
|
+
"@happyvertical/smrt-scanner": "0.57.10",
|
|
204
|
+
"@happyvertical/smrt-types": "0.57.10",
|
|
205
|
+
"@happyvertical/sql": "^0.105.3",
|
|
206
|
+
"@happyvertical/utils": "^0.105.3",
|
|
207
207
|
"@noble/hashes": "^2.4.0",
|
|
208
208
|
"@oxc-project/runtime": "^0.150.0",
|
|
209
209
|
"cheerio": "^1.2.0",
|