@happyvertical/smrt-core 0.40.61 → 0.40.63
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 +16 -3
- package/README.md +32 -1
- package/agents/generators.md +22 -0
- package/dist/collection.d.ts +12 -1
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +37 -5
- package/dist/collection.js.map +1 -1
- package/dist/decorators/index.d.ts +8 -2
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js.map +1 -1
- package/dist/embeddings/types.d.ts +4 -1
- package/dist/embeddings/types.d.ts.map +1 -1
- package/dist/generators/mcp-emit.d.ts +72 -0
- package/dist/generators/mcp-emit.d.ts.map +1 -0
- package/dist/generators/mcp-emit.js +104 -0
- package/dist/generators/mcp-emit.js.map +1 -0
- package/dist/generators/mcp-runtime-template.d.ts +5 -0
- package/dist/generators/mcp-runtime-template.d.ts.map +1 -1
- package/dist/generators/mcp-runtime-template.js +236 -3
- package/dist/generators/mcp-runtime-template.js.map +1 -1
- package/dist/generators/mcp.d.ts +63 -3
- package/dist/generators/mcp.d.ts.map +1 -1
- package/dist/generators/mcp.js +145 -21
- package/dist/generators/mcp.js.map +1 -1
- package/dist/manifest/generator.d.ts +5 -0
- package/dist/manifest/generator.d.ts.map +1 -1
- package/dist/manifest/generator.js +10 -7
- package/dist/manifest/generator.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/object.d.ts +11 -1
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +11 -1
- package/dist/object.js.map +1 -1
- package/dist/registry/types.d.ts +8 -0
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +7 -7
- package/dist/system/compatibility.d.ts.map +1 -1
- package/dist/system/compatibility.js +6 -0
- package/dist/system/compatibility.js.map +1 -1
- package/dist/system/types.d.ts +4 -1
- package/dist/system/types.d.ts.map +1 -1
- package/dist/utils/scanner-module.d.ts +7 -0
- package/dist/utils/scanner-module.d.ts.map +1 -1
- package/dist/vite-plugin/index.d.ts +7 -0
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +3 -2
- package/dist/vite-plugin/index.js.map +1 -1
- package/package.json +10 -12
package/dist/registry/types.d.ts
CHANGED
|
@@ -344,6 +344,14 @@ export interface SmartObjectConfig {
|
|
|
344
344
|
* Exclude specific tools (supports both standard CRUD actions and custom methods)
|
|
345
345
|
*/
|
|
346
346
|
exclude?: string[];
|
|
347
|
+
/**
|
|
348
|
+
* Enable the MCP Tasks extension for long-running custom actions.
|
|
349
|
+
*
|
|
350
|
+
* `false`/omitted keeps tasks fully disabled. `true` enables eligible
|
|
351
|
+
* item-scoped custom actions exposed by this MCP configuration; an
|
|
352
|
+
* array enables only the named actions.
|
|
353
|
+
*/
|
|
354
|
+
tasks?: boolean | string[];
|
|
347
355
|
};
|
|
348
356
|
/**
|
|
349
357
|
* CLI configuration
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/registry/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/registry/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,KAAK,EACV,eAAe,EACf,SAAS,EACT,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,cAAc,EACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D;;;GAGG;AAEH,MAAM,MAAM,qBAAqB,GAAG,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,UAAU,CAAC;AAEvE,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AAExE,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;IAE9B;;;OAGG;IACH,MAAM,CAAC,EAAE,aAAa,CAAC;IAEvB;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IAEH;;OAEG;IACH,IAAI,CAAC,EAAE,sBAAsB,CAAC;IAE9B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,sBAAsB,CAAC;CACnC;AAED,MAAM,WAAW,SAAS;IACxB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC;IAEvB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAChB,MAAM,EACN,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAC3E,CAAC;IAEF;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAE9C;;;OAGG;IACH,WAAW,CAAC,EAAE,oBAAoB,CAAC;IAEnC;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAE1B;;;;;;;;;;;;;OAaG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB;;;;;;;;;;OAUG;IACH,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;IAC/D,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,SAAS,GAAG,aAAa,CAAC;QACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC;QACpC,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,CAAC;CACH;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAEzB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,aAAa,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAE9B;;;;;;;;;;;;;;;;OAgBG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAE3B;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,KAAK,CAAC,EAAE,qBAAqB,GAAG,KAAK,CAAC;IAEtC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAE5B;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,CAAC;IAE9E;;;OAGG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IAEtB;;OAEG;IACH,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAE1B;;OAEG;IACH,GAAG,CAAC,EACA,OAAO,GACP;QACE;;WAEG;QACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QAEnB;;WAEG;QACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QAEnB;;;;;;WAMG;QACH,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,EAAE,CAAC;KAC5B,CAAC;IAEN;;OAEG;IACH,GAAG,CAAC,EACA,OAAO,GACP;QACE;;WAEG;QACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QAEnB;;WAEG;QACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QAEnB;;;;;WAKG;QACH,YAAY,CAAC,EAAE,OAAO,CAAC;QAEvB;;;;;;;WAOG;QACH,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,CAAC;IAEN;;OAEG;IACH,EAAE,CAAC,EAAE;QACH;;;;;WAKG;QACH,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,cAAc,GAAG,KAAK,CAAC;QAE7C;;WAEG;QACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QAEnB;;WAEG;QACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACvC,CAAC;IAEF;;OAEG;IACH,KAAK,CAAC,EAAE;QACN,UAAU,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAChE,SAAS,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/D,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAClE,WAAW,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACjE,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAClE,WAAW,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACjE,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAClE,WAAW,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;KAClE,CAAC;IAEF;;;;;;;;;;;;;;;;;;;OAmBG;IACH,UAAU,CAAC,EAAE;QACX;;;WAGG;QACH,MAAM,EAAE,MAAM,EAAE,CAAC;QAEjB;;;;;WAKG;QACH,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,MAAM,CAAC;QAEnC;;;;WAIG;QACH,YAAY,CAAC,EAAE,OAAO,CAAC;QAEvB;;;;WAIG;QACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAE7B;;;;;;;;;;;WAWG;QACH,aAAa,CAAC,EAAE;YACd,4CAA4C;YAC5C,IAAI,EAAE,MAAM,CAAC;YACb,6CAA6C;YAC7C,QAAQ,EAAE,MAAM,CAAC;SAClB,CAAC;KACH,CAAC;IAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,YAAY,CAAC,EACT,OAAO,GACP;QACE;;;;;WAKG;QACH,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;QAE/B;;;WAGG;QACH,KAAK,CAAC,EAAE,MAAM,CAAC;QAEf;;;WAGG;QACH,UAAU,CAAC,EAAE,OAAO,CAAC;QAErB;;;WAGG;QACH,YAAY,CAAC,EAAE,OAAO,CAAC;QAEvB;;;WAGG;QACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;KACjC,CAAC;IAEN;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,MAAM,CACf,MAAM,EACN;QACE;;WAEG;QACH,cAAc,EAAE,OAAO,CAAC;QAExB;;WAEG;QACH,KAAK,CAAC,EAAE,MAAM,CAAC;QAEf;;WAEG;QACH,WAAW,CAAC,EAAE,MAAM,CAAC;QAErB;;WAEG;QACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACpC,CACF,CAAC;IAEF;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,KAAK,CAAC,EAAE;QACN,iEAAiE;QACjE,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,oDAAoD;QACpD,IAAI,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;QACvC,8CAA8C;QAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IAEF;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,EAAE,CAAC,EAAE;QACH;;;;WAIG;QACH,IAAI,CAAC,EAAE,MAAM,CAAC;QACd;;;WAGG;QACH,KAAK,CAAC,EAAE,MAAM,CAAC;QACf;;;;WAIG;QACH,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IAEF;;;;OAIG;IACH,SAAS,CAAC,EAAE,mBAAmB,CAAC;CACjC;AAED;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC9B,QAAQ,EAAE,UAAU,KACjB,OAAO,CAAC,OAAO,WAAW,EAAE,eAAe,GAAG,IAAI,CAAC,CAAC;AAEzD;;GAEG;AACH,MAAM,MAAM,gBAAgB,GACxB,YAAY,GACZ,iBAAiB,GACjB,WAAW,GACX,YAAY,CAAC;AAEjB;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,wBAAwB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,2BAA2B;IAC3B,IAAI,EAAE,gBAAgB,CAAC;IACvB,kFAAkF;IAClF,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD;iFAC6E;IAC7E,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;IACjC,yEAAyE;IACzE,SAAS,CAAC,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC;IACnC,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,uEAAuE;IACvE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;;OAIG;IACH,aAAa,CAAC,EAAE,kBAAkB,CAAC;IAEnC,WAAW,EAAE,OAAO,UAAU,CAAC;IAC/B;;;;;;;;;;;OAWG;IACH,qBAAqB,CAAC,EAAE,KAEtB,OAAO,EAAE,GAAG,KAET,cAAc,CAAC,GAAG,CAAC,CAAC;IACzB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACrC,2EAA2E;IAC3E,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACvC,6DAA6D;IAC7D,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,qEAAqE;IACrE,UAAU,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACjC;;;;OAIG;IACH,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IACnC,6DAA6D;IAC7D,KAAK,CAAC,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,UAAU,CAAC;QACjB,QAAQ,EAAE;YACR,IAAI,EAAE,MAAM,CAAC;YACb,WAAW,CAAC,EAAE,MAAM,CAAC;YACrB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;SACtC,CAAC;KACH,CAAC,CAAC;IACH,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,mFAAmF;IACnF,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC/C,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACjD,2DAA2D;IAC3D,kBAAkB,CAAC,EAAE;QACnB,IAAI,EAAE,UAAU,GAAG,UAAU,CAAC;QAC9B,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,OAAO,CAAC;QACpB,YAAY,EAAE,OAAO,CAAC;QACtB,qBAAqB,EAAE,OAAO,CAAC;KAChC,CAAC;IACF;;;;;OAKG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B"}
|
package/dist/smrt-knowledge.json
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"generatedAt": "1970-01-01T00:00:00.000Z",
|
|
4
4
|
"packageName": "@happyvertical/smrt-core",
|
|
5
|
-
"packageVersion": "0.40.
|
|
5
|
+
"packageVersion": "0.40.63",
|
|
6
6
|
"sourceManifestPath": "dist/manifest.json",
|
|
7
7
|
"agentDocPath": "AGENTS.md",
|
|
8
8
|
"sourceHashes": {
|
|
9
|
-
"manifest": "
|
|
10
|
-
"packageJson": "
|
|
11
|
-
"agents": "
|
|
9
|
+
"manifest": "1c7cbc861b1554af52ed76ad421222a5a6820a23cbe89f9062911abdff2e2516",
|
|
10
|
+
"packageJson": "f3a3aab026f6f5916a30fe30d9cb54df2be1948f4c4a6fc7b508b063828bdf68",
|
|
11
|
+
"agents": "7e3062db6d928c5b58de03c9d945292d01a81b2d1bd14961ea216c6ea398dd38",
|
|
12
12
|
"moduleDoc:agents/change-feed.md": "5278797d6c049ea21071b011182a9fcd79d17f9d74e0f90a93caa4c05c25294f",
|
|
13
13
|
"moduleDoc:agents/change-signals.md": "d9cb6a5541728ffea46607a6b1d4fa61d4621849f2b4ea86a0645fbb0af892e9",
|
|
14
|
-
"moduleDoc:agents/generators.md": "
|
|
14
|
+
"moduleDoc:agents/generators.md": "2b6ccd5ff557293f2ce84d25cc254ceb518f4c274d93b7e3175985b18f36f564"
|
|
15
15
|
},
|
|
16
16
|
"exports": [
|
|
17
17
|
".",
|
|
@@ -327,7 +327,7 @@
|
|
|
327
327
|
"polymorphicAssociations": 1,
|
|
328
328
|
"uuidColumns": 3
|
|
329
329
|
},
|
|
330
|
-
"agentDoc": "# @happyvertical/smrt-core\n\nORM, code generation, AI integration, and the DispatchBus. Everything else builds on this.\n\nKey surfaces are `SmrtObject`, `SmrtCollection`, `ObjectRegistry`,\n`DispatchBus`, `GlobalInterceptors`, and `LearningMemory`; this file documents\ntheir invariants and source locations, and the module docs below cover the\nper-subsystem semantics.\n\n## Modules\n\nSubsystem semantics live in sibling module docs — read the one for the\nsubsystem you are editing. This file keeps what holds across all of them.\n\n| Module | Scope | Module doc |\n|---|---|---|\n| `src/change-feed.ts` | the adapter-agnostic change-observation spine — `_smrt_changes`, cursors, table versions, generated `_changes` routes, retention | [agents/change-feed.md](agents/change-feed.md) |\n| `src/change-signals.ts` + the generated `_events` SSE route | the push companion to the change feed — the signal bus, cross-replica fan-out, the SSE route, and its documented gaps | [agents/change-signals.md](agents/change-signals.md) |\n| `src/generators/` + `src/vite-plugin/web-collections.ts` | REST/CLI/MCP/web-collection generation, the `manifestHash` emission sites, and generated conditional-GET / ETag v2 semantics | [agents/generators.md](agents/generators.md) |\n\n## SmrtObject Lifecycle\n\n`constructor(options)` → `initialize()` → ready for `save()`/`delete()`/`loadFromId()`\n\n- `initialize()`: loads field initializers, applies option values (options override initializers), loads from DB if id/slug provided\n- `save()`: upsert with STI validation, interceptor execution, auto-embeddings. Persisted objects (`isPersisted` — set by DB hydration and successful saves) upsert on `['id']` so natural-key edits (e.g. slug renames) update in place; new objects upsert on the natural-key conflict columns for ingestion-style dedup (#1472)\n- `is(criteria)` / `do(instructions)` / `describe()`: AI operations via function calling. They inject the object's own `toPublicJSON()` (sensitive fields stripped) as a \"content body\" so the model reasons over the instance. Options: `includeData: false` skips injection (for callers that already curate the relevant fields into the instruction); `maxDataLength` overrides the truncation budget. Neither key is forwarded to `ai.message()`. (#1567)\n- `getSlug()`: auto-generates from name → title → label → id\n- `loadRelated(fieldName)`: lazy-loads relationships (cached in `_loadedRelationships` Map)\n\n## LearningMemory (#1886)\n\n`LearningMemory` provides tenant-isolated, confidence-scored recall over\n`_smrt_contexts` plus optional injected semantic search. `capture()` reinforces\nsuccesses and decays failures while updating outcome counters; `recall()`\napplies confidence, expiry, time-decay, and hierarchical-scope filters and\nrefreshes `last_used_at`. Keep semantic search behind the\n`SmrtCollection.semanticSearch`-compatible injection boundary.\n\n## SmrtCollection Query\n\n```typescript\nawait collection.list({\n where: { status: 'active', price: { op: '>', value: 10 } },\n limit: 50, offset: 0, orderBy: 'created_at DESC'\n});\n```\n\nProjection primitive (#1902): pass `select: ['id', 'title', 'tenantId']` to\n`list()` when an admin/list workflow needs compact rows. `select` uses SMRT\nfield names, maps them to DB columns internally, and returns plain objects keyed\nby the same SMRT field names without hydrating `SmrtObject` instances. It\ncomposes with `where`, `orderBy`, `limit`, and `offset`; `beforeList`\ninterceptors still run. It is for column-backed fields only and cannot combine\nwith `include`/relationship eager loading.\n\n`list()` and `query()` hydrate model instances serially in result order because\nan `initialize()` hook may query through the same transaction-bound PostgreSQL\nclient. Keep this serialization invariant; use `select` when callers need plain\nrows without model hydration.\n\n**WHERE operators**: `=`, `>`, `<`, `>=`, `<=`, `!=`, `in`, `not in`, `like`, `is null`, `is not null`. Arrays auto-detect `IN`. Dot notation for JSON paths: `metadata.userId`.\n\nSTI child collections auto-filter by `_meta_type`.\n\n## Object Memory & Semantic Search\n\nTwo persistence primitives every `SmrtObject`/`SmrtCollection` inherits — load-bearing for learning agents, usable by any object. Full guide: `docs/content/core.md` → \"Context Memory System\".\n\n- **Context memory** (`remember`/`recall`/`recallAll`/`forget`/`forgetScope`, table `_smrt_contexts`): stores any JSON value keyed by `(owner_class, owner_id, scope, key, version)` with a `confidence` score (0–1) and a stored `expiresAt` (metadata — `recall()` does **not** filter expired rows; expiry is caller-managed). `recall()` returns the highest-confidence match with an optional `minConfidence` floor and **opt-in** hierarchical scope fallback (`includeAncestors: true` → `'a/b/c' → 'a/b' → 'a' → 'global'`; default off); `recallAll()` returns a `Map`. Typical use: cache a learned strategy (e.g. a working selector per host) and reuse it across sessions. `success_count`/`failure_count` columns exist for outcome-weighting but are not auto-updated by the framework.\n- **Semantic search** (on `SmrtCollection`, table `_smrt_embeddings`): `semanticSearch(query)`, `findSimilar(object)`, `findSimilarToEmbedding(vector)` — cosine ranking over embeddings of the fields declared in `@smrt({ embeddings })`. Native pgvector/HNSW when configured, in-memory `CosineSimilarity` fallback otherwise; default local model `Xenova/bge-base-en-v1.5` (768-dim) or AI `text-embedding-3-small`. Hits hydrate via `list({ 'id in': … })`, so `@TenantScoped` isolation applies to results.\n\n## @smrt() Decorator Options\n\nKey options: `tableName`, `tableStrategy` ('cti'|'sti'), `conflictColumns`, `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\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## 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\nConfig precedence for knowledge is defaults → top-level `knowledge` in\n`smrt.config.ts` → `packages[packageName].knowledge` → plugin option →\nobject-level `@smrt({ knowledge })`.\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## DispatchBus\n\n- `emit(signalType, payload, metadata)` → creates persistent Dispatch record\n- `on(pattern, handler)` → in-memory handler (immediate)\n- `subscribe({ signalType, subscriber })` → persistent subscription (survives restarts)\n- `process(subscriberName, handler)` → process pending dispatches\n- Wildcards: `campaign.*` matches `campaign.completed` (single segment only)\n- Tables: `_smrt_dispatch`, `_smrt_dispatch_subscriptions`\n- Status: `pending → processing → completed` (or `failed`)\n\n## Single Table Inheritance (STI)\n\n- Base: `@smrt({ tableStrategy: 'sti' })` — children inherit, share one table\n- Discriminator: `_meta_type` column with qualified names (`@happyvertical/smrt-content:Article`)\n- Child fields: `@meta()` decorator → stored in `_meta_data` JSONB (not as columns)\n- Polymorphic queries: collection loads `_meta_type`, creates correct subclass dynamically\n- Validation: fail-fast on save if `_meta_type` missing or mismatched\n\n## Child Accessors (R10)\n\n`src/child-accessors.ts` installs a consistent `get<FieldName>()` instance method for every `@oneToMany` field at `@smrt()` registration time (e.g. `@oneToMany('OrderItem') items` → `order.getItems()`), delegating to `loadRelatedMany`. Two invariants:\n\n- **Additive** — never overwrites a hand-rolled method of the same name (checks the whole prototype chain). `Profile.getMetadata()` (key-value) and `ProfileRelationship.getTerms()` are preserved.\n- **Runtime-only** — attached to the prototype, invisible to the build-time manifest, so it never leaks into the REST/CLI/MCP surface.\n\nWhen the target declares multiple FKs back to the parent, annotate `@oneToMany(Target, { foreignKey: '<inverseField>' })`; `loadRelatedMany` and the eager `include:` loader both honor it (else first-match).\n\n## Vite Plugin\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\n## Gotchas\n\n- **Filesystem support is a lazy boundary (#1979)**: `SmrtClass` acquires `options.fs` adapters via `createFilesystemAdapter()` (`src/filesystem-loader.ts`), never a static `@happyvertical/files` import — the files SDK statically pulls @aws-sdk/client-s3 and reaches googleapis, and a static edge here would land it in every downstream SSR bundle. Node/tsx/vite-dev runtimes resolve it on first use; fully-bundled deployments import `@happyvertical/smrt-core/filesystem` at startup. Use `importOptionalDependency()` (`src/lazy-external.ts`) for any similar optional heavyweight dependency.\n- **Never override toJSON()** — handles STI discriminator + meta field extraction. Use `transformJSON()`\n- **Property init order**: TypeScript initializers run first, then `initialize()` applies option values (options win)\n- **No runtime schema creation**: application tables must be prepared explicitly via migrations/tooling; runtime only verifies and fails clearly\n- **Retry logic**: `db.get()` (3 retries, 250ms) and `db.upsert()` (3 retries, 500ms) have built-in retry\n- **Field caching**: `_cachedFields` populated during `Collection.create()` — eliminates async `getFields()` per query\n- **Smart cloning**: arrays/objects shallow-cloned in property init to prevent aliasing (Issue #22)\n- **Table verification cache**: `isTableVerified(dbUrl, tableName)` avoids redundant `tableExists()` calls\n- **Manifest required**: build-time AST scanning creates manifest. Without vitest plugin → \"No field metadata\"\n- **ManifestBuilder fails on scanner errors**: every production manifest path\n must abort before adapting partial scan results. A syntax error or unresolved\n `@smrt()` config spread cannot be allowed to emit a default-open manifest.\n- **Vite plugin loads scanner from `dist/` first**: `src/vite-plugin/import-build-aware.ts` prefers `dist/` when it exists on disk; it only falls back to `src/` on fresh clones. So if you edit `src/scanner/*.ts` or `src/schema/generator.ts` and want those edits reflected in consumer manifest generation, you must rebuild (`pnpm build` or have `pnpm dev` / `pnpm build:watch` running in core). This is intentional — sniffing `.ts` vs `.js` via `import.meta.url` was non-deterministic under tsx and broke 12–13 publishes (#1139).\n",
|
|
330
|
+
"agentDoc": "# @happyvertical/smrt-core\n\nORM, code generation, AI integration, and the DispatchBus. Everything else builds on this.\n\nKey surfaces are `SmrtObject`, `SmrtCollection`, `ObjectRegistry`,\n`DispatchBus`, `GlobalInterceptors`, and `LearningMemory`; this file documents\ntheir invariants and source locations, and the module docs below cover the\nper-subsystem semantics.\n\n## Modules\n\nSubsystem semantics live in sibling module docs — read the one for the\nsubsystem you are editing. This file keeps what holds across all of them.\n\n| Module | Scope | Module doc |\n|---|---|---|\n| `src/change-feed.ts` | the adapter-agnostic change-observation spine — `_smrt_changes`, cursors, table versions, generated `_changes` routes, retention | [agents/change-feed.md](agents/change-feed.md) |\n| `src/change-signals.ts` + the generated `_events` SSE route | the push companion to the change feed — the signal bus, cross-replica fan-out, the SSE route, and its documented gaps | [agents/change-signals.md](agents/change-signals.md) |\n| `src/generators/` + `src/vite-plugin/web-collections.ts` | REST/CLI/MCP/web-collection generation, the `manifestHash` emission sites, and generated conditional-GET / ETag v2 semantics | [agents/generators.md](agents/generators.md) |\n\n## SmrtObject Lifecycle\n\n`constructor(options)` → `initialize()` → ready for `save()`/`delete()`/`loadFromId()`\n\n- `initialize()`: loads field initializers, applies option values (options override initializers), loads from DB if id/slug provided\n- `save()`: upsert with STI validation, interceptor execution, auto-embeddings. Persisted objects (`isPersisted` — set by DB hydration and successful saves) upsert on `['id']` so natural-key edits (e.g. slug renames) update in place; new objects upsert on the natural-key conflict columns for ingestion-style dedup (#1472)\n- `is(criteria)` / `do(instructions)` / `describe()`: AI operations via function calling. They inject the object's own `toPublicJSON()` (sensitive fields stripped) as a \"content body\" so the model reasons over the instance. Options: `includeData: false` skips injection (for callers that already curate the relevant fields into the instruction); `maxDataLength` overrides the truncation budget. Neither key is forwarded to `ai.message()`. (#1567)\n- `getSlug()`: auto-generates from name → title → label → id\n- `loadRelated(fieldName)`: lazy-loads relationships (cached in `_loadedRelationships` Map)\n\n## LearningMemory (#1886)\n\n`LearningMemory` provides tenant-isolated, confidence-scored recall over\n`_smrt_contexts` plus optional injected semantic search. `capture()` reinforces\nsuccesses and decays failures while updating outcome counters; `recall()`\napplies confidence, expiry, time-decay, and hierarchical-scope filters and\nrefreshes `last_used_at`. Keep semantic search behind the\n`SmrtCollection.semanticSearch`-compatible injection boundary.\n\n## SmrtCollection Query\n\n```typescript\nawait collection.list({\n where: { status: 'active', 'price >': 10 },\n limit: 50, offset: 0, orderBy: 'created_at DESC'\n});\n```\n\nProjection primitive (#1902): pass `select: ['id', 'title', 'tenantId']` to\n`list()` when an admin/list workflow needs compact rows. `select` uses SMRT\nfield names, maps them to DB columns internally, and returns plain objects keyed\nby the same SMRT field names without hydrating `SmrtObject` instances. It\ncomposes with `where`, `orderBy`, `limit`, and `offset`; `beforeList`\ninterceptors still run. It is for column-backed fields only and cannot combine\nwith `include`/relationship eager loading.\n\n`list()` and `query()` hydrate model instances serially in result order because\nan `initialize()` hook may query through the same transaction-bound PostgreSQL\nclient. Keep this serialization invariant; use `select` when callers need plain\nrows without model hydration.\n\n**WHERE operators**: `=`, `>`, `<`, `>=`, `<=`, `!=`, `in`, `not in`, `like`.\nArrays auto-detect `IN`. NULL is a value, not an operator: `{ deletedAt: null }`\nrenders `IS NULL` and `{ 'deletedAt !=': null }` renders `IS NOT NULL`.\n\nThis list is the set `@happyvertical/sql`'s `buildWhere` can execute, and\n`convertWhereKeys` accepts nothing outside it — an operator accepted here but\nunknown there fails inside the query builder, after the API said the query was\nvalid (#2276). Two entries were removed for that reason and now reject at the\nAPI boundary: `contains` (never existed in the SQL layer; use `like` with\nexplicit wildcards) and dot-notation JSON paths such as `metadata.userId` (never\nrewritten into an extraction expression, so they reached SQL as qualified column\nreferences). Re-adding either requires the query builder to support it first;\n`src/__tests__/issue-2276-where-contract.test.ts` executes every accepted\noperator against a database to keep the two in step.\n\nSTI child collections auto-filter by `_meta_type`.\n\n## Object Memory & Semantic Search\n\nTwo persistence primitives every `SmrtObject`/`SmrtCollection` inherits — load-bearing for learning agents, usable by any object. Full guide: `docs/content/core.md` → \"Context Memory System\".\n\n- **Context memory** (`remember`/`recall`/`recallAll`/`forget`/`forgetScope`, table `_smrt_contexts`): stores any JSON value keyed by `(owner_class, owner_id, scope, key, version)` with a `confidence` score (0–1) and a stored `expiresAt` (metadata — `recall()` does **not** filter expired rows; expiry is caller-managed). `recall()` returns the highest-confidence match with an optional `minConfidence` floor and **opt-in** hierarchical scope fallback (`includeAncestors: true` → `'a/b/c' → 'a/b' → 'a' → 'global'`; default off); `recallAll()` returns a `Map`. Typical use: cache a learned strategy (e.g. a working selector per host) and reuse it across sessions. `success_count`/`failure_count` columns exist for outcome-weighting: `SmrtObject.remember()` leaves them untouched, `SmrtCollection.remember()` resets them to zero, and neither recall path updates them. `LearningMemory` is the layer that maintains them (and that does filter expired rows).\n- **Semantic search** (on `SmrtCollection`, table `_smrt_embeddings`): `semanticSearch(query)`, `findSimilar(object)`, `findSimilarToEmbedding(vector)` — cosine ranking over embeddings of the fields declared in `@smrt({ embeddings })`. Native pgvector/HNSW when configured, in-memory `CosineSimilarity` fallback otherwise; default local model `Xenova/bge-base-en-v1.5` (768-dim) or AI `text-embedding-3-small`. Hits hydrate via `list({ 'id in': … })`, so `@TenantScoped` isolation applies to results.\n\n## @smrt() Decorator Options\n\nKey options: `tableName`, `tableStrategy` ('cti'|'sti'), `conflictColumns`, `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\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## 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\nConfig precedence for knowledge is defaults → top-level `knowledge` in\n`smrt.config.ts` → `packages[packageName].knowledge` → plugin option →\nobject-level `@smrt({ knowledge })`.\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## DispatchBus\n\n- `emit(signalType, payload, metadata)` → creates persistent Dispatch record\n- `on(pattern, handler)` → in-memory handler (immediate)\n- `subscribe({ signalType, subscriber })` → persistent subscription (survives restarts)\n- `process(subscriberName, handler)` → process pending dispatches\n- Wildcards: `campaign.*` matches `campaign.completed` (single segment only)\n- Tables: `_smrt_dispatch`, `_smrt_dispatch_subscriptions`\n- Status: `pending → processing → completed` (or `failed`)\n\n## Single Table Inheritance (STI)\n\n- Base: `@smrt({ tableStrategy: 'sti' })` — children inherit, share one table\n- Discriminator: `_meta_type` column with qualified names (`@happyvertical/smrt-content:Article`)\n- Child fields: `@meta()` decorator → stored in `_meta_data` JSONB (not as columns)\n- Polymorphic queries: collection loads `_meta_type`, creates correct subclass dynamically\n- Validation: fail-fast on save if `_meta_type` missing or mismatched\n\n## Child Accessors (R10)\n\n`src/child-accessors.ts` installs a consistent `get<FieldName>()` instance method for every `@oneToMany` field at `@smrt()` registration time (e.g. `@oneToMany('OrderItem') items` → `order.getItems()`), delegating to `loadRelatedMany`. Two invariants:\n\n- **Additive** — never overwrites a hand-rolled method of the same name (checks the whole prototype chain). `Profile.getMetadata()` (key-value) and `ProfileRelationship.getTerms()` are preserved.\n- **Runtime-only** — attached to the prototype, invisible to the build-time manifest, so it never leaks into the REST/CLI/MCP surface.\n\nWhen the target declares multiple FKs back to the parent, annotate `@oneToMany(Target, { foreignKey: '<inverseField>' })`; `loadRelatedMany` and the eager `include:` loader both honor it (else first-match).\n\n## Vite Plugin\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\n## Gotchas\n\n- **Filesystem support is a lazy boundary (#1979)**: `SmrtClass` acquires `options.fs` adapters via `createFilesystemAdapter()` (`src/filesystem-loader.ts`), never a static `@happyvertical/files` import — the files SDK statically pulls @aws-sdk/client-s3 and reaches googleapis, and a static edge here would land it in every downstream SSR bundle. Node/tsx/vite-dev runtimes resolve it on first use; fully-bundled deployments import `@happyvertical/smrt-core/filesystem` at startup. Use `importOptionalDependency()` (`src/lazy-external.ts`) for any similar optional heavyweight dependency.\n- **Never override toJSON()** — handles STI discriminator + meta field extraction. Use `transformJSON()`\n- **Property init order**: TypeScript initializers run first, then `initialize()` applies option values (options win)\n- **No runtime schema creation**: application tables must be prepared explicitly via migrations/tooling; runtime only verifies and fails clearly\n- **Retry logic**: `db.get()` (3 retries, 250ms) and `db.upsert()` (3 retries, 500ms) have built-in retry\n- **Field caching**: `_cachedFields` populated during `Collection.create()` — eliminates async `getFields()` per query\n- **Smart cloning**: arrays/objects shallow-cloned in property init to prevent aliasing (Issue #22)\n- **Table verification cache**: `isTableVerified(dbUrl, tableName)` avoids redundant `tableExists()` calls\n- **Manifest required**: build-time AST scanning creates manifest. Without vitest plugin → \"No field metadata\"\n- **ManifestBuilder fails on scanner errors**: every production manifest path\n must abort before adapting partial scan results. A syntax error or unresolved\n `@smrt()` config spread cannot be allowed to emit a default-open manifest.\n- **Vite plugin loads scanner from `dist/` first**: `src/vite-plugin/import-build-aware.ts` prefers `dist/` when it exists on disk; it only falls back to `src/` on fresh clones. So if you edit `src/scanner/*.ts` or `src/schema/generator.ts` and want those edits reflected in consumer manifest generation, you must rebuild (`pnpm build` or have `pnpm dev` / `pnpm build:watch` running in core). This is intentional — sniffing `.ts` vs `.js` via `import.meta.url` was non-deterministic under tsx and broke 12–13 publishes (#1139).\n",
|
|
331
331
|
"moduleDocs": [
|
|
332
332
|
{
|
|
333
333
|
"path": "agents/change-feed.md",
|
|
@@ -342,7 +342,7 @@
|
|
|
342
342
|
{
|
|
343
343
|
"path": "agents/generators.md",
|
|
344
344
|
"module": "generators",
|
|
345
|
-
"content": "# smrt-core/code generators\n\nModule semantics for `src/generators/` + `src/vite-plugin/`. Package orientation, the cross-module\ninvariants, and the traps that apply before editing anything live in\n[../AGENTS.md](../AGENTS.md) — read that first.\n\n## Code Generators\n\n| Generator | Location | Output |\n|-----------|----------|--------|\n| REST API | `src/generators/rest.ts` | OpenAPI-compliant CRUD endpoints |\n| CLI | `src/generators/cli.ts` | `objectname:action` admin commands — writable allowlist, exhaustive-include, `--from-file`, fail-closed tenant context |\n| MCP Server | `src/generators/mcp.ts` | Model Context Protocol tools |\n| 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` |\n\nGenerated API clients share `selectApiClientEntries()` across the runtime Vite\nmodule, its ambient declaration, and physical prebuild declarations. When a\ncollection class and its populated model share an endpoint, the model owns the\ncanonical collection key and row payload schema; the collection class remains\navailable under a deterministic class-derived secondary key. Selection and\ncollision suffixes must not depend on manifest insertion order (#2027).\nFor aggregated manifests, inheritance and item-type references resolve exact\nqualified names first, then package-local simple names, then a stable identity\nfallback so duplicate class names across packages cannot reintroduce ordering.\n\nThe web module also emits a build-time **`manifestHash`** constant (#1764): `computeWebManifestHash(manifest)` is a deterministic, replica-stable digest of the emitted web-collection SHAPE (name/className/endpoint/idField/actions/fields/relationships), canonicalized (recursive key sort) before `sha256 → base64url`, truncated to 16 chars — so the same schema always hashes the same, and a field add/remove/type-change/edge-change changes it. A change means old persisted client rows may mis-hydrate, so smrt-web keys its durable persistence namespace on it and its `updateAvailable` contract signal compares against it. Four co-managed emission sites must not drift: the runtime value (`generateWebModule`), the `@happyvertical/smrt-virt-web` ambient d.ts (`vite-plugin/index.ts`), the physical `@smrt/web` d.ts (`prebuild/index.ts`), and the hand-written type mirror in `@happyvertical/smrt-web` (`packages/smrt-web/src/index.ts` — dependency-free, so textual sync only).\n\nPer-field web emission (#2046): `buildWebFieldDefinitions` carries `description` (from `@field({ description })`) and sanitized `ui` hints (from `@field({ ui: { basic, group, order, locked } })`, read off the manifest `_meta.ui` bag through per-key type guards) into each emitted field definition, and `buildWebToolDescriptors` threads the same `description` into browser MCP tool schemas. `sensitive`/`transient` fields are excluded from emission entirely, so their descriptions never ship. Both keys are conditional, so hint-less schemas emit byte-identical definitions (and hashes) as before; adding a description/ui hint changes the manifest hash — deliberate over-invalidation, harmless per the #1764 contract.\n\n## Custom-action contract\n\n`resolveCustomActionMetadata()` is the common discovery and invocation contract\nfor generated REST routes and API clients, MCP, CLI, WebMCP, and simple\nREST-resource discovery. Receiver scope comes from the executable method, never a\nconfiguration-only `api.routes[name].scope` override: instance model methods\nare item-scoped and require `id`; static model methods and recognized\n`SmrtCollection` methods are collection-scoped and do not accept `id`. Route\nconfiguration may still choose its path and HTTP verb, but it cannot turn an\ninstance call into `ClassRef.action` or vice versa.\n\nWhen scanner method metadata exists, discovery projects each named parameter\nand its JSON-schema type, and invokers pass the values positionally in declared\norder. The legacy single `options` bag remains compatible when metadata is\nabsent (or the declared method takes `options`). Do not infer this from runtime\nfunction arity. An omitted typed `options` parameter remains `undefined`, so a\nmethod's JavaScript default initializer continues to apply; an explicit `null`\nremains `null`. Flat tool and CLI inputs reserve `id` for receiver parsing. If\nan action declares an `id` parameter, its flat MCP/WebMCP field is `actionId`\n(and CLI uses `--action-id`); REST keeps its independent path/body\nnamespaces. Typed CLI actions may use standard flag names such as `limit`,\n`offset`, `where`, and `format` without those values being stripped as CRUD\nflags.\n\nCustom actions may return an explicit, domain-neutral failure object with\n`ok: false`, `code`, and `message` plus optional `status`, `details`,\n`retryable`, and `correlationId`. `normalizeCustomActionFailure()` redacts it;\ngenerated REST returns `{ error: failure }` with the non-2xx status, while MCP\nreturns `isError: true` and `_meta['io.happyvertical/smrt']`. Opaque successful\nobjects (including `{ code, message }`) remain untouched; thrown exceptions are\nnot reclassified as domain failures.\n\nGenerated reads (`list`/`get`) on the REST and SvelteKit generators support conditional GET (helpers in `src/generators/conditional-get.ts`). ETag v2 (#1765): the validator is the table's change-feed version (`getTableVersion`) keyed by the request representation, so a **concrete** `If-None-Match` short-circuits into a 304 with an empty body **before** the collection query runs — an unchanged table revalidates with zero table scan. A wildcard `If-None-Match: *` is deferred until the payload builds (existence confirmed), so a missing item still returns 404, not a false 304. Tenant-scoped reads fold the active tenant into the representation (`resolveTenantEtagDiscriminator`) so one tenant's cached validator never satisfies another's read of the same URL. Routes whose GET renders via a **custom serializer** (which can load related tables the base-table version can't observe) keep the v1 body-hash ETag (`#1757`, query-first but correct); the default `toPublicJSON` path — all REST reads and non-serializer SvelteKit reads — uses v2. v2 is weakly consistent by design (the cost of not reading the data): a revalidation in the sub-statement window between a committed write and its feed append can return a stale 304 that self-heals on the next revalidation. The other v2 window — a deploy that changes the response shape WITHOUT a table write — is closed by the **#1764 ETag salt**: `computeTableVersionEtag(version, representation, manifestHash?)` folds the build's web-collection shape digest into the digest, so a shape-only redeploy busts every read validator (`undefined` reproduces the pre-#1764 unsalted value byte-for-byte for direct helper callers). The generated SvelteKit route bakes the digest in as a `MANIFEST_HASH` constant (via `generateConditionalGetRouteHelper`'s `manifestHash` option, sourced from `computeWebManifestHash(manifest)`) — automatic for the SvelteKit transport. The runtime `APIGenerator` auto-populates the same salt from the runtime registry with `computeRuntimeWebManifestHash()` when `APIConfig.manifestHash` is omitted; explicit `APIConfig.manifestHash` still wins for custom setups. The digest scope is get-OR-list (`selectWebEtagSaltEntries`), so **get-only** routes are salted too. Strong consistency still requires the v1 body-hash path. Cache-Control policy (unchanged from #1757): `private, no-cache` by default; public models may opt into shared caching via `@smrt({ api: { public: true | 'read', cache: { sMaxage } } })` → `public, max-age=0, s-maxage=<n>`; non-public models never emit shared-cache headers. Tenant-scoped models (any mode) never emit them either — bodies vary with session-cookie tenant context that URL-keyed shared caches cannot see; `sMaxage` is neutralized to `private, no-cache` with a one-time warning.\n"
|
|
345
|
+
"content": "# smrt-core/code generators\n\nModule semantics for `src/generators/` + `src/vite-plugin/`. Package orientation, the cross-module\ninvariants, and the traps that apply before editing anything live in\n[../AGENTS.md](../AGENTS.md) — read that first.\n\n## Code Generators\n\n| Generator | Location | Output |\n|-----------|----------|--------|\n| REST API | `src/generators/rest.ts` | OpenAPI-compliant CRUD endpoints |\n| CLI | `src/generators/cli.ts` | `objectname:action` admin commands — writable allowlist, exhaustive-include, `--from-file`, fail-closed tenant context |\n| MCP Server | `src/generators/mcp.ts` | Model Context Protocol tools |\n| 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` |\n\nGenerated API clients share `selectApiClientEntries()` across the runtime Vite\nmodule, its ambient declaration, and physical prebuild declarations. When a\ncollection class and its populated model share an endpoint, the model owns the\ncanonical collection key and row payload schema; the collection class remains\navailable under a deterministic class-derived secondary key. Selection and\ncollision suffixes must not depend on manifest insertion order (#2027).\nFor aggregated manifests, inheritance and item-type references resolve exact\nqualified names first, then package-local simple names, then a stable identity\nfallback so duplicate class names across packages cannot reintroduce ordering.\n\nThe web module also emits a build-time **`manifestHash`** constant (#1764): `computeWebManifestHash(manifest)` is a deterministic, replica-stable digest of the emitted web-collection SHAPE (name/className/endpoint/idField/actions/fields/relationships), canonicalized (recursive key sort) before `sha256 → base64url`, truncated to 16 chars — so the same schema always hashes the same, and a field add/remove/type-change/edge-change changes it. A change means old persisted client rows may mis-hydrate, so smrt-web keys its durable persistence namespace on it and its `updateAvailable` contract signal compares against it. Four co-managed emission sites must not drift: the runtime value (`generateWebModule`), the `@happyvertical/smrt-virt-web` ambient d.ts (`vite-plugin/index.ts`), the physical `@smrt/web` d.ts (`prebuild/index.ts`), and the hand-written type mirror in `@happyvertical/smrt-web` (`packages/smrt-web/src/index.ts` — dependency-free, so textual sync only).\n\nPer-field web emission (#2046): `buildWebFieldDefinitions` carries `description` (from `@field({ description })`) and sanitized `ui` hints (from `@field({ ui: { basic, group, order, locked } })`, read off the manifest `_meta.ui` bag through per-key type guards) into each emitted field definition, and `buildWebToolDescriptors` threads the same `description` into browser MCP tool schemas. `sensitive`/`transient` fields are excluded from emission entirely, so their descriptions never ship. Both keys are conditional, so hint-less schemas emit byte-identical definitions (and hashes) as before; adding a description/ui hint changes the manifest hash — deliberate over-invalidation, harmless per the #1764 contract.\n\n## Generated MCP server output language\n\n`MCPGenerator` builds every file as TypeScript, so the requested `outputPath`\nextension decides what is written (#2279). `.ts`/`.mts` targets keep the source\nverbatim for `tsx` or Node type stripping — which is why the generated source\nmust stay erasable-syntax-only (no parameter properties, enums, or namespaces).\nEvery other target (`.smrt/mcp-server/index.js` by default) is transpiled to\nJavaScript with the `typescript` dependency before writing, because the printed\nrun script and the generated `claude-config.example.json` both invoke it with\nplain `node`. A `.cjs`/`.cts` target is rejected outright: generated servers are\nES modules. `src/generators/mcp-emit.ts` owns those decisions — do not\nreintroduce a bare `writeFile` of generated source.\n\nModular output writes `config`, `tools/index`, and `handlers/index` with the\nentry point's own extension, and emits the entry's relative import specifiers\nwith that same extension, so the files it imports both exist and load with the\nsame module semantics — an `.mjs` entry gets `.mjs` siblings, not `.js` ones a\nCommonJS package would then parse as CommonJS. The entry is written at the\nrequested path rather than a hardcoded `index.js`.\nGenerated code also has to be valid in an ES module: `arguments` is not a legal\nbinding name there, however convenient it reads.\n\n## Custom-action contract\n\n`resolveCustomActionMetadata()` is the common discovery and invocation contract\nfor generated REST routes and API clients, MCP, CLI, WebMCP, and simple\nREST-resource discovery. Receiver scope comes from the executable method, never a\nconfiguration-only `api.routes[name].scope` override: instance model methods\nare item-scoped and require `id`; static model methods and recognized\n`SmrtCollection` methods are collection-scoped and do not accept `id`. Route\nconfiguration may still choose its path and HTTP verb, but it cannot turn an\ninstance call into `ClassRef.action` or vice versa.\n\nWhen scanner method metadata exists, discovery projects each named parameter\nand its JSON-schema type, and invokers pass the values positionally in declared\norder. The legacy single `options` bag remains compatible when metadata is\nabsent (or the declared method takes `options`). Do not infer this from runtime\nfunction arity. An omitted typed `options` parameter remains `undefined`, so a\nmethod's JavaScript default initializer continues to apply; an explicit `null`\nremains `null`. Flat tool and CLI inputs reserve `id` for receiver parsing. If\nan action declares an `id` parameter, its flat MCP/WebMCP field is `actionId`\n(and CLI uses `--action-id`); REST keeps its independent path/body\nnamespaces. Typed CLI actions may use standard flag names such as `limit`,\n`offset`, `where`, and `format` without those values being stripped as CRUD\nflags.\n\nCustom actions may return an explicit, domain-neutral failure object with\n`ok: false`, `code`, and `message` plus optional `status`, `details`,\n`retryable`, and `correlationId`. `normalizeCustomActionFailure()` redacts it;\ngenerated REST returns `{ error: failure }` with the non-2xx status, while MCP\nreturns `isError: true` and `_meta['io.happyvertical/smrt']`. Opaque successful\nobjects (including `{ code, message }`) remain untouched; thrown exceptions are\nnot reclassified as domain failures.\n\nGenerated reads (`list`/`get`) on the REST and SvelteKit generators support conditional GET (helpers in `src/generators/conditional-get.ts`). ETag v2 (#1765): the validator is the table's change-feed version (`getTableVersion`) keyed by the request representation, so a **concrete** `If-None-Match` short-circuits into a 304 with an empty body **before** the collection query runs — an unchanged table revalidates with zero table scan. A wildcard `If-None-Match: *` is deferred until the payload builds (existence confirmed), so a missing item still returns 404, not a false 304. Tenant-scoped reads fold the active tenant into the representation (`resolveTenantEtagDiscriminator`) so one tenant's cached validator never satisfies another's read of the same URL. Routes whose GET renders via a **custom serializer** (which can load related tables the base-table version can't observe) keep the v1 body-hash ETag (`#1757`, query-first but correct); the default `toPublicJSON` path — all REST reads and non-serializer SvelteKit reads — uses v2. v2 is weakly consistent by design (the cost of not reading the data): a revalidation in the sub-statement window between a committed write and its feed append can return a stale 304 that self-heals on the next revalidation. The other v2 window — a deploy that changes the response shape WITHOUT a table write — is closed by the **#1764 ETag salt**: `computeTableVersionEtag(version, representation, manifestHash?)` folds the build's web-collection shape digest into the digest, so a shape-only redeploy busts every read validator (`undefined` reproduces the pre-#1764 unsalted value byte-for-byte for direct helper callers). The generated SvelteKit route bakes the digest in as a `MANIFEST_HASH` constant (via `generateConditionalGetRouteHelper`'s `manifestHash` option, sourced from `computeWebManifestHash(manifest)`) — automatic for the SvelteKit transport. The runtime `APIGenerator` auto-populates the same salt from the runtime registry with `computeRuntimeWebManifestHash()` when `APIConfig.manifestHash` is omitted; explicit `APIConfig.manifestHash` still wins for custom setups. The digest scope is get-OR-list (`selectWebEtagSaltEntries`), so **get-only** routes are salted too. Strong consistency still requires the v1 body-hash path. Cache-Control policy (unchanged from #1757): `private, no-cache` by default; public models may opt into shared caching via `@smrt({ api: { public: true | 'read', cache: { sMaxage } } })` → `public, max-age=0, s-maxage=<n>`; non-public models never emit shared-cache headers. Tenant-scoped models (any mode) never emit them either — bodies vary with session-cookie tenant context that URL-keyed shared caches cannot see; `sMaxage` is neutralized to `private, no-cache` with a one-time warning.\n"
|
|
346
346
|
}
|
|
347
347
|
]
|
|
348
348
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compatibility.d.ts","sourceRoot":"","sources":["../../src/system/compatibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AA6BtD,wBAAsB,WAAW,CAC/B,EAAE,EAAE,iBAAiB,EACrB,SAAS,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CAgBlB;AA+BD,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,iBAAiB,EACrB,QAAQ,CAAC,EAAE,MAAM,GAChB,UAAU,CAAC,OAAO,YAAY,CAAC,CAMjC;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,4CAA4C;IAC3D;;;OAGG;IACH,cAAc,EAAE,KAAK,CAAC;CACvB;AAED,MAAM,WAAW,oCAAoC;IACnD,IAAI,EAAE,QAAQ,GAAG,sBAAsB,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;CACb;AAMD;;;;GAIG;AACH,wBAAsB,qCAAqC,CACzD,EAAE,EAAE,iBAAiB,EACrB,YAAY,EAAE,4CAA4C,EAC1D,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,oCAAoC,EAAE,CAAC,CA8DjD;AAED;;;;GAIG;AACH,wBAAsB,qCAAqC,CACzD,EAAE,EAAE,iBAAiB,EACrB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED,wBAAsB,+BAA+B,CACnD,EAAE,EAAE,iBAAiB,EACrB,YAAY,EAAE,4CAA4C,EAC1D,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CA+Cf;AAyUD,wBAAsB,sCAAsC,CAC1D,EAAE,EAAE,iBAAiB,EACrB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CA4Cf;AAED,wBAAsB,mDAAmD,CACvE,EAAE,EAAE,iBAAiB,EACrB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CA+Bf;AAED,wBAAsB,kCAAkC,CACtD,EAAE,EAAE,iBAAiB,EACrB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"compatibility.d.ts","sourceRoot":"","sources":["../../src/system/compatibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AA6BtD,wBAAsB,WAAW,CAC/B,EAAE,EAAE,iBAAiB,EACrB,SAAS,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CAgBlB;AA+BD,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,iBAAiB,EACrB,QAAQ,CAAC,EAAE,MAAM,GAChB,UAAU,CAAC,OAAO,YAAY,CAAC,CAMjC;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,4CAA4C;IAC3D;;;OAGG;IACH,cAAc,EAAE,KAAK,CAAC;CACvB;AAED,MAAM,WAAW,oCAAoC;IACnD,IAAI,EAAE,QAAQ,GAAG,sBAAsB,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;CACb;AAMD;;;;GAIG;AACH,wBAAsB,qCAAqC,CACzD,EAAE,EAAE,iBAAiB,EACrB,YAAY,EAAE,4CAA4C,EAC1D,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,oCAAoC,EAAE,CAAC,CA8DjD;AAED;;;;GAIG;AACH,wBAAsB,qCAAqC,CACzD,EAAE,EAAE,iBAAiB,EACrB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED,wBAAsB,+BAA+B,CACnD,EAAE,EAAE,iBAAiB,EACrB,YAAY,EAAE,4CAA4C,EAC1D,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CA+Cf;AAyUD,wBAAsB,sCAAsC,CAC1D,EAAE,EAAE,iBAAiB,EACrB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CA4Cf;AAED,wBAAsB,mDAAmD,CACvE,EAAE,EAAE,iBAAiB,EACrB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CA+Bf;AAED,wBAAsB,kCAAkC,CACtD,EAAE,EAAE,iBAAiB,EACrB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAyCf;AAED,wBAAsB,uCAAuC,CAC3D,EAAE,EAAE,iBAAiB,EACrB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAwCf;AAED,wBAAsB,oCAAoC,CACxD,EAAE,EAAE,iBAAiB,EACrB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAKf"}
|
|
@@ -324,6 +324,12 @@ async function ensureJobsSystemTableCompatibility(db, typeHint) {
|
|
|
324
324
|
if (!await tableExists(db, "_smrt_jobs", typeHint)) return;
|
|
325
325
|
await addColumnIfMissing(db, "_smrt_jobs", "tenant_id", "TEXT", typeHint);
|
|
326
326
|
await addIndexIfMissing(db, "idx_smrt_jobs_tenant_id", "_smrt_jobs", "tenant_id", typeHint);
|
|
327
|
+
await addColumnIfMissing(db, "_smrt_jobs", "task_id", "TEXT", typeHint);
|
|
328
|
+
await addColumnIfMissing(db, "_smrt_jobs", "task_owner_id", "TEXT", typeHint);
|
|
329
|
+
await addColumnIfMissing(db, "_smrt_jobs", "task_result", "TEXT", typeHint);
|
|
330
|
+
await addColumnIfMissing(db, "_smrt_jobs", "task_input_requests", "TEXT", typeHint);
|
|
331
|
+
await addColumnIfMissing(db, "_smrt_jobs", "task_input_responses", "TEXT", typeHint);
|
|
332
|
+
await addUniqueIndexIfMissing(db, "idx_smrt_jobs_task_id", "_smrt_jobs", "task_id", typeHint);
|
|
327
333
|
}
|
|
328
334
|
async function ensureJobEventsSystemTableCompatibility(db, typeHint) {
|
|
329
335
|
if (!await tableExists(db, "_smrt_job_events", typeHint)) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compatibility.js","names":[],"sources":["../../src/system/compatibility.ts"],"sourcesContent":["import type { DatabaseInterface } from '@happyvertical/sql';\nimport { detectEngine } from '../schema/ddl/index.js';\nimport {\n CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION,\n LEGACY_POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY,\n} from './schema.js';\n\ntype DatabaseWithConfig = DatabaseInterface & {\n config?: {\n type?: string;\n url?: string;\n };\n type?: string;\n};\n\nfunction getQueryRows(result: unknown): Record<string, unknown>[] {\n if (Array.isArray(result)) {\n return result as Record<string, unknown>[];\n }\n\n if (result && typeof result === 'object' && 'rows' in result) {\n const rows = (result as { rows?: unknown }).rows;\n if (Array.isArray(rows)) {\n return rows as Record<string, unknown>[];\n }\n }\n\n return [];\n}\n\nexport async function tableExists(\n db: DatabaseInterface,\n tableName: string,\n typeHint?: string,\n): Promise<boolean> {\n try {\n const engine = getDatabaseEngine(db, typeHint);\n if (engine === 'postgres') {\n const result = await db.query(\n 'SELECT 1 FROM information_schema.tables WHERE table_schema = current_schema() AND table_name = $1 LIMIT 1',\n tableName,\n );\n return getQueryRows(result).length > 0;\n }\n\n await db.query(`SELECT 1 FROM ${tableName} LIMIT 1`);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function columnExists(\n db: DatabaseInterface,\n tableName: string,\n columnName: string,\n typeHint?: string,\n): Promise<boolean> {\n try {\n const engine = getDatabaseEngine(db, typeHint);\n if (engine === 'postgres') {\n const result = await db.query(\n 'SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = $1 AND column_name = $2 LIMIT 1',\n tableName,\n columnName,\n );\n return getQueryRows(result).length > 0;\n }\n\n const result = await db.query(`PRAGMA table_info(${tableName})`);\n return getQueryRows(result).some((row) => row.name === columnName);\n } catch {\n return false;\n }\n}\n\nfunction getDatabaseUrl(db: DatabaseInterface): string {\n const dbWithConfig = db as DatabaseWithConfig;\n return db.url || dbWithConfig.config?.url || '';\n}\n\nexport function getDatabaseEngine(\n db: DatabaseInterface,\n typeHint?: string,\n): ReturnType<typeof detectEngine> {\n const dbWithConfig = db as DatabaseWithConfig;\n return detectEngine(\n getDatabaseUrl(db),\n typeHint || dbWithConfig.type || dbWithConfig.config?.type,\n );\n}\n\n/**\n * Upgrade framework-owned PostgreSQL timestamp columns to instant-safe storage.\n *\n * Legacy SMRT and system writers serialized instants as UTC wall times. The\n * conversion is intentionally fail-closed unless the migration session is UTC:\n * operators must first confirm that historical database-default/raw writers\n * also used UTC. A deployment with non-UTC legacy writers needs an explicit,\n * provenance-aware data migration rather than a guessed offset.\n */\nexport interface PostgresSystemTimestampMigrationConfirmation {\n /**\n * Explicitly confirmed timezone used by every historical writer of legacy\n * timezone-naive system timestamps. Only UTC is supported by this helper.\n */\n legacyTimezone: 'UTC';\n}\n\nexport interface PostgresSystemTimestampMigrationPlan {\n kind: 'column' | 'change-feed-function';\n tableName: string;\n columnName?: string;\n sql: string;\n}\n\nfunction quotePostgresIdentifier(identifier: string): string {\n return `\"${identifier.replaceAll('\"', '\"\"')}\"`;\n}\n\n/**\n * Read-only plan for the framework-owned timestamp conversion. This is kept\n * separate from the atomic mutator so CLI dry-runs can report every ALTER\n * without bootstrapping or changing framework tables.\n */\nexport async function planPostgresSystemTimestampMigrations(\n db: DatabaseInterface,\n confirmation: PostgresSystemTimestampMigrationConfirmation,\n typeHint?: string,\n): Promise<PostgresSystemTimestampMigrationPlan[]> {\n if (getDatabaseEngine(db, typeHint) !== 'postgres') return [];\n if (confirmation.legacyTimezone !== 'UTC') {\n throw new Error(\n 'Legacy SMRT system timestamp migration requires an explicit UTC provenance confirmation',\n );\n }\n\n const sessionRows = getQueryRows(\n await db.query(`\n SELECT\n current_setting('TimeZone') AS timezone,\n to_regprocedure('${LEGACY_POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY}')\n IS NOT NULL AS legacy_change_feed_function_exists\n `),\n );\n const session = sessionRows[0] ?? {};\n const timezone = String(session.timezone ?? '').toUpperCase();\n if (!['UTC', 'ETC/UTC', 'GMT'].includes(timezone)) {\n throw new Error(\n `Refusing to preview legacy SMRT timestamp migration outside a UTC PostgreSQL session (current TimeZone: ${String(session.timezone ?? 'unknown')})`,\n );\n }\n\n const rows = getQueryRows(\n await db.query(`\n SELECT table_name, column_name\n FROM information_schema.columns\n WHERE table_schema = current_schema()\n AND table_name LIKE '\\\\_smrt\\\\_%' ESCAPE '\\\\'\n AND data_type = 'timestamp without time zone'\n ORDER BY table_name, ordinal_position\n `),\n );\n\n const plans: PostgresSystemTimestampMigrationPlan[] = rows.map((row) => {\n const tableName = String(row.table_name);\n const columnName = String(row.column_name);\n const table = quotePostgresIdentifier(tableName);\n const column = quotePostgresIdentifier(columnName);\n return {\n kind: 'column',\n tableName,\n columnName,\n sql: `ALTER TABLE ${table} ALTER COLUMN ${column} TYPE TIMESTAMPTZ USING ${column} AT TIME ZONE 'UTC'`,\n };\n });\n\n if (\n session.legacy_change_feed_function_exists === true ||\n session.legacy_change_feed_function_exists === 't' ||\n session.legacy_change_feed_function_exists === 'true' ||\n session.legacy_change_feed_function_exists === 1\n ) {\n plans.push({\n kind: 'change-feed-function',\n tableName: '_smrt_changes',\n sql: `DROP FUNCTION IF EXISTS ${LEGACY_POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY};\\n${CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION.trim()}`,\n });\n }\n\n return plans;\n}\n\n/**\n * Fail closed when framework-owned PostgreSQL tables still contain legacy\n * timezone-naive timestamps. Call this before recording the current system\n * schema version so a partial installation cannot be stamped as upgraded.\n */\nexport async function assertPostgresSystemTimestampsCurrent(\n db: DatabaseInterface,\n typeHint?: string,\n): Promise<void> {\n if (getDatabaseEngine(db, typeHint) !== 'postgres') return;\n\n const rows = getQueryRows(\n await db.query(`\n SELECT table_name, column_name\n FROM information_schema.columns\n WHERE table_schema = current_schema()\n AND table_name LIKE '\\\\_smrt\\\\_%' ESCAPE '\\\\'\n AND data_type = 'timestamp without time zone'\n ORDER BY table_name, ordinal_position\n `),\n );\n if (rows.length === 0) return;\n\n const columns = rows\n .map((row) => `${String(row.table_name)}.${String(row.column_name)}`)\n .join(', ');\n throw new Error(\n `Legacy SMRT system timestamps remain (${columns}); run the explicit audited migratePostgresSystemTimestamps() migration before bootstrap`,\n );\n}\n\nexport async function migratePostgresSystemTimestamps(\n db: DatabaseInterface,\n confirmation: PostgresSystemTimestampMigrationConfirmation,\n typeHint?: string,\n): Promise<void> {\n if (getDatabaseEngine(db, typeHint) !== 'postgres') return;\n if (confirmation.legacyTimezone !== 'UTC') {\n throw new Error(\n 'Legacy SMRT system timestamp migration requires an explicit UTC provenance confirmation',\n );\n }\n\n // One server-side statement makes the catalog change atomic and holds the\n // same transaction-scoped lock used by system bootstrap. Any failing column\n // rolls back every preceding ALTER in the block.\n await db.query(`\n DO $smrt_migrate_system_timestamps$\n DECLARE\n target RECORD;\n BEGIN\n PERFORM pg_advisory_xact_lock(\n hashtext('smrt'),\n hashtext('system-tables')\n );\n IF upper(current_setting('TimeZone')) NOT IN ('UTC', 'ETC/UTC', 'GMT') THEN\n RAISE EXCEPTION 'Refusing to reinterpret legacy SMRT timestamps outside a UTC PostgreSQL session; confirm historical writers used UTC and SET TIME ZONE UTC';\n END IF;\n FOR target IN\n SELECT table_name, column_name\n FROM information_schema.columns\n WHERE table_schema = current_schema()\n AND table_name LIKE '\\\\_smrt\\\\_%' ESCAPE '\\\\'\n AND data_type = 'timestamp without time zone'\n ORDER BY table_name, ordinal_position\n LOOP\n EXECUTE format(\n 'ALTER TABLE %I ALTER COLUMN %I TYPE TIMESTAMPTZ USING %I AT TIME ZONE ''UTC''',\n target.table_name,\n target.column_name,\n target.column_name\n );\n END LOOP;\n IF to_regprocedure('${LEGACY_POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY}') IS NOT NULL THEN\n DROP FUNCTION IF EXISTS ${LEGACY_POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY};\n EXECUTE $smrt_change_feed_ddl$\n${CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION.trim()}\n$smrt_change_feed_ddl$;\n END IF;\n END;\n $smrt_migrate_system_timestamps$;\n `);\n}\n\nfunction isDuplicateColumnError(error: unknown): boolean {\n const message =\n error && typeof error === 'object'\n ? String((error as { message?: unknown }).message || error)\n : String(error);\n\n return (\n /column .*already exists/i.test(message) ||\n /duplicate column name/i.test(message)\n );\n}\n\nfunction collectErrorDetails(\n error: unknown,\n seen = new Set<unknown>(),\n): {\n codes: Set<string>;\n messages: string[];\n} {\n const codes = new Set<string>();\n const messages: string[] = [];\n\n if (!error || seen.has(error)) {\n return { codes, messages };\n }\n\n seen.add(error);\n\n if (typeof error === 'object') {\n const errorRecord = error as Record<string, unknown>;\n\n if (typeof errorRecord.code === 'string') {\n codes.add(errorRecord.code);\n }\n\n for (const messageKey of ['message', 'detail']) {\n if (typeof errorRecord[messageKey] === 'string') {\n messages.push(errorRecord[messageKey]);\n }\n }\n\n for (const nestedKey of ['cause', 'context', 'originalError']) {\n const nested = collectErrorDetails(errorRecord[nestedKey], seen);\n for (const code of nested.codes) {\n codes.add(code);\n }\n messages.push(...nested.messages);\n }\n } else {\n messages.push(String(error));\n }\n\n return { codes, messages };\n}\n\nfunction isDuplicateIndexRaceError(error: unknown, indexName: string): boolean {\n const { codes, messages } = collectErrorDetails(error);\n const message = messages.join('\\n').toLowerCase();\n const normalizedIndexName = indexName.toLowerCase();\n const hasDuplicateCode =\n codes.has('23505') || /\\bcode\\s*=\\s*23505\\b/.test(message);\n const hasRelationExistsCode =\n codes.has('42P07') || /\\bcode\\s*=\\s*42p07\\b/i.test(message);\n\n if (!message.includes(normalizedIndexName)) {\n return false;\n }\n\n if (hasDuplicateCode && message.includes('pg_class_relname_nsp_index')) {\n return true;\n }\n\n if (hasRelationExistsCode && /relation .*already exists/i.test(message)) {\n return true;\n }\n\n return false;\n}\n\nasync function addColumnIfMissing(\n db: DatabaseInterface,\n tableName: string,\n columnName: string,\n definition: string,\n typeHint?: string,\n): Promise<void> {\n const engine = getDatabaseEngine(db, typeHint);\n\n if (engine === 'postgres') {\n if (await columnExists(db, tableName, columnName, typeHint)) {\n return;\n }\n\n await db.query(\n `ALTER TABLE ${tableName} ADD COLUMN IF NOT EXISTS ${columnName} ${definition}`,\n );\n return;\n }\n\n if (await columnExists(db, tableName, columnName, typeHint)) {\n return;\n }\n\n try {\n await db.query(\n `ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${definition}`,\n );\n } catch (error) {\n if (isDuplicateColumnError(error)) {\n return;\n }\n\n throw error;\n }\n}\n\nasync function indexExists(\n db: DatabaseInterface,\n indexName: string,\n typeHint?: string,\n): Promise<boolean> {\n try {\n const engine = getDatabaseEngine(db, typeHint);\n if (engine === 'postgres') {\n const result = await db.query(\n 'SELECT 1 FROM pg_indexes WHERE schemaname = current_schema() AND indexname = $1 LIMIT 1',\n indexName,\n );\n return getQueryRows(result).length > 0;\n }\n\n const result = await db.query(\n `SELECT 1 FROM sqlite_master WHERE type = 'index' AND name = ? LIMIT 1`,\n indexName,\n );\n return getQueryRows(result).length > 0;\n } catch {\n return false;\n }\n}\n\nasync function addIndexIfMissing(\n db: DatabaseInterface,\n indexName: string,\n tableName: string,\n columnName: string,\n typeHint?: string,\n): Promise<void> {\n if (await indexExists(db, indexName, typeHint)) {\n return;\n }\n\n try {\n await db.query(\n `CREATE INDEX IF NOT EXISTS ${indexName} ON ${tableName}(${columnName})`,\n );\n } catch (error) {\n if (isDuplicateIndexRaceError(error, indexName)) {\n if (await indexExists(db, indexName, typeHint)) {\n return;\n }\n }\n\n throw error;\n }\n}\n\nasync function addUniqueIndexIfMissing(\n db: DatabaseInterface,\n indexName: string,\n tableName: string,\n columns: string,\n typeHint?: string,\n): Promise<void> {\n if (await indexExists(db, indexName, typeHint)) {\n return;\n }\n\n try {\n await db.query(\n `CREATE UNIQUE INDEX IF NOT EXISTS ${indexName} ON ${tableName}(${columns})`,\n );\n } catch (error) {\n if (isDuplicateIndexRaceError(error, indexName)) {\n if (await indexExists(db, indexName, typeHint)) {\n return;\n }\n }\n\n throw error;\n }\n}\n\n/**\n * Migrate the legacy `UNIQUE(signal_type, subscriber)` subscription identity to\n * the tenant-scoped `UNIQUE(tenant_id, signal_type, subscriber)` (S5 #1398).\n *\n * The old identity let any tenant overwrite/delete/enable another tenant's\n * subscription because the natural key ignored the tenant. This:\n * 1. Creates the tenant-scoped unique index used by upsert conflict handling.\n * 2. Drops the legacy 2-column unique constraint:\n * - **SQLite**: inline `UNIQUE(...)` constraints can't be dropped in place,\n * so the table is rebuilt without it (data preserved). The legacy\n * constraint surfaces as an auto-created `sqlite_autoindex_*` index.\n * - **Postgres**: the inline constraint is dropped by its conventional\n * name if present.\n *\n * Additive and idempotent: re-running is a no-op once migrated.\n */\nasync function migrateDispatchSubscriptionsIdentity(\n db: DatabaseInterface,\n typeHint?: string,\n): Promise<void> {\n const engine = getDatabaseEngine(db, typeHint);\n\n // 1. Tenant-scoped unique index (used by upsert's ON CONFLICT target).\n await addUniqueIndexIfMissing(\n db,\n 'uq_smrt_dispatch_subs_tenant_signal_subscriber',\n '_smrt_dispatch_subscriptions',\n 'tenant_id, signal_type, subscriber',\n typeHint,\n );\n\n // 2. Drop the legacy (signal_type, subscriber) uniqueness.\n if (engine === 'postgres') {\n // Inline table-level UNIQUE constraints get a conventional name. Drop it if\n // present; ignore if it was never created under that name.\n try {\n await db.query(\n 'ALTER TABLE _smrt_dispatch_subscriptions DROP CONSTRAINT IF EXISTS _smrt_dispatch_subscriptions_signal_type_subscriber_key',\n );\n } catch {\n // Best-effort: a differently-named legacy constraint is left in place; the\n // tenant-scoped index above still enforces correct per-tenant identity.\n }\n return;\n }\n\n // SQLite: detect a legacy auto-index over exactly (signal_type, subscriber)\n // and, if found, rebuild the table without the inline UNIQUE.\n if (engine !== 'sqlite') {\n return;\n }\n\n let legacyAutoIndex: string | null = null;\n try {\n const result = await db.query(\n `SELECT name FROM sqlite_master\n WHERE type = 'index'\n AND tbl_name = '_smrt_dispatch_subscriptions'\n AND name LIKE 'sqlite_autoindex_%'`,\n );\n for (const row of getQueryRows(result)) {\n const indexName = String(row.name);\n const info = await db.query(`PRAGMA index_info(${indexName})`);\n const cols = getQueryRows(info)\n .map((r) => String(r.name))\n .sort();\n if (\n cols.length === 2 &&\n cols[0] === 'signal_type' &&\n cols[1] === 'subscriber'\n ) {\n legacyAutoIndex = indexName;\n break;\n }\n }\n } catch {\n // Unable to introspect → assume no legacy constraint (fresh tables created\n // from the new schema have none).\n legacyAutoIndex = null;\n }\n\n if (!legacyAutoIndex) {\n return;\n }\n\n // Rebuild the table without the inline UNIQUE(signal_type, subscriber).\n // System table is small; this runs once per legacy database.\n await db.query('PRAGMA foreign_keys=OFF');\n try {\n await db.query(`\n CREATE TABLE _smrt_dispatch_subscriptions_new (\n id TEXT PRIMARY KEY,\n signal_type TEXT NOT NULL,\n subscriber TEXT NOT NULL,\n handler TEXT NOT NULL DEFAULT 'handleDispatch',\n delivery TEXT NOT NULL DEFAULT 'compete',\n enabled INTEGER DEFAULT 1,\n tenant_id TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n )\n `);\n await db.query(`\n INSERT INTO _smrt_dispatch_subscriptions_new\n (id, signal_type, subscriber, handler, delivery, enabled, tenant_id, created_at, updated_at)\n SELECT id, signal_type, subscriber, handler, delivery, enabled, tenant_id, created_at, updated_at\n FROM _smrt_dispatch_subscriptions\n `);\n await db.query('DROP TABLE _smrt_dispatch_subscriptions');\n await db.query(\n 'ALTER TABLE _smrt_dispatch_subscriptions_new RENAME TO _smrt_dispatch_subscriptions',\n );\n // Recreate the secondary indexes (the rebuild dropped them) plus the\n // tenant-scoped unique index.\n await db.query(\n 'CREATE UNIQUE INDEX IF NOT EXISTS uq_smrt_dispatch_subs_tenant_signal_subscriber ON _smrt_dispatch_subscriptions(tenant_id, signal_type, subscriber)',\n );\n await db.query(\n 'CREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_subscriber ON _smrt_dispatch_subscriptions(subscriber)',\n );\n await db.query(\n 'CREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_tenant_id ON _smrt_dispatch_subscriptions(tenant_id)',\n );\n await db.query(\n 'CREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_signal_type ON _smrt_dispatch_subscriptions(signal_type)',\n );\n await db.query(\n 'CREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_enabled ON _smrt_dispatch_subscriptions(enabled)',\n );\n } finally {\n await db.query('PRAGMA foreign_keys=ON');\n }\n}\n\nexport async function ensureDispatchSystemTableCompatibility(\n db: DatabaseInterface,\n typeHint?: string,\n): Promise<void> {\n if (!(await tableExists(db, '_smrt_dispatch', typeHint))) {\n return;\n }\n\n await addColumnIfMissing(\n db,\n '_smrt_dispatch',\n 'target_subscriber',\n 'TEXT',\n typeHint,\n );\n await addColumnIfMissing(\n db,\n '_smrt_dispatch',\n 'correlation_id',\n 'TEXT',\n typeHint,\n );\n // Tenant isolation (S5 #1398): dispatches are scoped to the emitting\n // tenant context so a subscriber in tenant A cannot snoop/claim tenant B's\n // dispatches. Nullable so non-tenant (global) dispatches keep tenant_id NULL.\n await addColumnIfMissing(db, '_smrt_dispatch', 'tenant_id', 'TEXT', typeHint);\n await addIndexIfMissing(\n db,\n 'idx_smrt_dispatch_target',\n '_smrt_dispatch',\n 'target_subscriber',\n typeHint,\n );\n await addIndexIfMissing(\n db,\n 'idx_smrt_dispatch_correlation',\n '_smrt_dispatch',\n 'correlation_id',\n typeHint,\n );\n await addIndexIfMissing(\n db,\n 'idx_smrt_dispatch_tenant_id',\n '_smrt_dispatch',\n 'tenant_id',\n typeHint,\n );\n}\n\nexport async function ensureDispatchSubscriptionsSystemTableCompatibility(\n db: DatabaseInterface,\n typeHint?: string,\n): Promise<void> {\n if (!(await tableExists(db, '_smrt_dispatch_subscriptions', typeHint))) {\n return;\n }\n\n await addColumnIfMissing(\n db,\n '_smrt_dispatch_subscriptions',\n 'delivery',\n \"TEXT NOT NULL DEFAULT 'compete'\",\n typeHint,\n );\n // Tenant isolation (S5 #1398): records the tenant context a subscription was\n // registered in. Nullable so global/non-tenant subscriptions keep it NULL.\n await addColumnIfMissing(\n db,\n '_smrt_dispatch_subscriptions',\n 'tenant_id',\n 'TEXT',\n typeHint,\n );\n await addIndexIfMissing(\n db,\n 'idx_smrt_dispatch_subs_tenant_id',\n '_smrt_dispatch_subscriptions',\n 'tenant_id',\n typeHint,\n );\n // Migrate the subscription natural key to (tenant_id, signal_type, subscriber)\n // so tenants can't clobber each other's subscriptions (S5 #1398).\n await migrateDispatchSubscriptionsIdentity(db, typeHint);\n}\n\nexport async function ensureJobsSystemTableCompatibility(\n db: DatabaseInterface,\n typeHint?: string,\n): Promise<void> {\n if (!(await tableExists(db, '_smrt_jobs', typeHint))) {\n return;\n }\n\n await addColumnIfMissing(db, '_smrt_jobs', 'tenant_id', 'TEXT', typeHint);\n await addIndexIfMissing(\n db,\n 'idx_smrt_jobs_tenant_id',\n '_smrt_jobs',\n 'tenant_id',\n typeHint,\n );\n}\n\nexport async function ensureJobEventsSystemTableCompatibility(\n db: DatabaseInterface,\n typeHint?: string,\n): Promise<void> {\n if (!(await tableExists(db, '_smrt_job_events', typeHint))) {\n return;\n }\n\n await addColumnIfMissing(\n db,\n '_smrt_job_events',\n 'tenant_id',\n 'TEXT',\n typeHint,\n );\n await addIndexIfMissing(\n db,\n 'idx_smrt_job_events_tenant_id',\n '_smrt_job_events',\n 'tenant_id',\n typeHint,\n );\n await addIndexIfMissing(\n db,\n 'idx_smrt_job_events_job_id',\n '_smrt_job_events',\n 'job_id',\n typeHint,\n );\n await addIndexIfMissing(\n db,\n 'idx_smrt_job_events_type',\n '_smrt_job_events',\n 'type',\n typeHint,\n );\n await addIndexIfMissing(\n db,\n 'idx_smrt_job_events_created_at',\n '_smrt_job_events',\n 'created_at',\n typeHint,\n );\n}\n\nexport async function ensureLegacySystemTableCompatibility(\n db: DatabaseInterface,\n typeHint?: string,\n): Promise<void> {\n await ensureDispatchSystemTableCompatibility(db, typeHint);\n await ensureDispatchSubscriptionsSystemTableCompatibility(db, typeHint);\n await ensureJobsSystemTableCompatibility(db, typeHint);\n await ensureJobEventsSystemTableCompatibility(db, typeHint);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"compatibility.js","names":[],"sources":["../../src/system/compatibility.ts"],"sourcesContent":["import type { DatabaseInterface } from '@happyvertical/sql';\nimport { detectEngine } from '../schema/ddl/index.js';\nimport {\n CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION,\n LEGACY_POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY,\n} from './schema.js';\n\ntype DatabaseWithConfig = DatabaseInterface & {\n config?: {\n type?: string;\n url?: string;\n };\n type?: string;\n};\n\nfunction getQueryRows(result: unknown): Record<string, unknown>[] {\n if (Array.isArray(result)) {\n return result as Record<string, unknown>[];\n }\n\n if (result && typeof result === 'object' && 'rows' in result) {\n const rows = (result as { rows?: unknown }).rows;\n if (Array.isArray(rows)) {\n return rows as Record<string, unknown>[];\n }\n }\n\n return [];\n}\n\nexport async function tableExists(\n db: DatabaseInterface,\n tableName: string,\n typeHint?: string,\n): Promise<boolean> {\n try {\n const engine = getDatabaseEngine(db, typeHint);\n if (engine === 'postgres') {\n const result = await db.query(\n 'SELECT 1 FROM information_schema.tables WHERE table_schema = current_schema() AND table_name = $1 LIMIT 1',\n tableName,\n );\n return getQueryRows(result).length > 0;\n }\n\n await db.query(`SELECT 1 FROM ${tableName} LIMIT 1`);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function columnExists(\n db: DatabaseInterface,\n tableName: string,\n columnName: string,\n typeHint?: string,\n): Promise<boolean> {\n try {\n const engine = getDatabaseEngine(db, typeHint);\n if (engine === 'postgres') {\n const result = await db.query(\n 'SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = $1 AND column_name = $2 LIMIT 1',\n tableName,\n columnName,\n );\n return getQueryRows(result).length > 0;\n }\n\n const result = await db.query(`PRAGMA table_info(${tableName})`);\n return getQueryRows(result).some((row) => row.name === columnName);\n } catch {\n return false;\n }\n}\n\nfunction getDatabaseUrl(db: DatabaseInterface): string {\n const dbWithConfig = db as DatabaseWithConfig;\n return db.url || dbWithConfig.config?.url || '';\n}\n\nexport function getDatabaseEngine(\n db: DatabaseInterface,\n typeHint?: string,\n): ReturnType<typeof detectEngine> {\n const dbWithConfig = db as DatabaseWithConfig;\n return detectEngine(\n getDatabaseUrl(db),\n typeHint || dbWithConfig.type || dbWithConfig.config?.type,\n );\n}\n\n/**\n * Upgrade framework-owned PostgreSQL timestamp columns to instant-safe storage.\n *\n * Legacy SMRT and system writers serialized instants as UTC wall times. The\n * conversion is intentionally fail-closed unless the migration session is UTC:\n * operators must first confirm that historical database-default/raw writers\n * also used UTC. A deployment with non-UTC legacy writers needs an explicit,\n * provenance-aware data migration rather than a guessed offset.\n */\nexport interface PostgresSystemTimestampMigrationConfirmation {\n /**\n * Explicitly confirmed timezone used by every historical writer of legacy\n * timezone-naive system timestamps. Only UTC is supported by this helper.\n */\n legacyTimezone: 'UTC';\n}\n\nexport interface PostgresSystemTimestampMigrationPlan {\n kind: 'column' | 'change-feed-function';\n tableName: string;\n columnName?: string;\n sql: string;\n}\n\nfunction quotePostgresIdentifier(identifier: string): string {\n return `\"${identifier.replaceAll('\"', '\"\"')}\"`;\n}\n\n/**\n * Read-only plan for the framework-owned timestamp conversion. This is kept\n * separate from the atomic mutator so CLI dry-runs can report every ALTER\n * without bootstrapping or changing framework tables.\n */\nexport async function planPostgresSystemTimestampMigrations(\n db: DatabaseInterface,\n confirmation: PostgresSystemTimestampMigrationConfirmation,\n typeHint?: string,\n): Promise<PostgresSystemTimestampMigrationPlan[]> {\n if (getDatabaseEngine(db, typeHint) !== 'postgres') return [];\n if (confirmation.legacyTimezone !== 'UTC') {\n throw new Error(\n 'Legacy SMRT system timestamp migration requires an explicit UTC provenance confirmation',\n );\n }\n\n const sessionRows = getQueryRows(\n await db.query(`\n SELECT\n current_setting('TimeZone') AS timezone,\n to_regprocedure('${LEGACY_POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY}')\n IS NOT NULL AS legacy_change_feed_function_exists\n `),\n );\n const session = sessionRows[0] ?? {};\n const timezone = String(session.timezone ?? '').toUpperCase();\n if (!['UTC', 'ETC/UTC', 'GMT'].includes(timezone)) {\n throw new Error(\n `Refusing to preview legacy SMRT timestamp migration outside a UTC PostgreSQL session (current TimeZone: ${String(session.timezone ?? 'unknown')})`,\n );\n }\n\n const rows = getQueryRows(\n await db.query(`\n SELECT table_name, column_name\n FROM information_schema.columns\n WHERE table_schema = current_schema()\n AND table_name LIKE '\\\\_smrt\\\\_%' ESCAPE '\\\\'\n AND data_type = 'timestamp without time zone'\n ORDER BY table_name, ordinal_position\n `),\n );\n\n const plans: PostgresSystemTimestampMigrationPlan[] = rows.map((row) => {\n const tableName = String(row.table_name);\n const columnName = String(row.column_name);\n const table = quotePostgresIdentifier(tableName);\n const column = quotePostgresIdentifier(columnName);\n return {\n kind: 'column',\n tableName,\n columnName,\n sql: `ALTER TABLE ${table} ALTER COLUMN ${column} TYPE TIMESTAMPTZ USING ${column} AT TIME ZONE 'UTC'`,\n };\n });\n\n if (\n session.legacy_change_feed_function_exists === true ||\n session.legacy_change_feed_function_exists === 't' ||\n session.legacy_change_feed_function_exists === 'true' ||\n session.legacy_change_feed_function_exists === 1\n ) {\n plans.push({\n kind: 'change-feed-function',\n tableName: '_smrt_changes',\n sql: `DROP FUNCTION IF EXISTS ${LEGACY_POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY};\\n${CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION.trim()}`,\n });\n }\n\n return plans;\n}\n\n/**\n * Fail closed when framework-owned PostgreSQL tables still contain legacy\n * timezone-naive timestamps. Call this before recording the current system\n * schema version so a partial installation cannot be stamped as upgraded.\n */\nexport async function assertPostgresSystemTimestampsCurrent(\n db: DatabaseInterface,\n typeHint?: string,\n): Promise<void> {\n if (getDatabaseEngine(db, typeHint) !== 'postgres') return;\n\n const rows = getQueryRows(\n await db.query(`\n SELECT table_name, column_name\n FROM information_schema.columns\n WHERE table_schema = current_schema()\n AND table_name LIKE '\\\\_smrt\\\\_%' ESCAPE '\\\\'\n AND data_type = 'timestamp without time zone'\n ORDER BY table_name, ordinal_position\n `),\n );\n if (rows.length === 0) return;\n\n const columns = rows\n .map((row) => `${String(row.table_name)}.${String(row.column_name)}`)\n .join(', ');\n throw new Error(\n `Legacy SMRT system timestamps remain (${columns}); run the explicit audited migratePostgresSystemTimestamps() migration before bootstrap`,\n );\n}\n\nexport async function migratePostgresSystemTimestamps(\n db: DatabaseInterface,\n confirmation: PostgresSystemTimestampMigrationConfirmation,\n typeHint?: string,\n): Promise<void> {\n if (getDatabaseEngine(db, typeHint) !== 'postgres') return;\n if (confirmation.legacyTimezone !== 'UTC') {\n throw new Error(\n 'Legacy SMRT system timestamp migration requires an explicit UTC provenance confirmation',\n );\n }\n\n // One server-side statement makes the catalog change atomic and holds the\n // same transaction-scoped lock used by system bootstrap. Any failing column\n // rolls back every preceding ALTER in the block.\n await db.query(`\n DO $smrt_migrate_system_timestamps$\n DECLARE\n target RECORD;\n BEGIN\n PERFORM pg_advisory_xact_lock(\n hashtext('smrt'),\n hashtext('system-tables')\n );\n IF upper(current_setting('TimeZone')) NOT IN ('UTC', 'ETC/UTC', 'GMT') THEN\n RAISE EXCEPTION 'Refusing to reinterpret legacy SMRT timestamps outside a UTC PostgreSQL session; confirm historical writers used UTC and SET TIME ZONE UTC';\n END IF;\n FOR target IN\n SELECT table_name, column_name\n FROM information_schema.columns\n WHERE table_schema = current_schema()\n AND table_name LIKE '\\\\_smrt\\\\_%' ESCAPE '\\\\'\n AND data_type = 'timestamp without time zone'\n ORDER BY table_name, ordinal_position\n LOOP\n EXECUTE format(\n 'ALTER TABLE %I ALTER COLUMN %I TYPE TIMESTAMPTZ USING %I AT TIME ZONE ''UTC''',\n target.table_name,\n target.column_name,\n target.column_name\n );\n END LOOP;\n IF to_regprocedure('${LEGACY_POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY}') IS NOT NULL THEN\n DROP FUNCTION IF EXISTS ${LEGACY_POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY};\n EXECUTE $smrt_change_feed_ddl$\n${CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION.trim()}\n$smrt_change_feed_ddl$;\n END IF;\n END;\n $smrt_migrate_system_timestamps$;\n `);\n}\n\nfunction isDuplicateColumnError(error: unknown): boolean {\n const message =\n error && typeof error === 'object'\n ? String((error as { message?: unknown }).message || error)\n : String(error);\n\n return (\n /column .*already exists/i.test(message) ||\n /duplicate column name/i.test(message)\n );\n}\n\nfunction collectErrorDetails(\n error: unknown,\n seen = new Set<unknown>(),\n): {\n codes: Set<string>;\n messages: string[];\n} {\n const codes = new Set<string>();\n const messages: string[] = [];\n\n if (!error || seen.has(error)) {\n return { codes, messages };\n }\n\n seen.add(error);\n\n if (typeof error === 'object') {\n const errorRecord = error as Record<string, unknown>;\n\n if (typeof errorRecord.code === 'string') {\n codes.add(errorRecord.code);\n }\n\n for (const messageKey of ['message', 'detail']) {\n if (typeof errorRecord[messageKey] === 'string') {\n messages.push(errorRecord[messageKey]);\n }\n }\n\n for (const nestedKey of ['cause', 'context', 'originalError']) {\n const nested = collectErrorDetails(errorRecord[nestedKey], seen);\n for (const code of nested.codes) {\n codes.add(code);\n }\n messages.push(...nested.messages);\n }\n } else {\n messages.push(String(error));\n }\n\n return { codes, messages };\n}\n\nfunction isDuplicateIndexRaceError(error: unknown, indexName: string): boolean {\n const { codes, messages } = collectErrorDetails(error);\n const message = messages.join('\\n').toLowerCase();\n const normalizedIndexName = indexName.toLowerCase();\n const hasDuplicateCode =\n codes.has('23505') || /\\bcode\\s*=\\s*23505\\b/.test(message);\n const hasRelationExistsCode =\n codes.has('42P07') || /\\bcode\\s*=\\s*42p07\\b/i.test(message);\n\n if (!message.includes(normalizedIndexName)) {\n return false;\n }\n\n if (hasDuplicateCode && message.includes('pg_class_relname_nsp_index')) {\n return true;\n }\n\n if (hasRelationExistsCode && /relation .*already exists/i.test(message)) {\n return true;\n }\n\n return false;\n}\n\nasync function addColumnIfMissing(\n db: DatabaseInterface,\n tableName: string,\n columnName: string,\n definition: string,\n typeHint?: string,\n): Promise<void> {\n const engine = getDatabaseEngine(db, typeHint);\n\n if (engine === 'postgres') {\n if (await columnExists(db, tableName, columnName, typeHint)) {\n return;\n }\n\n await db.query(\n `ALTER TABLE ${tableName} ADD COLUMN IF NOT EXISTS ${columnName} ${definition}`,\n );\n return;\n }\n\n if (await columnExists(db, tableName, columnName, typeHint)) {\n return;\n }\n\n try {\n await db.query(\n `ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${definition}`,\n );\n } catch (error) {\n if (isDuplicateColumnError(error)) {\n return;\n }\n\n throw error;\n }\n}\n\nasync function indexExists(\n db: DatabaseInterface,\n indexName: string,\n typeHint?: string,\n): Promise<boolean> {\n try {\n const engine = getDatabaseEngine(db, typeHint);\n if (engine === 'postgres') {\n const result = await db.query(\n 'SELECT 1 FROM pg_indexes WHERE schemaname = current_schema() AND indexname = $1 LIMIT 1',\n indexName,\n );\n return getQueryRows(result).length > 0;\n }\n\n const result = await db.query(\n `SELECT 1 FROM sqlite_master WHERE type = 'index' AND name = ? LIMIT 1`,\n indexName,\n );\n return getQueryRows(result).length > 0;\n } catch {\n return false;\n }\n}\n\nasync function addIndexIfMissing(\n db: DatabaseInterface,\n indexName: string,\n tableName: string,\n columnName: string,\n typeHint?: string,\n): Promise<void> {\n if (await indexExists(db, indexName, typeHint)) {\n return;\n }\n\n try {\n await db.query(\n `CREATE INDEX IF NOT EXISTS ${indexName} ON ${tableName}(${columnName})`,\n );\n } catch (error) {\n if (isDuplicateIndexRaceError(error, indexName)) {\n if (await indexExists(db, indexName, typeHint)) {\n return;\n }\n }\n\n throw error;\n }\n}\n\nasync function addUniqueIndexIfMissing(\n db: DatabaseInterface,\n indexName: string,\n tableName: string,\n columns: string,\n typeHint?: string,\n): Promise<void> {\n if (await indexExists(db, indexName, typeHint)) {\n return;\n }\n\n try {\n await db.query(\n `CREATE UNIQUE INDEX IF NOT EXISTS ${indexName} ON ${tableName}(${columns})`,\n );\n } catch (error) {\n if (isDuplicateIndexRaceError(error, indexName)) {\n if (await indexExists(db, indexName, typeHint)) {\n return;\n }\n }\n\n throw error;\n }\n}\n\n/**\n * Migrate the legacy `UNIQUE(signal_type, subscriber)` subscription identity to\n * the tenant-scoped `UNIQUE(tenant_id, signal_type, subscriber)` (S5 #1398).\n *\n * The old identity let any tenant overwrite/delete/enable another tenant's\n * subscription because the natural key ignored the tenant. This:\n * 1. Creates the tenant-scoped unique index used by upsert conflict handling.\n * 2. Drops the legacy 2-column unique constraint:\n * - **SQLite**: inline `UNIQUE(...)` constraints can't be dropped in place,\n * so the table is rebuilt without it (data preserved). The legacy\n * constraint surfaces as an auto-created `sqlite_autoindex_*` index.\n * - **Postgres**: the inline constraint is dropped by its conventional\n * name if present.\n *\n * Additive and idempotent: re-running is a no-op once migrated.\n */\nasync function migrateDispatchSubscriptionsIdentity(\n db: DatabaseInterface,\n typeHint?: string,\n): Promise<void> {\n const engine = getDatabaseEngine(db, typeHint);\n\n // 1. Tenant-scoped unique index (used by upsert's ON CONFLICT target).\n await addUniqueIndexIfMissing(\n db,\n 'uq_smrt_dispatch_subs_tenant_signal_subscriber',\n '_smrt_dispatch_subscriptions',\n 'tenant_id, signal_type, subscriber',\n typeHint,\n );\n\n // 2. Drop the legacy (signal_type, subscriber) uniqueness.\n if (engine === 'postgres') {\n // Inline table-level UNIQUE constraints get a conventional name. Drop it if\n // present; ignore if it was never created under that name.\n try {\n await db.query(\n 'ALTER TABLE _smrt_dispatch_subscriptions DROP CONSTRAINT IF EXISTS _smrt_dispatch_subscriptions_signal_type_subscriber_key',\n );\n } catch {\n // Best-effort: a differently-named legacy constraint is left in place; the\n // tenant-scoped index above still enforces correct per-tenant identity.\n }\n return;\n }\n\n // SQLite: detect a legacy auto-index over exactly (signal_type, subscriber)\n // and, if found, rebuild the table without the inline UNIQUE.\n if (engine !== 'sqlite') {\n return;\n }\n\n let legacyAutoIndex: string | null = null;\n try {\n const result = await db.query(\n `SELECT name FROM sqlite_master\n WHERE type = 'index'\n AND tbl_name = '_smrt_dispatch_subscriptions'\n AND name LIKE 'sqlite_autoindex_%'`,\n );\n for (const row of getQueryRows(result)) {\n const indexName = String(row.name);\n const info = await db.query(`PRAGMA index_info(${indexName})`);\n const cols = getQueryRows(info)\n .map((r) => String(r.name))\n .sort();\n if (\n cols.length === 2 &&\n cols[0] === 'signal_type' &&\n cols[1] === 'subscriber'\n ) {\n legacyAutoIndex = indexName;\n break;\n }\n }\n } catch {\n // Unable to introspect → assume no legacy constraint (fresh tables created\n // from the new schema have none).\n legacyAutoIndex = null;\n }\n\n if (!legacyAutoIndex) {\n return;\n }\n\n // Rebuild the table without the inline UNIQUE(signal_type, subscriber).\n // System table is small; this runs once per legacy database.\n await db.query('PRAGMA foreign_keys=OFF');\n try {\n await db.query(`\n CREATE TABLE _smrt_dispatch_subscriptions_new (\n id TEXT PRIMARY KEY,\n signal_type TEXT NOT NULL,\n subscriber TEXT NOT NULL,\n handler TEXT NOT NULL DEFAULT 'handleDispatch',\n delivery TEXT NOT NULL DEFAULT 'compete',\n enabled INTEGER DEFAULT 1,\n tenant_id TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n )\n `);\n await db.query(`\n INSERT INTO _smrt_dispatch_subscriptions_new\n (id, signal_type, subscriber, handler, delivery, enabled, tenant_id, created_at, updated_at)\n SELECT id, signal_type, subscriber, handler, delivery, enabled, tenant_id, created_at, updated_at\n FROM _smrt_dispatch_subscriptions\n `);\n await db.query('DROP TABLE _smrt_dispatch_subscriptions');\n await db.query(\n 'ALTER TABLE _smrt_dispatch_subscriptions_new RENAME TO _smrt_dispatch_subscriptions',\n );\n // Recreate the secondary indexes (the rebuild dropped them) plus the\n // tenant-scoped unique index.\n await db.query(\n 'CREATE UNIQUE INDEX IF NOT EXISTS uq_smrt_dispatch_subs_tenant_signal_subscriber ON _smrt_dispatch_subscriptions(tenant_id, signal_type, subscriber)',\n );\n await db.query(\n 'CREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_subscriber ON _smrt_dispatch_subscriptions(subscriber)',\n );\n await db.query(\n 'CREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_tenant_id ON _smrt_dispatch_subscriptions(tenant_id)',\n );\n await db.query(\n 'CREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_signal_type ON _smrt_dispatch_subscriptions(signal_type)',\n );\n await db.query(\n 'CREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_enabled ON _smrt_dispatch_subscriptions(enabled)',\n );\n } finally {\n await db.query('PRAGMA foreign_keys=ON');\n }\n}\n\nexport async function ensureDispatchSystemTableCompatibility(\n db: DatabaseInterface,\n typeHint?: string,\n): Promise<void> {\n if (!(await tableExists(db, '_smrt_dispatch', typeHint))) {\n return;\n }\n\n await addColumnIfMissing(\n db,\n '_smrt_dispatch',\n 'target_subscriber',\n 'TEXT',\n typeHint,\n );\n await addColumnIfMissing(\n db,\n '_smrt_dispatch',\n 'correlation_id',\n 'TEXT',\n typeHint,\n );\n // Tenant isolation (S5 #1398): dispatches are scoped to the emitting\n // tenant context so a subscriber in tenant A cannot snoop/claim tenant B's\n // dispatches. Nullable so non-tenant (global) dispatches keep tenant_id NULL.\n await addColumnIfMissing(db, '_smrt_dispatch', 'tenant_id', 'TEXT', typeHint);\n await addIndexIfMissing(\n db,\n 'idx_smrt_dispatch_target',\n '_smrt_dispatch',\n 'target_subscriber',\n typeHint,\n );\n await addIndexIfMissing(\n db,\n 'idx_smrt_dispatch_correlation',\n '_smrt_dispatch',\n 'correlation_id',\n typeHint,\n );\n await addIndexIfMissing(\n db,\n 'idx_smrt_dispatch_tenant_id',\n '_smrt_dispatch',\n 'tenant_id',\n typeHint,\n );\n}\n\nexport async function ensureDispatchSubscriptionsSystemTableCompatibility(\n db: DatabaseInterface,\n typeHint?: string,\n): Promise<void> {\n if (!(await tableExists(db, '_smrt_dispatch_subscriptions', typeHint))) {\n return;\n }\n\n await addColumnIfMissing(\n db,\n '_smrt_dispatch_subscriptions',\n 'delivery',\n \"TEXT NOT NULL DEFAULT 'compete'\",\n typeHint,\n );\n // Tenant isolation (S5 #1398): records the tenant context a subscription was\n // registered in. Nullable so global/non-tenant subscriptions keep it NULL.\n await addColumnIfMissing(\n db,\n '_smrt_dispatch_subscriptions',\n 'tenant_id',\n 'TEXT',\n typeHint,\n );\n await addIndexIfMissing(\n db,\n 'idx_smrt_dispatch_subs_tenant_id',\n '_smrt_dispatch_subscriptions',\n 'tenant_id',\n typeHint,\n );\n // Migrate the subscription natural key to (tenant_id, signal_type, subscriber)\n // so tenants can't clobber each other's subscriptions (S5 #1398).\n await migrateDispatchSubscriptionsIdentity(db, typeHint);\n}\n\nexport async function ensureJobsSystemTableCompatibility(\n db: DatabaseInterface,\n typeHint?: string,\n): Promise<void> {\n if (!(await tableExists(db, '_smrt_jobs', typeHint))) {\n return;\n }\n\n await addColumnIfMissing(db, '_smrt_jobs', 'tenant_id', 'TEXT', typeHint);\n await addIndexIfMissing(\n db,\n 'idx_smrt_jobs_tenant_id',\n '_smrt_jobs',\n 'tenant_id',\n typeHint,\n );\n // MCP task support was added after `_smrt_jobs` had already shipped. Keep\n // the task projection in the job row so a task can never outlive (or lose\n // track of) the worker job that executes it. JSON is portable here because\n // SMRT's adapters serialize JSON fields to text where necessary.\n await addColumnIfMissing(db, '_smrt_jobs', 'task_id', 'TEXT', typeHint);\n await addColumnIfMissing(db, '_smrt_jobs', 'task_owner_id', 'TEXT', typeHint);\n await addColumnIfMissing(db, '_smrt_jobs', 'task_result', 'TEXT', typeHint);\n await addColumnIfMissing(\n db,\n '_smrt_jobs',\n 'task_input_requests',\n 'TEXT',\n typeHint,\n );\n await addColumnIfMissing(\n db,\n '_smrt_jobs',\n 'task_input_responses',\n 'TEXT',\n typeHint,\n );\n await addUniqueIndexIfMissing(\n db,\n 'idx_smrt_jobs_task_id',\n '_smrt_jobs',\n 'task_id',\n typeHint,\n );\n}\n\nexport async function ensureJobEventsSystemTableCompatibility(\n db: DatabaseInterface,\n typeHint?: string,\n): Promise<void> {\n if (!(await tableExists(db, '_smrt_job_events', typeHint))) {\n return;\n }\n\n await addColumnIfMissing(\n db,\n '_smrt_job_events',\n 'tenant_id',\n 'TEXT',\n typeHint,\n );\n await addIndexIfMissing(\n db,\n 'idx_smrt_job_events_tenant_id',\n '_smrt_job_events',\n 'tenant_id',\n typeHint,\n );\n await addIndexIfMissing(\n db,\n 'idx_smrt_job_events_job_id',\n '_smrt_job_events',\n 'job_id',\n typeHint,\n );\n await addIndexIfMissing(\n db,\n 'idx_smrt_job_events_type',\n '_smrt_job_events',\n 'type',\n typeHint,\n );\n await addIndexIfMissing(\n db,\n 'idx_smrt_job_events_created_at',\n '_smrt_job_events',\n 'created_at',\n typeHint,\n );\n}\n\nexport async function ensureLegacySystemTableCompatibility(\n db: DatabaseInterface,\n typeHint?: string,\n): Promise<void> {\n await ensureDispatchSystemTableCompatibility(db, typeHint);\n await ensureDispatchSubscriptionsSystemTableCompatibility(db, typeHint);\n await ensureJobsSystemTableCompatibility(db, typeHint);\n await ensureJobEventsSystemTableCompatibility(db, typeHint);\n}\n"],"mappings":"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"}
|
package/dist/system/types.d.ts
CHANGED
|
@@ -17,7 +17,10 @@ export interface NoteOptions {
|
|
|
17
17
|
confidence?: number;
|
|
18
18
|
/** Strategy version number */
|
|
19
19
|
version?: number;
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Expiration date persisted to `_smrt_contexts.expires_at`. Stored only —
|
|
22
|
+
* `recall()`/`recallAll()` do not filter expired rows, so callers own expiry.
|
|
23
|
+
*/
|
|
21
24
|
expiresAt?: Date;
|
|
22
25
|
}
|
|
23
26
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/system/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,sDAAsD;IACtD,KAAK,EAAE,OAAO,CAAC;IACf,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/system/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,6 +22,7 @@ export interface OxcScannerConstructor {
|
|
|
22
22
|
includePrivateMethods?: boolean;
|
|
23
23
|
includeStaticMethods?: boolean;
|
|
24
24
|
followImports?: boolean;
|
|
25
|
+
followSymbolicLinks?: boolean;
|
|
25
26
|
}): OxcScannerLike;
|
|
26
27
|
}
|
|
27
28
|
export interface ManifestAdapterLike {
|
|
@@ -34,5 +35,11 @@ export interface ManifestAdapterLike {
|
|
|
34
35
|
export interface ScannerModule {
|
|
35
36
|
OxcScanner: OxcScannerConstructor;
|
|
36
37
|
ManifestAdapter: new () => ManifestAdapterLike;
|
|
38
|
+
discoverSourceFiles(options: {
|
|
39
|
+
cwd: string;
|
|
40
|
+
include: string[];
|
|
41
|
+
exclude: string[];
|
|
42
|
+
followSymbolicLinks?: boolean;
|
|
43
|
+
}): Promise<string[]>;
|
|
37
44
|
}
|
|
38
45
|
//# sourceMappingURL=scanner-module.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanner-module.d.ts","sourceRoot":"","sources":["../../src/utils/scanner-module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,MAAM,WAAW,cAAc;IAC7B,cAAc,IAAI,OAAO,CAAC;QACxB,OAAO,EAAE;YACP,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACpC,MAAM,EAAE,KAAK,CAAC;gBACZ,QAAQ,EAAE,MAAM,CAAC;gBACjB,OAAO,EAAE,MAAM,CAAC;gBAChB,IAAI,CAAC,EAAE,MAAM,CAAC;gBACd,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;aAC/B,CAAC,CAAC;SACJ,CAAC;QACF,QAAQ,EAAE,OAAO,EAAE,CAAC;KACrB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,OAAO,EAAE;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,qBAAqB,CAAC,EAAE,OAAO,CAAC;QAChC,oBAAoB,CAAC,EAAE,OAAO,CAAC;QAC/B,aAAa,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"scanner-module.d.ts","sourceRoot":"","sources":["../../src/utils/scanner-module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,MAAM,WAAW,cAAc;IAC7B,cAAc,IAAI,OAAO,CAAC;QACxB,OAAO,EAAE;YACP,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACpC,MAAM,EAAE,KAAK,CAAC;gBACZ,QAAQ,EAAE,MAAM,CAAC;gBACjB,OAAO,EAAE,MAAM,CAAC;gBAChB,IAAI,CAAC,EAAE,MAAM,CAAC;gBACd,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;aAC/B,CAAC,CAAC;SACJ,CAAC;QACF,QAAQ,EAAE,OAAO,EAAE,CAAC;KACrB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,OAAO,EAAE;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,qBAAqB,CAAC,EAAE,OAAO,CAAC;QAChC,oBAAoB,CAAC,EAAE,OAAO,CAAC;QAC/B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,mBAAmB,CAAC,EAAE,OAAO,CAAC;KAC/B,GAAG,cAAc,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,CACR,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE;QACP,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACrC,GACA,mBAAmB,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,qBAAqB,CAAC;IAClC,eAAe,EAAE,UAAU,mBAAmB,CAAC;IAC/C,mBAAmB,CAAC,OAAO,EAAE;QAC3B,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;KAC/B,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CACvB"}
|
|
@@ -10,6 +10,13 @@ export interface SmrtPluginOptions {
|
|
|
10
10
|
include?: string[];
|
|
11
11
|
/** Patterns to exclude */
|
|
12
12
|
exclude?: string[];
|
|
13
|
+
/**
|
|
14
|
+
* Follow symbolic links while discovering sources. Defaults to `false`: a
|
|
15
|
+
* pnpm `node_modules` is a symlink graph with cycles, so a link-following
|
|
16
|
+
* walk does not terminate on a real install (#2275). Enable it only for a
|
|
17
|
+
* project that genuinely keeps sources behind a link.
|
|
18
|
+
*/
|
|
19
|
+
followSymbolicLinks?: boolean;
|
|
13
20
|
/** Output directory for generated files */
|
|
14
21
|
outDir?: string;
|
|
15
22
|
/** Enable hot module replacement */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EACV,qBAAqB,EAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,MAAM,EAAiC,MAAM,MAAM,CAAC;AAGlE,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,qBAAqB,CAAC;AA2B7B,YAAY,EACV,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,6BAA6B,EAC7B,uBAAuB,EACvB,iBAAiB,EACjB,2BAA2B,EAC3B,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,0BAA0B,CAAC;AAalC,MAAM,WAAW,iBAAiB;IAChC,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,6BAA6B;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qBAAqB;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,kFAAkF;IAClF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,4EAA4E;IAC5E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,wEAAwE;IACxE,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC;;OAEG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,8CAA8C;IAC9C,SAAS,CAAC,EAAE;QACV,yDAAyD;QACzD,OAAO,EAAE,OAAO,CAAC;QACjB,wEAAwE;QACxE,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,qEAAqE;QACrE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mEAAmE;QACnE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mDAAmD;QACnD,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB;;;;WAIG;QACH,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB;;;WAGG;QACH,YAAY,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;QACrC;;;;WAIG;QACH,WAAW,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,OAAO,CAAC;YAAC,cAAc,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAC9D,CAAC;IACF,mEAAmE;IACnE,SAAS,CAAC,EAAE,qBAAqB,GAAG,KAAK,CAAC;IAC1C;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AA+BD,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAG1D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,mBAAmB,GAC5B,MAAM,CAaR;AAED,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EACV,qBAAqB,EAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,MAAM,EAAiC,MAAM,MAAM,CAAC;AAGlE,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,qBAAqB,CAAC;AA2B7B,YAAY,EACV,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,6BAA6B,EAC7B,uBAAuB,EACvB,iBAAiB,EACjB,2BAA2B,EAC3B,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,0BAA0B,CAAC;AAalC,MAAM,WAAW,iBAAiB;IAChC,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,6BAA6B;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qBAAqB;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,kFAAkF;IAClF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,4EAA4E;IAC5E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,wEAAwE;IACxE,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC;;OAEG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,8CAA8C;IAC9C,SAAS,CAAC,EAAE;QACV,yDAAyD;QACzD,OAAO,EAAE,OAAO,CAAC;QACjB,wEAAwE;QACxE,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,qEAAqE;QACrE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mEAAmE;QACnE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mDAAmD;QACnD,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB;;;;WAIG;QACH,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB;;;WAGG;QACH,YAAY,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;QACrC;;;;WAIG;QACH,WAAW,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,OAAO,CAAC;YAAC,cAAc,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAC9D,CAAC;IACF,mEAAmE;IACnE,SAAS,CAAC,EAAE,qBAAqB,GAAG,KAAK,CAAC;IAC1C;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AA+BD,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAG1D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,mBAAmB,GAC5B,MAAM,CAaR;AAED,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAu7BlE;AAwTD;;;GAGG;AACH,wBAAsB,2BAA2B,CAC/C,QAAQ,EAAE,mBAAmB,EAC7B,WAAW,EAAE,MAAM,EACnB,oBAAoB,EAAE,MAAM,GAC3B,OAAO,CAAC,IAAI,CAAC,CAgaf"}
|
|
@@ -81,7 +81,7 @@ function smrtPlugin(options = {}) {
|
|
|
81
81
|
"**/*.test.ts",
|
|
82
82
|
"**/*.spec.ts",
|
|
83
83
|
"**/node_modules/**"
|
|
84
|
-
], hmr = true, watch = true, generateTypes = true, baseClasses = ["SmrtObject", "SmartObject"], followImports = true, typeDeclarationsPath = "src/types", mode = "auto", svelteKit = {
|
|
84
|
+
], followSymbolicLinks = false, hmr = true, watch = true, generateTypes = true, baseClasses = ["SmrtObject", "SmartObject"], followImports = true, typeDeclarationsPath = "src/types", mode = "auto", svelteKit = {
|
|
85
85
|
enabled: false,
|
|
86
86
|
routesDir: "src/routes/api",
|
|
87
87
|
objectsDir: "src/lib/objects",
|
|
@@ -495,7 +495,8 @@ function smrtPlugin(options = {}) {
|
|
|
495
495
|
const { results, resolved } = await new OxcScanner({
|
|
496
496
|
cwd: rootDir,
|
|
497
497
|
include,
|
|
498
|
-
exclude: safeExclude
|
|
498
|
+
exclude: safeExclude,
|
|
499
|
+
followSymbolicLinks
|
|
499
500
|
}).scanAndResolve();
|
|
500
501
|
if (results.errors.length > 0) {
|
|
501
502
|
console.error(`\n[smrt] ❌ Build failed: ${results.errors.length} error(s) during scanning:\n`);
|