@happyvertical/smrt-core 0.45.3 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -0
- package/agents/generators.md +0 -1
- package/dist/browser.d.ts +2 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/embedded-write-queue.d.ts.map +1 -1
- package/dist/embedded-write-queue.js +20 -1
- package/dist/embedded-write-queue.js.map +1 -1
- package/dist/generators/custom-action.d.ts +71 -39
- package/dist/generators/custom-action.d.ts.map +1 -1
- package/dist/generators/custom-action.js +71 -39
- package/dist/generators/custom-action.js.map +1 -1
- package/dist/generators/index.d.ts +0 -2
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +2 -3
- package/dist/generators/mcp.d.ts.map +1 -1
- package/dist/generators/mcp.js +1 -1
- package/dist/generators/mcp.js.map +1 -1
- package/dist/generators/tenant-gate.d.ts +33 -9
- package/dist/generators/tenant-gate.d.ts.map +1 -1
- package/dist/generators/tenant-gate.js +2 -1
- package/dist/generators/tenant-gate.js.map +1 -1
- package/dist/generators.js +2 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/knowledge.js +28 -16
- package/dist/knowledge.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/migrations/framework-base-tables.d.ts +148 -0
- package/dist/migrations/framework-base-tables.d.ts.map +1 -0
- package/dist/migrations/framework-base-tables.js +419 -0
- package/dist/migrations/framework-base-tables.js.map +1 -0
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +2 -1
- package/dist/migrations.js +2 -1
- package/dist/postgres-permissions.d.ts +40 -0
- package/dist/postgres-permissions.d.ts.map +1 -0
- package/dist/postgres-permissions.js +353 -0
- package/dist/postgres-permissions.js.map +1 -0
- package/dist/registry/framework-base-classes.d.ts.map +1 -1
- package/dist/registry/framework-base-classes.js +4 -1
- package/dist/registry/framework-base-classes.js.map +1 -1
- package/dist/scripts/create-wrappers.js +0 -5
- package/dist/smrt-knowledge.json +7 -8
- package/dist/testing/database.d.ts +6 -1
- package/dist/testing/database.d.ts.map +1 -1
- package/dist/testing/database.js +9 -1
- package/dist/testing/database.js.map +1 -1
- package/dist/vite-plugin/api-client-entries.d.ts.map +1 -1
- package/dist/vite-plugin/api-client-entries.js +16 -14
- package/dist/vite-plugin/api-client-entries.js.map +1 -1
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +2 -166
- package/dist/vite-plugin/index.js.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.d.ts +3 -2
- package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.js +20 -24
- package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
- package/dist/vite-plugin/templates/default-ui.ts +22 -1
- package/dist/vite-plugin/web-collections.d.ts.map +1 -1
- package/dist/vite-plugin/web-collections.js.map +1 -1
- package/package.json +4 -9
- package/dist/generators/cli.d.ts +0 -155
- package/dist/generators/cli.d.ts.map +0 -1
- package/dist/generators/cli.js +0 -473
- package/dist/generators/cli.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -14,6 +14,7 @@ and repository rules.
|
|
|
14
14
|
| `src/collection.ts` | Limits, sort whitelist, generated list order | [agents/query-bounds.md](agents/query-bounds.md) |
|
|
15
15
|
| `src/data-query.ts` | Transport-neutral bounded query normalization | [agents/data-query.md](agents/data-query.md) |
|
|
16
16
|
| `src/schema/`, `src/migrations/`, `src/cascade.ts`, `src/system/` | DDL parity, indexes, migrations, delete integrity, retention | [agents/schema-paths.md](agents/schema-paths.md) |
|
|
17
|
+
| `src/postgres-permissions.ts` | Explicit PostgreSQL ACL plans and atomic reconciliation | [deployment permission contract](../../docs/content/postgres-permissions.md) |
|
|
17
18
|
| `src/change-feed.ts` | Durable changes, cursors, table versions, retention | [agents/change-feed.md](agents/change-feed.md) |
|
|
18
19
|
| `src/change-signals.ts` | Signal bus, replica fan-out, SSE | [agents/change-signals.md](agents/change-signals.md) |
|
|
19
20
|
| `src/generators/`, `src/vite-plugin/web-collections.ts` | REST/CLI/MCP generation, manifest hashes, ETags | [agents/generators.md](agents/generators.md) |
|
package/agents/generators.md
CHANGED
|
@@ -9,7 +9,6 @@ invariants, and the traps that apply before editing anything live in
|
|
|
9
9
|
| Generator | Location | Output |
|
|
10
10
|
|-----------|----------|--------|
|
|
11
11
|
| REST API | `src/generators/rest.ts` | OpenAPI-compliant CRUD endpoints |
|
|
12
|
-
| CLI | `src/generators/cli.ts` | `objectname:action` admin commands — writable allowlist, exhaustive-include, `--from-file`, fail-closed tenant context |
|
|
13
12
|
| MCP Server | `src/generators/mcp.ts` | Model Context Protocol tools |
|
|
14
13
|
| Web collections | `src/vite-plugin/web-collections.ts` (selectors) + `generateWebModule` | `@happyvertical/smrt-virt-web` — one typed collection definition per API-exposed REST collection (#1761), consumed by `@happyvertical/smrt-web` |
|
|
15
14
|
|
package/dist/browser.d.ts
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* builds, federation, and client-side applications.
|
|
7
7
|
*
|
|
8
8
|
* Excluded:
|
|
9
|
-
* - Code generators (
|
|
9
|
+
* - Code generators (APIGenerator, MCPGenerator; the local CLI transport
|
|
10
|
+
* lives outside core in packages/cli/src/cli-generator.ts)
|
|
10
11
|
* - Runtime manifest utilities (Node.js-only)
|
|
11
12
|
* - Prebuild utilities (Node.js-only)
|
|
12
13
|
*
|
package/dist/browser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,cAAc,kBAAkB,CAAC;AAEjC,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,YAAY,GACb,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAOlC,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,KAAK,2BAA2B,EAChC,KAAK,iBAAiB,EACtB,yBAAyB,EACzB,4BAA4B,EAC5B,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AACrB,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,KAAK,aAAa,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,cAAc,UAAU,CAAC;AACzB,OAAO,EACL,0BAA0B,EAC1B,KAAK,iCAAiC,GACvC,MAAM,2BAA2B,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,IAAI,IAAI,YAAY,EAAE,MAAM,YAAY,CAAC;AAElD,cAAc,iBAAiB,CAAC;AAEhC,cAAc,gBAAgB,CAAC;AAC/B,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AAExB,cAAc,eAAe,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedded-write-queue.d.ts","sourceRoot":"","sources":["../src/embedded-write-queue.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AA0C5D,UAAU,iBAAiB;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,iBAAiB,GAAG,OAAO,CAEjE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,iBAAiB,GAAG,OAAO,CAEjE;AAED;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,iBAAiB,GAAG,OAAO,CAG3E;
|
|
1
|
+
{"version":3,"file":"embedded-write-queue.d.ts","sourceRoot":"","sources":["../src/embedded-write-queue.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AA0C5D,UAAU,iBAAiB;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,iBAAiB,GAAG,OAAO,CAEjE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,iBAAiB,GAAG,OAAO,CAEjE;AAED;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,iBAAiB,GAAG,OAAO,CAG3E;AA0BD;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,CAAC,EAC5C,EAAE,EAAE,iBAAiB,EACrB,SAAS,EAAE,OAAO,EAClB,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,CAAC,CAAC,CAwBZ;AAED;;;;;GAKG;AACH,wBAAsB,4BAA4B,CAAC,CAAC,EAClD,EAAE,EAAE,iBAAiB,EACrB,SAAS,EAAE,OAAO,EAClB,SAAS,EAAE,CAAC,WAAW,EAAE,iBAAiB,KAAK,OAAO,CAAC,CAAC,CAAC,GACxD,OAAO,CAAC,CAAC,CAAC,CAeZ"}
|
|
@@ -69,8 +69,27 @@ function usesEmbeddedRevisionFallback(db) {
|
|
|
69
69
|
const url = db?.url ?? "";
|
|
70
70
|
return isEmbeddedDatabase(db) && !/^(?:https?|libsql):\/\//iu.test(url);
|
|
71
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Whether `url` identifies a SQLite/DuckDB in-memory database that is
|
|
74
|
+
* private to its own connection, as opposed to shared state.
|
|
75
|
+
*
|
|
76
|
+
* Bare `:memory:` and `file::memory:` (no query string) each mint a fresh,
|
|
77
|
+
* connection-private database -- `resolveDuckDBUrl(':memory:')` returns it
|
|
78
|
+
* unchanged, with no per-instance suffix -- so two independent handles that
|
|
79
|
+
* both happen to carry this literal string are NOT the same database and
|
|
80
|
+
* must not share a write-queue identity (#2707). A `cache=shared` query
|
|
81
|
+
* parameter (e.g. `file::memory:?cache=shared`) is the opposite case: it is
|
|
82
|
+
* SQLite's own mechanism for making an in-memory database genuinely shared
|
|
83
|
+
* across connections in the process, so a URL carrying it must keep the
|
|
84
|
+
* ordinary string-keyed (shared) behavior.
|
|
85
|
+
*/
|
|
86
|
+
function isPerConnectionMemoryUrl(url) {
|
|
87
|
+
if (url.includes("cache=shared")) return false;
|
|
88
|
+
return url === ":memory:" || url === "file::memory:";
|
|
89
|
+
}
|
|
72
90
|
function queueKey(db) {
|
|
73
|
-
|
|
91
|
+
if (typeof db?.url !== "string" || db.url.length === 0) return db;
|
|
92
|
+
return isPerConnectionMemoryUrl(db.url) ? db : db.url;
|
|
74
93
|
}
|
|
75
94
|
/**
|
|
76
95
|
* Run `operation` after every previously queued write on the same database
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedded-write-queue.js","names":[],"sources":["../src/embedded-write-queue.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { DatabaseInterface } from '@happyvertical/sql';\n\n/**\n * In-process write serialization for embedded database engines (#2360).\n *\n * File-backed SQLite (libsql) allows one writer at a time and the SDK opens a\n * NEW connection for every null-aware upsert attempt (`transaction(\"write\")`),\n * with no busy timeout on either connection. Any other write on the root\n * connection while such a transaction is open — most commonly the change-feed\n * append that follows every model save — collides as `SQLITE_BUSY`, and under\n * sustained concurrency (a `Promise.all` of creates) the two sides livelock\n * through every retry tier. Before the tenant-led conflict target (#2360) the\n * null-aware path was rare (custom conflict keys with NULL values, #1246);\n * with `tenant_id` leading every tenant-scoped default key, EVERY NULL-tenant\n * (global) create takes it, so the collision became the steady state.\n *\n * The queue makes the process a single writer per embedded database for:\n *\n * - a `save()` whose upsert conflict values contain NULL (the only shape the\n * SDK routes through a second-connection transaction), and\n * - the change-feed append (a root-connection write that follows every save).\n * - every model save and delete, so the embedded revision-CAS compare/upsert\n * fallback cannot race an ordinary save, natural-key create, or delete.\n * - a complete root `SmrtObject.withTransaction()` callback, with writes on\n * its bound handle re-entering the same hold instead of self-deadlocking.\n *\n * Ordinary save/delete holds contain database calls only — never an\n * interceptor or hook. Explicit transaction callbacks are caller-controlled,\n * so their bound model writes re-enter the existing hold. PostgreSQL is never\n * serialized here:\n * its null-aware upsert already arbitrates through advisory locks\n * server-side, and cross-process writers exist that an in-process queue could\n * not order anyway (which is also why this queue is a mitigation for embedded\n * engines' in-process writers, not a cross-process guarantee — embedded\n * engines have no cross-process writers in supported deployments).\n */\n\n/** Queue tail per database identity (URL string, or the handle itself). */\nconst urlQueues = new Map<string, Promise<unknown>>();\nconst handleQueues = new WeakMap<object, Promise<unknown>>();\nconst activeQueueKeys = new AsyncLocalStorage<ReadonlySet<string | object>>();\n\ninterface QueueableDatabase {\n url?: string;\n}\n\n/**\n * Whether `db` is an embedded engine (SQLite/DuckDB/JSON — anything that is\n * not PostgreSQL). Handles without a URL are treated as embedded: serializing\n * them is at worst a throughput cost, while not serializing an embedded\n * handle reintroduces the livelock.\n */\nexport function isEmbeddedDatabase(db: QueueableDatabase): boolean {\n return !isPostgresDatabase(db);\n}\n\n/**\n * Whether `db` is a PostgreSQL handle.\n *\n * The revision compare-and-swap predicate is dialect-specific (#2620): only\n * PostgreSQL needs the millisecond-truncating, timezone-tolerant guard, and\n * only PostgreSQL can evaluate `date_trunc`.\n */\nexport function isPostgresDatabase(db: QueueableDatabase): boolean {\n return /^postgres(?:ql)?:/iu.test(db?.url ?? '');\n}\n\n/**\n * Whether revision compare-and-swap needs the process-local read/upsert path.\n *\n * DuckDB/JSON cannot express the timestamp predicate through the generic\n * update API, and local SQLite has only the process's writers in supported\n * deployments. Remote LibSQL/Turso is different: other processes may write\n * the same database, so it must use one conditional UPDATE at the server.\n */\nexport function usesEmbeddedRevisionFallback(db: QueueableDatabase): boolean {\n const url = db?.url ?? '';\n return isEmbeddedDatabase(db) && !/^(?:https?|libsql):\\/\\//iu.test(url);\n}\n\nfunction queueKey(db: QueueableDatabase): string | object {\n
|
|
1
|
+
{"version":3,"file":"embedded-write-queue.js","names":[],"sources":["../src/embedded-write-queue.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { DatabaseInterface } from '@happyvertical/sql';\n\n/**\n * In-process write serialization for embedded database engines (#2360).\n *\n * File-backed SQLite (libsql) allows one writer at a time and the SDK opens a\n * NEW connection for every null-aware upsert attempt (`transaction(\"write\")`),\n * with no busy timeout on either connection. Any other write on the root\n * connection while such a transaction is open — most commonly the change-feed\n * append that follows every model save — collides as `SQLITE_BUSY`, and under\n * sustained concurrency (a `Promise.all` of creates) the two sides livelock\n * through every retry tier. Before the tenant-led conflict target (#2360) the\n * null-aware path was rare (custom conflict keys with NULL values, #1246);\n * with `tenant_id` leading every tenant-scoped default key, EVERY NULL-tenant\n * (global) create takes it, so the collision became the steady state.\n *\n * The queue makes the process a single writer per embedded database for:\n *\n * - a `save()` whose upsert conflict values contain NULL (the only shape the\n * SDK routes through a second-connection transaction), and\n * - the change-feed append (a root-connection write that follows every save).\n * - every model save and delete, so the embedded revision-CAS compare/upsert\n * fallback cannot race an ordinary save, natural-key create, or delete.\n * - a complete root `SmrtObject.withTransaction()` callback, with writes on\n * its bound handle re-entering the same hold instead of self-deadlocking.\n *\n * Ordinary save/delete holds contain database calls only — never an\n * interceptor or hook. Explicit transaction callbacks are caller-controlled,\n * so their bound model writes re-enter the existing hold. PostgreSQL is never\n * serialized here:\n * its null-aware upsert already arbitrates through advisory locks\n * server-side, and cross-process writers exist that an in-process queue could\n * not order anyway (which is also why this queue is a mitigation for embedded\n * engines' in-process writers, not a cross-process guarantee — embedded\n * engines have no cross-process writers in supported deployments).\n */\n\n/** Queue tail per database identity (URL string, or the handle itself). */\nconst urlQueues = new Map<string, Promise<unknown>>();\nconst handleQueues = new WeakMap<object, Promise<unknown>>();\nconst activeQueueKeys = new AsyncLocalStorage<ReadonlySet<string | object>>();\n\ninterface QueueableDatabase {\n url?: string;\n}\n\n/**\n * Whether `db` is an embedded engine (SQLite/DuckDB/JSON — anything that is\n * not PostgreSQL). Handles without a URL are treated as embedded: serializing\n * them is at worst a throughput cost, while not serializing an embedded\n * handle reintroduces the livelock.\n */\nexport function isEmbeddedDatabase(db: QueueableDatabase): boolean {\n return !isPostgresDatabase(db);\n}\n\n/**\n * Whether `db` is a PostgreSQL handle.\n *\n * The revision compare-and-swap predicate is dialect-specific (#2620): only\n * PostgreSQL needs the millisecond-truncating, timezone-tolerant guard, and\n * only PostgreSQL can evaluate `date_trunc`.\n */\nexport function isPostgresDatabase(db: QueueableDatabase): boolean {\n return /^postgres(?:ql)?:/iu.test(db?.url ?? '');\n}\n\n/**\n * Whether revision compare-and-swap needs the process-local read/upsert path.\n *\n * DuckDB/JSON cannot express the timestamp predicate through the generic\n * update API, and local SQLite has only the process's writers in supported\n * deployments. Remote LibSQL/Turso is different: other processes may write\n * the same database, so it must use one conditional UPDATE at the server.\n */\nexport function usesEmbeddedRevisionFallback(db: QueueableDatabase): boolean {\n const url = db?.url ?? '';\n return isEmbeddedDatabase(db) && !/^(?:https?|libsql):\\/\\//iu.test(url);\n}\n\n/**\n * Whether `url` identifies a SQLite/DuckDB in-memory database that is\n * private to its own connection, as opposed to shared state.\n *\n * Bare `:memory:` and `file::memory:` (no query string) each mint a fresh,\n * connection-private database -- `resolveDuckDBUrl(':memory:')` returns it\n * unchanged, with no per-instance suffix -- so two independent handles that\n * both happen to carry this literal string are NOT the same database and\n * must not share a write-queue identity (#2707). A `cache=shared` query\n * parameter (e.g. `file::memory:?cache=shared`) is the opposite case: it is\n * SQLite's own mechanism for making an in-memory database genuinely shared\n * across connections in the process, so a URL carrying it must keep the\n * ordinary string-keyed (shared) behavior.\n */\nfunction isPerConnectionMemoryUrl(url: string): boolean {\n if (url.includes('cache=shared')) return false;\n return url === ':memory:' || url === 'file::memory:';\n}\n\nfunction queueKey(db: QueueableDatabase): string | object {\n if (typeof db?.url !== 'string' || db.url.length === 0) return db;\n return isPerConnectionMemoryUrl(db.url) ? db : db.url;\n}\n\n/**\n * Run `operation` after every previously queued write on the same database\n * has settled. When `serialize` is false the operation runs immediately.\n */\nexport async function withEmbeddedWriteQueue<T>(\n db: QueueableDatabase,\n serialize: boolean,\n operation: () => Promise<T>,\n): Promise<T> {\n if (!serialize) return operation();\n\n const key = queueKey(db);\n const active = activeQueueKeys.getStore();\n if (active?.has(key)) return operation();\n const previous =\n (typeof key === 'string' ? urlQueues.get(key) : handleQueues.get(key)) ??\n Promise.resolve();\n const run = () =>\n activeQueueKeys.run(new Set([...(active ?? []), key]), operation);\n const next = previous.then(run, run);\n // The stored tail must never reject, or one failed write would poison the\n // queue for every later writer.\n const tail = next.catch(() => undefined);\n if (typeof key === 'string') {\n urlQueues.set(key, tail);\n void tail.finally(() => {\n if (urlQueues.get(key) === tail) urlQueues.delete(key);\n });\n } else {\n handleQueues.set(key, tail);\n }\n return next;\n}\n\n/**\n * Serialize a complete embedded root transaction with ordinary model writes.\n * The transaction handle is registered as a reentrant alias so saves and\n * change-feed appends inside the callback do not queue behind their own root\n * transaction.\n */\nexport async function withEmbeddedWriteTransaction<T>(\n db: DatabaseInterface,\n serialize: boolean,\n operation: (transaction: DatabaseInterface) => Promise<T>,\n): Promise<T> {\n const transaction = db.transaction?.bind(db);\n if (!transaction) {\n throw new Error('Database transaction support is required');\n }\n return withEmbeddedWriteQueue(db, serialize, () =>\n transaction((bound) => {\n if (!serialize) return operation(bound);\n const active = activeQueueKeys.getStore();\n return activeQueueKeys.run(\n new Set([...(active ?? []), queueKey(bound)]),\n () => operation(bound),\n );\n }),\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,IAAM,4BAAY,IAAI,IAA8B;AACpD,IAAM,+BAAe,IAAI,QAAkC;AAC3D,IAAM,kBAAkB,IAAI,kBAAgD;;;;;;;AAY5E,SAAgB,mBAAmB,IAAgC;CACjE,OAAO,CAAC,mBAAmB,EAAE;AAC/B;;;;;;;;AASA,SAAgB,mBAAmB,IAAgC;CACjE,OAAO,sBAAsB,KAAK,IAAI,OAAO,EAAE;AACjD;;;;;;;;;AAUA,SAAgB,6BAA6B,IAAgC;CAC3E,MAAM,MAAM,IAAI,OAAO;CACvB,OAAO,mBAAmB,EAAE,KAAK,CAAC,4BAA4B,KAAK,GAAG;AACxE;;;;;;;;;;;;;;;AAgBA,SAAS,yBAAyB,KAAsB;CACtD,IAAI,IAAI,SAAS,cAAc,GAAG,OAAO;CACzC,OAAO,QAAQ,cAAc,QAAQ;AACvC;AAEA,SAAS,SAAS,IAAwC;CACxD,IAAI,OAAO,IAAI,QAAQ,YAAY,GAAG,IAAI,WAAW,GAAG,OAAO;CAC/D,OAAO,yBAAyB,GAAG,GAAG,IAAI,KAAK,GAAG;AACpD;;;;;AAMA,eAAsB,uBACpB,IACA,WACA,WACY;CACZ,IAAI,CAAC,WAAW,OAAO,UAAU;CAEjC,MAAM,MAAM,SAAS,EAAE;CACvB,MAAM,SAAS,gBAAgB,SAAS;CACxC,IAAI,QAAQ,IAAI,GAAG,GAAG,OAAO,UAAU;CACvC,MAAM,YACH,OAAO,QAAQ,WAAW,UAAU,IAAI,GAAG,IAAI,aAAa,IAAI,GAAG,MACpE,QAAQ,QAAQ;CAClB,MAAM,YACJ,gBAAgB,oBAAI,IAAI,IAAI,CAAC,GAAI,UAAU,CAAC,GAAI,GAAG,CAAC,GAAG,SAAS;CAClE,MAAM,OAAO,SAAS,KAAK,KAAK,GAAG;CAGnC,MAAM,OAAO,KAAK,YAAY,KAAA,CAAS;CACvC,IAAI,OAAO,QAAQ,UAAU;EAC3B,UAAU,IAAI,KAAK,IAAI;EACvB,KAAU,cAAc;GACtB,IAAI,UAAU,IAAI,GAAG,MAAM,MAAM,UAAU,OAAO,GAAG;EACvD,CAAC;CACH,OACE,aAAa,IAAI,KAAK,IAAI;CAE5B,OAAO;AACT;;;;;;;AAQA,eAAsB,6BACpB,IACA,WACA,WACY;CACZ,MAAM,cAAc,GAAG,aAAa,KAAK,EAAE;CAC3C,IAAI,CAAC,aACH,MAAM,IAAI,MAAM,0CAA0C;CAE5D,OAAO,uBAAuB,IAAI,iBAChC,aAAa,UAAU;EACrB,IAAI,CAAC,WAAW,OAAO,UAAU,KAAK;EACtC,MAAM,SAAS,gBAAgB,SAAS;EACxC,OAAO,gBAAgB,oBACrB,IAAI,IAAI,CAAC,GAAI,UAAU,CAAC,GAAI,SAAS,KAAK,CAAC,CAAC,SACtC,UAAU,KAAK,CACvB;CACF,CAAC,CACH;AACF"}
|
|
@@ -15,8 +15,8 @@ export type { ToolEffect } from '../registry/types.js';
|
|
|
15
15
|
* even when a subclass declares its own override (e.g. `User.save()` at
|
|
16
16
|
* `packages/users/src/models/User.ts`). An override is still the same
|
|
17
17
|
* lifecycle operation, not a new one (#2638). `delete` itself is a CRUD verb
|
|
18
|
-
* `CLIGenerator`/`MCPGenerator` already
|
|
19
|
-
* here.
|
|
18
|
+
* `packages/cli/src/cli-generator.ts`'s `CLIGenerator`/`MCPGenerator` already
|
|
19
|
+
* special-case, so it is not repeated here.
|
|
20
20
|
*
|
|
21
21
|
* Scope is deliberately narrower than "every public method on
|
|
22
22
|
* SmrtObject/SmrtClass/SmrtCollection":
|
|
@@ -24,11 +24,13 @@ export type { ToolEffect } from '../registry/types.js';
|
|
|
24
24
|
* - AI operations `is()`/`do()`/`describe()` are declared on `SmrtObject`
|
|
25
25
|
* but are explicitly designed to be overridden with domain-specific
|
|
26
26
|
* behavior and exposed as a distinct action -- confirmed by existing,
|
|
27
|
-
* intentional coverage
|
|
28
|
-
*
|
|
29
|
-
* `vite-plugin/generated-client-integration.test.ts`'s `ArtCollection.
|
|
27
|
+
* intentional coverage
|
|
28
|
+
* (`vite-plugin/generated-client-integration.test.ts`'s `ArtCollection.
|
|
30
29
|
* describe(tone)` with its own declared API route). Excluding them here
|
|
31
|
-
* would regress real, working behavior.
|
|
30
|
+
* would regress real, working behavior. (The sibling
|
|
31
|
+
* `generators/cli-commands.spec.ts` fixture that used to cover the same
|
|
32
|
+
* `describe()` custom action was retired with core's `CLIGenerator`,
|
|
33
|
+
* #2664; this remaining fixture still exercises the behavior.)
|
|
32
34
|
* - Relationship loading (`loadRelated`/`loadRelatedMany`/`getRelated`/
|
|
33
35
|
* `isRelatedLoaded`), memory (`remember`/`recall`/`recallAll`/`forget`/
|
|
34
36
|
* `forgetScope`), embeddings (`generateEmbeddings`/`getEmbedding`/
|
|
@@ -89,29 +91,28 @@ export interface ResolvableMethod {
|
|
|
89
91
|
* for an object, given its transport config's `include`/`exclude` and its
|
|
90
92
|
* method map: every public method, minus CRUD verbs, minus framework
|
|
91
93
|
* lifecycle methods, restricted to `include` when present and always minus
|
|
92
|
-
* `exclude`.
|
|
93
|
-
*
|
|
94
|
+
* `exclude`. Its sole caller as of #2664 (`CLIGenerator` and the
|
|
95
|
+
* `generateCLIModule()` virtual module were retired):
|
|
94
96
|
*
|
|
95
|
-
* - `CLIGenerator.listCommands()` (over the live `ObjectRegistry`).
|
|
96
97
|
* - `findCliApiCoherenceViolations`'s bare-`cli: true`/`cli: {}` branch
|
|
97
98
|
* (over the static manifest, no explicit `include`) -- see
|
|
98
99
|
* `resolveCliActionSet` in `vite-plugin/sveltekit-generator.ts`.
|
|
99
|
-
* - `generateCLIModule()` in `vite-plugin/index.ts`, which generates the
|
|
100
|
-
* `smrt:cli` virtual module's static command metadata. It layers one
|
|
101
|
-
* additional filter on top of this function's result: a leading `_` on
|
|
102
|
-
* the method name, because the manifest's `isPublic` is unreliable there
|
|
103
|
-
* (see that call site's own comment) and this function has no other
|
|
104
|
-
* signal to exclude an internal-by-convention method.
|
|
105
100
|
*
|
|
106
101
|
* NOT the one universal resolution, and deliberately not reused by every
|
|
107
102
|
* caller that resolves a CLI command set:
|
|
108
103
|
*
|
|
109
|
-
* - `CLIGenerator.assertCommandExposed()`
|
|
110
|
-
* method branch — it
|
|
111
|
-
* its own inline
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
* test.
|
|
104
|
+
* - Core's now-retired `CLIGenerator.assertCommandExposed()` (#2664) did not
|
|
105
|
+
* call this for its custom-method branch — it checked
|
|
106
|
+
* `isFrameworkLifecycleMethod()` directly plus its own inline
|
|
107
|
+
* public/include/exclude logic, so it could give a distinct error message
|
|
108
|
+
* per failure reason (unknown vs. not public vs. not enabled vs. lifecycle
|
|
109
|
+
* method) rather than a single boolean membership test. The shipped local
|
|
110
|
+
* CLI's `generateObjectCommands()` (`packages/cli/src/cli-generator.ts`)
|
|
111
|
+
* has no equivalent of that gate at all today — it filters on reserved-CRUD
|
|
112
|
+
* name collision, `isPublic`, and `exclude`/`include`, but never
|
|
113
|
+
* `isFrameworkLifecycleMethod()`, so a locally overridden lifecycle method
|
|
114
|
+
* IS a reachable command there (see `knowledge.ts`'s `configuredOperations()`
|
|
115
|
+
* docblock for the same caveat).
|
|
115
116
|
* - `findCliApiCoherenceViolations`'s EXPLICIT-`cli.include` branch
|
|
116
117
|
* deliberately bypasses this function too: an `include` entry naming a
|
|
117
118
|
* typo, a getter, or a private/protected method must still surface as
|
|
@@ -121,12 +122,15 @@ export interface ResolvableMethod {
|
|
|
121
122
|
* `resolveCliActionSet` in `vite-plugin/sveltekit-generator.ts` for the
|
|
122
123
|
* full rationale; do not "simplify" that branch onto this function.
|
|
123
124
|
*
|
|
124
|
-
* `crudActionNames` stays a parameter
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
125
|
+
* `crudActionNames` stays a parameter even though every caller now passes
|
|
126
|
+
* {@link CRUD_OPERATIONS} (#2665 retired the last of the inline copies at
|
|
127
|
+
* `vite-plugin/sveltekit-generator.ts`, following `CLIGenerator`'s #2646
|
|
128
|
+
* switch; `vite-plugin/index.ts`'s copy backed the `generateCLIModule()`
|
|
129
|
+
* emitter #2664 later retired, so `index.ts` no longer calls this function
|
|
130
|
+
* or imports `CRUD_OPERATIONS` at all). Removing the parameter is a separate,
|
|
131
|
+
* unevidenced call this fix does not make -- it would foreclose a caller
|
|
132
|
+
* that legitimately needs a different verb set, and no such need has been
|
|
133
|
+
* demonstrated either way.
|
|
130
134
|
*/
|
|
131
135
|
export declare function resolveCustomActionNames(methods: Iterable<[string, ResolvableMethod]>, config: {
|
|
132
136
|
include?: string[];
|
|
@@ -144,11 +148,25 @@ export declare function resolveCustomActionNames(methods: Iterable<[string, Reso
|
|
|
144
148
|
* What a collision means differs by EMITTER, so consult the one you are
|
|
145
149
|
* changing rather than assuming a single rule. The reservation lives at each
|
|
146
150
|
* emission site, not here, and several emitters still keep their own inline
|
|
147
|
-
* verb array — find them with
|
|
151
|
+
* verb array — find them with a multi-line-tolerant search (#2665 turned the
|
|
152
|
+
* single-line form of this grep blind to a wrapped literal like
|
|
153
|
+
* `templates/default-ui.ts`'s `CRUD_OPERATIONS_FOR_BROWSER_TEMPLATE`):
|
|
148
154
|
*
|
|
149
|
-
*
|
|
155
|
+
* rg -U "'list',\s*'get',\s*'create',\s*'update',\s*'delete'" packages --type ts | grep -v include:
|
|
150
156
|
*
|
|
151
|
-
*
|
|
157
|
+
* This is a STARTING POINT, not a closed inventory: the `include:` filter
|
|
158
|
+
* only drops a single-line `include: [...]` block, so a wrapped one (e.g.
|
|
159
|
+
* `packages/content/src/content.ts`) still surfaces, and the pattern also
|
|
160
|
+
* matches non-decorator uses of the same five-word literal that have nothing
|
|
161
|
+
* to do with this collision rule (e.g. `packages/smrt-workbench/src/
|
|
162
|
+
* discovery.ts`'s `CRUD_ACTIONS`, `packages/smrt-dev-mcp/.../
|
|
163
|
+
* introspect-project.ts`'s `DEFAULT_MCP_OPERATIONS`). Triage each hit rather
|
|
164
|
+
* than trusting the raw list. Within `packages/core/src/vite-plugin/`
|
|
165
|
+
* specifically, the emitter-relevant survivors as of #2665 are
|
|
166
|
+
* `generated-client.ts` and `templates/default-ui.ts` (the latter
|
|
167
|
+
* deliberate, value-pinned by `issue-2665-crud-verb-consolidation.spec.ts`);
|
|
168
|
+
* `scanner/manifest-generator.ts` and `packages/users/src/sveltekit/
|
|
169
|
+
* resource-list-handler.ts` are outside this PR's scope.
|
|
152
170
|
*
|
|
153
171
|
* The sites this rule was audited against (#2646), NOT an exhaustive
|
|
154
172
|
* inventory:
|
|
@@ -158,10 +176,6 @@ export declare function resolveCustomActionNames(methods: Iterable<[string, Reso
|
|
|
158
176
|
* built-in list whichever branch emitted it: the class's method could never
|
|
159
177
|
* run, and emitting one would hand the caller an operation `include` never
|
|
160
178
|
* named.
|
|
161
|
-
* - `generators/cli.ts` (`CLIGenerator`) — unconditional, exact.
|
|
162
|
-
* `assertCommandExposed` returns inside its `isCrud` branch, so a CRUD-named
|
|
163
|
-
* action never reaches custom-method resolution, and `listCommands` skips
|
|
164
|
-
* those names outright.
|
|
165
179
|
* - `packages/cli/src/cli-generator.ts`, behind the shipped `smrt` object
|
|
166
180
|
* commands — reserves only where the CRUD command is actually emitted, and
|
|
167
181
|
* reserves the command NAMES AND THEIR ALIASES (`ls`, `show`, `new`, `edit`,
|
|
@@ -170,8 +184,9 @@ export declare function resolveCustomActionNames(methods: Iterable<[string, Reso
|
|
|
170
184
|
* command carries its own handler invoking the class's method, so with
|
|
171
185
|
* `cli: { include: ['list', 'get'] }` neither a public `create()` nor an
|
|
172
186
|
* `edit()` collides with anything and both stay reachable.
|
|
173
|
-
* - `vite-plugin/sveltekit-generator.ts` — unconditional, exact,
|
|
174
|
-
* `STANDARD_API_ACTIONS`
|
|
187
|
+
* - `vite-plugin/sveltekit-generator.ts` — unconditional, exact, via
|
|
188
|
+
* {@link isCrudOperation} (#2665; previously its own `STANDARD_API_ACTIONS`
|
|
189
|
+
* copy).
|
|
175
190
|
* - `vite-plugin/web-collections.ts` + `tool-schema.ts` — case-folded (ids are
|
|
176
191
|
* lowercased whole like MCP's), CONDITIONAL, and scoped PER COLLECTION.
|
|
177
192
|
* Applied in both of `selectWebMcpToolEntries`' loops and at the shared
|
|
@@ -194,9 +209,26 @@ export declare function resolveCustomActionNames(methods: Iterable<[string, Reso
|
|
|
194
209
|
* `resolveApiActionSet` stays exact-match: REST routes keep declared casing,
|
|
195
210
|
* so `/products/List` is genuinely distinct from `/products`.
|
|
196
211
|
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
* `
|
|
212
|
+
* `vite-plugin/api-client-entries.ts` and `vite-plugin/sveltekit-generator.ts`
|
|
213
|
+
* now import {@link CRUD_OPERATIONS} (or {@link isCrudOperation} where a bare
|
|
214
|
+
* `.includes()` on the readonly tuple failed the stricter
|
|
215
|
+
* `tsconfig.typecheck.json`) rather than keeping their own verb copies
|
|
216
|
+
* (#2665). `vite-plugin/index.ts`'s copy backed `generateCLIModule()`, which
|
|
217
|
+
* #2664 retired along with the rest of the unused `smrt-virt-cli` module, so
|
|
218
|
+
* `index.ts` no longer imports anything from this module at all.
|
|
219
|
+
* `vite-plugin/templates/default-ui.ts` keeps its
|
|
220
|
+
* verb list as a local literal deliberately: `src/vite-plugin/templates/**`
|
|
221
|
+
* is excluded from both tsconfigs and the vite-dts build graph, and the
|
|
222
|
+
* package build copies that directory to `dist/` verbatim rather than
|
|
223
|
+
* compiling it -- see the module's own comment. Its `.ts` source is never
|
|
224
|
+
* resolved or bundled by anything in this package, so a static import of the
|
|
225
|
+
* Node-side `isCrudOperation`/`CRUD_OPERATIONS` (which pull in
|
|
226
|
+
* `tools/tool-generator.js`) would never be satisfied there. The
|
|
227
|
+
* consolidation test instead asserts the literal's *value* matches
|
|
228
|
+
* {@link CRUD_OPERATIONS} rather than assuming it imports it. Consolidating
|
|
229
|
+
* the lists did not change any of the
|
|
230
|
+
* per-emitter RULES documented above -- each site still decides case-folding
|
|
231
|
+
* and conditionality for itself.
|
|
200
232
|
*
|
|
201
233
|
* Read the list through {@link isCrudOperation} or {@link isCrudToolAction}
|
|
202
234
|
* rather than re-declaring it; the two differ only in case folding, because the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom-action.d.ts","sourceRoot":"","sources":["../../src/generators/custom-action.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG5D,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,YAAY,CAAC;AACtD,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD
|
|
1
|
+
{"version":3,"file":"custom-action.d.ts","sourceRoot":"","sources":["../../src/generators/custom-action.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,8 +13,8 @@ import { convertTypeToJsonSchema } from "../tools/tool-generator.js";
|
|
|
13
13
|
* even when a subclass declares its own override (e.g. `User.save()` at
|
|
14
14
|
* `packages/users/src/models/User.ts`). An override is still the same
|
|
15
15
|
* lifecycle operation, not a new one (#2638). `delete` itself is a CRUD verb
|
|
16
|
-
* `CLIGenerator`/`MCPGenerator` already
|
|
17
|
-
* here.
|
|
16
|
+
* `packages/cli/src/cli-generator.ts`'s `CLIGenerator`/`MCPGenerator` already
|
|
17
|
+
* special-case, so it is not repeated here.
|
|
18
18
|
*
|
|
19
19
|
* Scope is deliberately narrower than "every public method on
|
|
20
20
|
* SmrtObject/SmrtClass/SmrtCollection":
|
|
@@ -22,11 +22,13 @@ import { convertTypeToJsonSchema } from "../tools/tool-generator.js";
|
|
|
22
22
|
* - AI operations `is()`/`do()`/`describe()` are declared on `SmrtObject`
|
|
23
23
|
* but are explicitly designed to be overridden with domain-specific
|
|
24
24
|
* behavior and exposed as a distinct action -- confirmed by existing,
|
|
25
|
-
* intentional coverage
|
|
26
|
-
*
|
|
27
|
-
* `vite-plugin/generated-client-integration.test.ts`'s `ArtCollection.
|
|
25
|
+
* intentional coverage
|
|
26
|
+
* (`vite-plugin/generated-client-integration.test.ts`'s `ArtCollection.
|
|
28
27
|
* describe(tone)` with its own declared API route). Excluding them here
|
|
29
|
-
* would regress real, working behavior.
|
|
28
|
+
* would regress real, working behavior. (The sibling
|
|
29
|
+
* `generators/cli-commands.spec.ts` fixture that used to cover the same
|
|
30
|
+
* `describe()` custom action was retired with core's `CLIGenerator`,
|
|
31
|
+
* #2664; this remaining fixture still exercises the behavior.)
|
|
30
32
|
* - Relationship loading (`loadRelated`/`loadRelatedMany`/`getRelated`/
|
|
31
33
|
* `isRelatedLoaded`), memory (`remember`/`recall`/`recallAll`/`forget`/
|
|
32
34
|
* `forgetScope`), embeddings (`generateEmbeddings`/`getEmbedding`/
|
|
@@ -106,29 +108,28 @@ function isFrameworkLifecycleMethod(methodName) {
|
|
|
106
108
|
* for an object, given its transport config's `include`/`exclude` and its
|
|
107
109
|
* method map: every public method, minus CRUD verbs, minus framework
|
|
108
110
|
* lifecycle methods, restricted to `include` when present and always minus
|
|
109
|
-
* `exclude`.
|
|
110
|
-
*
|
|
111
|
+
* `exclude`. Its sole caller as of #2664 (`CLIGenerator` and the
|
|
112
|
+
* `generateCLIModule()` virtual module were retired):
|
|
111
113
|
*
|
|
112
|
-
* - `CLIGenerator.listCommands()` (over the live `ObjectRegistry`).
|
|
113
114
|
* - `findCliApiCoherenceViolations`'s bare-`cli: true`/`cli: {}` branch
|
|
114
115
|
* (over the static manifest, no explicit `include`) -- see
|
|
115
116
|
* `resolveCliActionSet` in `vite-plugin/sveltekit-generator.ts`.
|
|
116
|
-
* - `generateCLIModule()` in `vite-plugin/index.ts`, which generates the
|
|
117
|
-
* `smrt:cli` virtual module's static command metadata. It layers one
|
|
118
|
-
* additional filter on top of this function's result: a leading `_` on
|
|
119
|
-
* the method name, because the manifest's `isPublic` is unreliable there
|
|
120
|
-
* (see that call site's own comment) and this function has no other
|
|
121
|
-
* signal to exclude an internal-by-convention method.
|
|
122
117
|
*
|
|
123
118
|
* NOT the one universal resolution, and deliberately not reused by every
|
|
124
119
|
* caller that resolves a CLI command set:
|
|
125
120
|
*
|
|
126
|
-
* - `CLIGenerator.assertCommandExposed()`
|
|
127
|
-
* method branch — it
|
|
128
|
-
* its own inline
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* test.
|
|
121
|
+
* - Core's now-retired `CLIGenerator.assertCommandExposed()` (#2664) did not
|
|
122
|
+
* call this for its custom-method branch — it checked
|
|
123
|
+
* `isFrameworkLifecycleMethod()` directly plus its own inline
|
|
124
|
+
* public/include/exclude logic, so it could give a distinct error message
|
|
125
|
+
* per failure reason (unknown vs. not public vs. not enabled vs. lifecycle
|
|
126
|
+
* method) rather than a single boolean membership test. The shipped local
|
|
127
|
+
* CLI's `generateObjectCommands()` (`packages/cli/src/cli-generator.ts`)
|
|
128
|
+
* has no equivalent of that gate at all today — it filters on reserved-CRUD
|
|
129
|
+
* name collision, `isPublic`, and `exclude`/`include`, but never
|
|
130
|
+
* `isFrameworkLifecycleMethod()`, so a locally overridden lifecycle method
|
|
131
|
+
* IS a reachable command there (see `knowledge.ts`'s `configuredOperations()`
|
|
132
|
+
* docblock for the same caveat).
|
|
132
133
|
* - `findCliApiCoherenceViolations`'s EXPLICIT-`cli.include` branch
|
|
133
134
|
* deliberately bypasses this function too: an `include` entry naming a
|
|
134
135
|
* typo, a getter, or a private/protected method must still surface as
|
|
@@ -138,12 +139,15 @@ function isFrameworkLifecycleMethod(methodName) {
|
|
|
138
139
|
* `resolveCliActionSet` in `vite-plugin/sveltekit-generator.ts` for the
|
|
139
140
|
* full rationale; do not "simplify" that branch onto this function.
|
|
140
141
|
*
|
|
141
|
-
* `crudActionNames` stays a parameter
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
142
|
+
* `crudActionNames` stays a parameter even though every caller now passes
|
|
143
|
+
* {@link CRUD_OPERATIONS} (#2665 retired the last of the inline copies at
|
|
144
|
+
* `vite-plugin/sveltekit-generator.ts`, following `CLIGenerator`'s #2646
|
|
145
|
+
* switch; `vite-plugin/index.ts`'s copy backed the `generateCLIModule()`
|
|
146
|
+
* emitter #2664 later retired, so `index.ts` no longer calls this function
|
|
147
|
+
* or imports `CRUD_OPERATIONS` at all). Removing the parameter is a separate,
|
|
148
|
+
* unevidenced call this fix does not make -- it would foreclose a caller
|
|
149
|
+
* that legitimately needs a different verb set, and no such need has been
|
|
150
|
+
* demonstrated either way.
|
|
147
151
|
*/
|
|
148
152
|
function resolveCustomActionNames(methods, config, crudActionNames) {
|
|
149
153
|
const included = config?.include;
|
|
@@ -171,11 +175,25 @@ function resolveCustomActionNames(methods, config, crudActionNames) {
|
|
|
171
175
|
* What a collision means differs by EMITTER, so consult the one you are
|
|
172
176
|
* changing rather than assuming a single rule. The reservation lives at each
|
|
173
177
|
* emission site, not here, and several emitters still keep their own inline
|
|
174
|
-
* verb array — find them with
|
|
178
|
+
* verb array — find them with a multi-line-tolerant search (#2665 turned the
|
|
179
|
+
* single-line form of this grep blind to a wrapped literal like
|
|
180
|
+
* `templates/default-ui.ts`'s `CRUD_OPERATIONS_FOR_BROWSER_TEMPLATE`):
|
|
175
181
|
*
|
|
176
|
-
*
|
|
182
|
+
* rg -U "'list',\s*'get',\s*'create',\s*'update',\s*'delete'" packages --type ts | grep -v include:
|
|
177
183
|
*
|
|
178
|
-
*
|
|
184
|
+
* This is a STARTING POINT, not a closed inventory: the `include:` filter
|
|
185
|
+
* only drops a single-line `include: [...]` block, so a wrapped one (e.g.
|
|
186
|
+
* `packages/content/src/content.ts`) still surfaces, and the pattern also
|
|
187
|
+
* matches non-decorator uses of the same five-word literal that have nothing
|
|
188
|
+
* to do with this collision rule (e.g. `packages/smrt-workbench/src/
|
|
189
|
+
* discovery.ts`'s `CRUD_ACTIONS`, `packages/smrt-dev-mcp/.../
|
|
190
|
+
* introspect-project.ts`'s `DEFAULT_MCP_OPERATIONS`). Triage each hit rather
|
|
191
|
+
* than trusting the raw list. Within `packages/core/src/vite-plugin/`
|
|
192
|
+
* specifically, the emitter-relevant survivors as of #2665 are
|
|
193
|
+
* `generated-client.ts` and `templates/default-ui.ts` (the latter
|
|
194
|
+
* deliberate, value-pinned by `issue-2665-crud-verb-consolidation.spec.ts`);
|
|
195
|
+
* `scanner/manifest-generator.ts` and `packages/users/src/sveltekit/
|
|
196
|
+
* resource-list-handler.ts` are outside this PR's scope.
|
|
179
197
|
*
|
|
180
198
|
* The sites this rule was audited against (#2646), NOT an exhaustive
|
|
181
199
|
* inventory:
|
|
@@ -185,10 +203,6 @@ function resolveCustomActionNames(methods, config, crudActionNames) {
|
|
|
185
203
|
* built-in list whichever branch emitted it: the class's method could never
|
|
186
204
|
* run, and emitting one would hand the caller an operation `include` never
|
|
187
205
|
* named.
|
|
188
|
-
* - `generators/cli.ts` (`CLIGenerator`) — unconditional, exact.
|
|
189
|
-
* `assertCommandExposed` returns inside its `isCrud` branch, so a CRUD-named
|
|
190
|
-
* action never reaches custom-method resolution, and `listCommands` skips
|
|
191
|
-
* those names outright.
|
|
192
206
|
* - `packages/cli/src/cli-generator.ts`, behind the shipped `smrt` object
|
|
193
207
|
* commands — reserves only where the CRUD command is actually emitted, and
|
|
194
208
|
* reserves the command NAMES AND THEIR ALIASES (`ls`, `show`, `new`, `edit`,
|
|
@@ -197,8 +211,9 @@ function resolveCustomActionNames(methods, config, crudActionNames) {
|
|
|
197
211
|
* command carries its own handler invoking the class's method, so with
|
|
198
212
|
* `cli: { include: ['list', 'get'] }` neither a public `create()` nor an
|
|
199
213
|
* `edit()` collides with anything and both stay reachable.
|
|
200
|
-
* - `vite-plugin/sveltekit-generator.ts` — unconditional, exact,
|
|
201
|
-
* `STANDARD_API_ACTIONS`
|
|
214
|
+
* - `vite-plugin/sveltekit-generator.ts` — unconditional, exact, via
|
|
215
|
+
* {@link isCrudOperation} (#2665; previously its own `STANDARD_API_ACTIONS`
|
|
216
|
+
* copy).
|
|
202
217
|
* - `vite-plugin/web-collections.ts` + `tool-schema.ts` — case-folded (ids are
|
|
203
218
|
* lowercased whole like MCP's), CONDITIONAL, and scoped PER COLLECTION.
|
|
204
219
|
* Applied in both of `selectWebMcpToolEntries`' loops and at the shared
|
|
@@ -221,9 +236,26 @@ function resolveCustomActionNames(methods, config, crudActionNames) {
|
|
|
221
236
|
* `resolveApiActionSet` stays exact-match: REST routes keep declared casing,
|
|
222
237
|
* so `/products/List` is genuinely distinct from `/products`.
|
|
223
238
|
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
* `
|
|
239
|
+
* `vite-plugin/api-client-entries.ts` and `vite-plugin/sveltekit-generator.ts`
|
|
240
|
+
* now import {@link CRUD_OPERATIONS} (or {@link isCrudOperation} where a bare
|
|
241
|
+
* `.includes()` on the readonly tuple failed the stricter
|
|
242
|
+
* `tsconfig.typecheck.json`) rather than keeping their own verb copies
|
|
243
|
+
* (#2665). `vite-plugin/index.ts`'s copy backed `generateCLIModule()`, which
|
|
244
|
+
* #2664 retired along with the rest of the unused `smrt-virt-cli` module, so
|
|
245
|
+
* `index.ts` no longer imports anything from this module at all.
|
|
246
|
+
* `vite-plugin/templates/default-ui.ts` keeps its
|
|
247
|
+
* verb list as a local literal deliberately: `src/vite-plugin/templates/**`
|
|
248
|
+
* is excluded from both tsconfigs and the vite-dts build graph, and the
|
|
249
|
+
* package build copies that directory to `dist/` verbatim rather than
|
|
250
|
+
* compiling it -- see the module's own comment. Its `.ts` source is never
|
|
251
|
+
* resolved or bundled by anything in this package, so a static import of the
|
|
252
|
+
* Node-side `isCrudOperation`/`CRUD_OPERATIONS` (which pull in
|
|
253
|
+
* `tools/tool-generator.js`) would never be satisfied there. The
|
|
254
|
+
* consolidation test instead asserts the literal's *value* matches
|
|
255
|
+
* {@link CRUD_OPERATIONS} rather than assuming it imports it. Consolidating
|
|
256
|
+
* the lists did not change any of the
|
|
257
|
+
* per-emitter RULES documented above -- each site still decides case-folding
|
|
258
|
+
* and conditionality for itself.
|
|
227
259
|
*
|
|
228
260
|
* Read the list through {@link isCrudOperation} or {@link isCrudToolAction}
|
|
229
261
|
* rather than re-declaring it; the two differ only in case folding, because the
|