@happyvertical/smrt-core 0.54.2 → 0.54.3

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.
Files changed (138) hide show
  1. package/AGENTS.md +6 -1
  2. package/README.md +8 -0
  3. package/agents/change-feed.md +7 -12
  4. package/agents/generators.md +13 -0
  5. package/agents/object-runtime.md +8 -0
  6. package/agents/schema-paths.md +14 -0
  7. package/dist/audit-context.d.ts +4 -0
  8. package/dist/audit-context.d.ts.map +1 -0
  9. package/dist/audit-context.js +16 -0
  10. package/dist/audit-context.js.map +1 -0
  11. package/dist/audit.d.ts +34 -0
  12. package/dist/audit.d.ts.map +1 -0
  13. package/dist/audit.js +63 -0
  14. package/dist/audit.js.map +1 -0
  15. package/dist/browser.d.ts +2 -1
  16. package/dist/browser.d.ts.map +1 -1
  17. package/dist/browser.js +4 -3
  18. package/dist/cascade.d.ts +1 -1
  19. package/dist/cascade.d.ts.map +1 -1
  20. package/dist/cascade.js +10 -7
  21. package/dist/cascade.js.map +1 -1
  22. package/dist/change-feed.js +1 -1
  23. package/dist/collection.d.ts +9 -2
  24. package/dist/collection.d.ts.map +1 -1
  25. package/dist/collection.js +59 -4
  26. package/dist/collection.js.map +1 -1
  27. package/dist/embedded-write-queue.d.ts +2 -0
  28. package/dist/embedded-write-queue.d.ts.map +1 -1
  29. package/dist/embedded-write-queue.js +12 -5
  30. package/dist/embedded-write-queue.js.map +1 -1
  31. package/dist/generators/changes-route.js +1 -1
  32. package/dist/generators/mcp-runtime-template.d.ts +2 -0
  33. package/dist/generators/mcp-runtime-template.d.ts.map +1 -1
  34. package/dist/generators/mcp-runtime-template.js +18 -16
  35. package/dist/generators/mcp-runtime-template.js.map +1 -1
  36. package/dist/generators/mcp-tool-name.d.ts +6 -0
  37. package/dist/generators/mcp-tool-name.d.ts.map +1 -1
  38. package/dist/generators/mcp-tool-name.js.map +1 -1
  39. package/dist/generators/mcp.d.ts +34 -1
  40. package/dist/generators/mcp.d.ts.map +1 -1
  41. package/dist/generators/mcp.js +148 -110
  42. package/dist/generators/mcp.js.map +1 -1
  43. package/dist/generators/rest.d.ts.map +1 -1
  44. package/dist/generators/rest.js +16 -7
  45. package/dist/generators/rest.js.map +1 -1
  46. package/dist/index.d.ts +3 -1
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +9 -7
  49. package/dist/junction.d.ts +29 -10
  50. package/dist/junction.d.ts.map +1 -1
  51. package/dist/junction.js +31 -12
  52. package/dist/junction.js.map +1 -1
  53. package/dist/knowledge.js +1 -1
  54. package/dist/knowledge.js.map +1 -1
  55. package/dist/manifest/manifest-loader.d.ts.map +1 -1
  56. package/dist/manifest/manifest-loader.js +1 -1
  57. package/dist/manifest/manifest-loader.js.map +1 -1
  58. package/dist/manifest/static-manifest.d.ts.map +1 -1
  59. package/dist/manifest/static-manifest.js +106 -9
  60. package/dist/manifest/static-manifest.js.map +1 -1
  61. package/dist/manifest/store.d.ts +8 -0
  62. package/dist/manifest/store.d.ts.map +1 -1
  63. package/dist/manifest/store.js +12 -2
  64. package/dist/manifest/store.js.map +1 -1
  65. package/dist/manifest.json +119 -9
  66. package/dist/migrations/differ.d.ts +3 -3
  67. package/dist/migrations/differ.d.ts.map +1 -1
  68. package/dist/migrations/differ.js +8 -4
  69. package/dist/migrations/differ.js.map +1 -1
  70. package/dist/object.d.ts +1 -0
  71. package/dist/object.d.ts.map +1 -1
  72. package/dist/object.js +19 -7
  73. package/dist/object.js.map +1 -1
  74. package/dist/registry/class-registration.d.ts.map +1 -1
  75. package/dist/registry/class-registration.js +75 -10
  76. package/dist/registry/class-registration.js.map +1 -1
  77. package/dist/registry/collection-resolution.d.ts +2 -2
  78. package/dist/registry/collection-resolution.d.ts.map +1 -1
  79. package/dist/registry/collection-resolution.js +2 -1
  80. package/dist/registry/collection-resolution.js.map +1 -1
  81. package/dist/registry/collision-policy.d.ts +8 -0
  82. package/dist/registry/collision-policy.d.ts.map +1 -1
  83. package/dist/registry/collision-policy.js +2 -2
  84. package/dist/registry/collision-policy.js.map +1 -1
  85. package/dist/registry/framework-base-classes.d.ts.map +1 -1
  86. package/dist/registry/framework-base-classes.js +2 -1
  87. package/dist/registry/framework-base-classes.js.map +1 -1
  88. package/dist/registry/schema-builder.d.ts.map +1 -1
  89. package/dist/registry/schema-builder.js +33 -4
  90. package/dist/registry/schema-builder.js.map +1 -1
  91. package/dist/registry/shared-state.d.ts +6 -0
  92. package/dist/registry/shared-state.d.ts.map +1 -1
  93. package/dist/registry/shared-state.js +12 -1
  94. package/dist/registry/shared-state.js.map +1 -1
  95. package/dist/registry/types.d.ts +7 -1
  96. package/dist/registry/types.d.ts.map +1 -1
  97. package/dist/registry.d.ts +7 -1
  98. package/dist/registry.d.ts.map +1 -1
  99. package/dist/registry.js +25 -3
  100. package/dist/registry.js.map +1 -1
  101. package/dist/scanner/manifest-generator.d.ts.map +1 -1
  102. package/dist/scanner/manifest-generator.js +5 -1
  103. package/dist/scanner/manifest-generator.js.map +1 -1
  104. package/dist/scanner/types.d.ts +8 -0
  105. package/dist/scanner/types.d.ts.map +1 -1
  106. package/dist/scanner/types.js.map +1 -1
  107. package/dist/schema/ddl/duckdb-strategy.d.ts.map +1 -1
  108. package/dist/schema/ddl/duckdb-strategy.js +1 -0
  109. package/dist/schema/ddl/duckdb-strategy.js.map +1 -1
  110. package/dist/schema/ddl/null-equal-index.d.ts.map +1 -1
  111. package/dist/schema/ddl/null-equal-index.js +4 -2
  112. package/dist/schema/ddl/null-equal-index.js.map +1 -1
  113. package/dist/schema/generator.d.ts +3 -0
  114. package/dist/schema/generator.d.ts.map +1 -1
  115. package/dist/schema/generator.js +38 -0
  116. package/dist/schema/generator.js.map +1 -1
  117. package/dist/smrt-knowledge.json +74 -20
  118. package/dist/sync/apply.d.ts +2 -0
  119. package/dist/sync/apply.d.ts.map +1 -1
  120. package/dist/sync/apply.js +7 -3
  121. package/dist/sync/apply.js.map +1 -1
  122. package/dist/utils/scanner-module.d.ts +9 -0
  123. package/dist/utils/scanner-module.d.ts.map +1 -1
  124. package/dist/vite-plugin/index.d.ts +14 -0
  125. package/dist/vite-plugin/index.d.ts.map +1 -1
  126. package/dist/vite-plugin/index.js +58 -18
  127. package/dist/vite-plugin/index.js.map +1 -1
  128. package/dist/vite-plugin/package-stamp.d.ts +41 -0
  129. package/dist/vite-plugin/package-stamp.d.ts.map +1 -0
  130. package/dist/vite-plugin/package-stamp.js +33 -0
  131. package/dist/vite-plugin/package-stamp.js.map +1 -0
  132. package/dist/vite-plugin/sveltekit-generator.js +5 -3
  133. package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
  134. package/dist/vite-plugin/sync-apply-route.d.ts.map +1 -1
  135. package/dist/vite-plugin/sync-apply-route.js +10 -0
  136. package/dist/vite-plugin/sync-apply-route.js.map +1 -1
  137. package/dist/vite-plugin.js +2 -2
  138. package/package.json +10 -10
@@ -157,6 +157,8 @@ export type SyncApplyAuthzDecision = 'ok' | 'auth_required' | 'forbidden';
157
157
  * and mass-assignment rules as the host's plain CRUD routes.
158
158
  */
159
159
  export interface SyncApplyTarget {
160
+ /** Host-owned transactional mutation seam (including opt-in audit recording). */
161
+ mutate?: (row: SyncApplyRowLike, op: 'update' | 'delete', data: Record<string, unknown>) => Promise<void>;
160
162
  /** Registry/class name, for diagnostics. */
161
163
  objectName: string;
162
164
  /** Collection whose get/create/save/delete run the full interceptor stack. */
@@ -1 +1 @@
1
- {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/sync/apply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAIH;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,4BAA6B,CAAC;AAEpE,8EAA8E;AAC9E,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C;;;GAGG;AACH,eAAO,MAAM,uBAAuB,QAC+B,CAAC;AAEpE,2DAA2D;AAC3D,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAIzD,+BAA+B;AAC/B,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;AAElE,qDAAqD;AACrD,MAAM,MAAM,wBAAwB,GAChC,cAAc,GACd,YAAY,GACZ,iBAAiB,GACjB,gBAAgB,GAChB,gBAAgB,GAChB,eAAe,GACf,WAAW,GACX,WAAW,GACX,aAAa,GACb,cAAc,CAAC;AAEnB,qDAAqD;AACrD,MAAM,MAAM,uBAAuB,GAAG,aAAa,GAAG,iBAAiB,CAAC;AAExE,MAAM,MAAM,eAAe,GACvB,wBAAwB,GACxB,uBAAuB,CAAC;AAE5B;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,WAAW,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,2CAA2C;AAC3C,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,MAAM,EAAE,eAAe,CAAC;IACxB,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,uDAAuD;AACvD,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,mBAAmB,EAAE,CAAC;CAChC;AAED,yCAAyC;AACzC,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE;QACL,IAAI,EAAE,eAAe,GAAG,iBAAiB,CAAC;QAC1C,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,yEAAyE;AACzE,MAAM,MAAM,gBAAgB,GACxB;IAAE,MAAM,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,sBAAsB,CAAA;CAAE,GAC7C;IAAE,MAAM,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,mBAAmB,CAAA;CAAE,CAAC;AAE/C;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,UAAU,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IAClC,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACzB,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAClD,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACrE;AAED,qDAAqD;AACrD,MAAM,MAAM,sBAAsB,GAAG,IAAI,GAAG,eAAe,GAAG,WAAW,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,UAAU,EAAE,uBAAuB,CAAC;IACpC,6EAA6E;IAC7E,WAAW,CAAC,EAAE,EAAE,WAAW,GAAG,OAAO,CAAC;IACtC,gEAAgE;IAChE,SAAS,CACP,EAAE,EAAE,WAAW,GACd,OAAO,CAAC,sBAAsB,CAAC,GAAG,sBAAsB,CAAC;IAC5D;;;;OAIG;IACH,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpE;AAED,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,aAAa,CACX,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,eAAe,GAAG,IAAI,CAAC;CAC7D;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,OAAO,EACb,MAAM,GAAE;IACN,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;CACzC,GACL,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA0BzB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,OAAO,GACZ;IAAE,KAAK,EAAE,OAAO,EAAE,CAAA;CAAE,GAAG,mBAAmB,CA0B5C;AAED,UAAU,aAAa;IACrB,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,aAAa,CAAC;CACrB;AAED,UAAU,WAAW;IACnB,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,mBAAmB,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,OAAO,GACX,aAAa,GAAG,WAAW,CAgE7B;AAcD;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,eAAe,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAChD,OAAO,CAMT;AAqCD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAOT;AA+KD;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,OAAO,EACb,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,gBAAgB,CAAC,CAa3B"}
1
+ {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/sync/apply.ts"],"names":[],"mappings":"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"}
@@ -299,8 +299,11 @@ async function applyValidatedItem(item, target) {
299
299
  reason: "stale_write",
300
300
  updatedAt: rowUpdatedAtIso(row)
301
301
  };
302
- Object.assign(row, data);
303
- await row.save();
302
+ if (target.mutate) await target.mutate(row, "update", data);
303
+ else {
304
+ Object.assign(row, data);
305
+ await row.save();
306
+ }
304
307
  return {
305
308
  ...base,
306
309
  status: "applied",
@@ -317,7 +320,8 @@ async function applyValidatedItem(item, target) {
317
320
  reason: "stale_write",
318
321
  updatedAt: rowUpdatedAtIso(row)
319
322
  };
320
- await row.delete();
323
+ if (target.mutate) await target.mutate(row, "delete", data);
324
+ else await row.delete();
321
325
  return {
322
326
  ...base,
323
327
  status: "applied"
@@ -1 +1 @@
1
- {"version":3,"file":"apply.js","names":[],"sources":["../../src/sync/apply.ts"],"sourcesContent":["/**\n * Idempotent sync-apply batch processing (#1759).\n *\n * The single shared write contract for the web offline outbox (#1762) and the\n * KMP mobile write queue (#1739): an ordered batch of client-authored\n * mutations carrying client-generated row UUIDs, applied FIFO through the\n * host's full stack (authentication, tenant isolation, interceptors, writable\n * policy), returning per-item results (`applied` / `conflict` / `rejected`).\n *\n * This module is transport-agnostic: the runtime REST generator\n * (`generators/rest.ts`) and the generated SvelteKit route\n * (`vite-plugin/sync-apply-route.ts`) both delegate here, supplying a\n * {@link SyncApplyHost} that adapts their own collection resolution,\n * authorization, and writable-policy machinery. Keeping the logic here keeps\n * the two generators byte-minimal and the contract single-sourced.\n *\n * Contract documentation: docs/content/architecture/sync-apply-contract.md\n *\n * ## Why replay is idempotent\n *\n * - Row identity is the client-generated UUID (validated server-side), and\n * ONLY that UUID: creates persist via a strict INSERT\n * (`_insertOnly`, never natural-key dedup-upsert), so a create can never\n * adopt or overwrite a different row, and a re-delivered create finds its\n * own row via the id pre-check instead of duplicating it.\n * - Before writing, each create/update is compared against the current row;\n * when the row already reflects the item's payload the write is skipped and\n * reported `applied` (a no-op re-apply). This is what makes N replays of an\n * identical batch yield byte-identical database state (no `updated_at`\n * churn) AND identical per-item results, without a server-side idempotency\n * ledger.\n * - Deletes of rows that are already gone report `applied` consistently.\n *\n * ## Conflict policy v1 (server-authoritative LWW, updated-at guard)\n *\n * The stale guard applies to mutations that would actually change server\n * state: an update/delete whose `baseUpdatedAt` is older than the server\n * row's `updated_at` AND whose effect is not already present is the LOSING\n * write — it is **skipped** (the newer server state wins) and the item is\n * reported `conflict` with the server's current `updatedAt` so the client can\n * rebase. Two deliberate precedences soften \"stale ⇒ conflict\":\n * no-op detection runs first (a stale update whose payload already matches\n * the row is an idempotent replay of an applied write → `applied`), and a\n * delete of a row that is already gone is a no-op → `applied`. Skipping\n * (rather than clobbering) was chosen because it never destroys newer data,\n * and it is replay-stable: a replayed conflicting item reports `conflict`\n * again against unchanged state. Field-level merge is explicitly out of\n * scope for v1.\n */\n\nimport { isUniqueViolationError } from '../db-errors.js';\n\n/**\n * URL segments of the generated batch apply endpoint, relative to the API\n * base path: `POST {basePath}/sync/apply`. The `sync` segment is reserved —\n * a collection named `sync` cannot expose an item route named `apply`.\n */\nexport const SYNC_APPLY_ROUTE_SEGMENTS = ['sync', 'apply'] as const;\n\n/** Maximum items accepted in one batch; larger batches are rejected (400). */\nexport const MAX_SYNC_APPLY_BATCH_SIZE = 1000;\n\n/**\n * Client-generated row ids must be well-formed UUIDs. Same shape the ORM\n * accepts as an id filter in `SmrtCollection.get()`.\n */\nexport const SYNC_APPLY_UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Mutation kinds accepted by the batch apply endpoint. */\nexport type SyncApplyOp = 'create' | 'update' | 'delete';\n\nconst SYNC_APPLY_OPS: readonly SyncApplyOp[] = ['create', 'update', 'delete'];\n\n/** Per-item outcome status. */\nexport type SyncApplyStatus = 'applied' | 'conflict' | 'rejected';\n\n/** Machine-readable reasons for `rejected` items. */\nexport type SyncApplyRejectionReason =\n | 'invalid_item'\n | 'invalid_id'\n | 'invalid_payload'\n | 'unknown_object'\n | 'op_not_allowed'\n | 'auth_required'\n | 'forbidden'\n | 'not_found'\n | 'id_conflict'\n | 'write_failed';\n\n/** Machine-readable reasons for `conflict` items. */\nexport type SyncApplyConflictReason = 'stale_write' | 'create_conflict';\n\nexport type SyncApplyReason =\n | SyncApplyRejectionReason\n | SyncApplyConflictReason;\n\n/**\n * One client-authored mutation in a batch. See the contract doc for field\n * semantics; briefly:\n *\n * - `itemId` — the client's idempotency/correlation handle (the durable queue\n * row id). Echoed back verbatim; results are also positional.\n * - `object` — the collection route segment the model's CRUD routes use\n * (e.g. `products`).\n * - `id` — the client-generated row UUID. This is the ONLY channel for a\n * client-supplied id; payload `id` fields are stripped by the writable\n * policy exactly as on plain CRUD routes (#1540).\n * - `baseUpdatedAt` — the server `updated_at` the client last saw for this\n * row; drives the conflict guard on update/delete. Omit to opt out.\n */\nexport interface SyncApplyItem {\n itemId: string;\n object: string;\n op: SyncApplyOp;\n id: string;\n payload?: Record<string, unknown>;\n baseUpdatedAt?: string;\n}\n\n/** Request body of `POST …/sync/apply`. */\nexport interface SyncApplyBatchRequest {\n items: SyncApplyItem[];\n}\n\n/**\n * Per-item result. `results[i]` always corresponds to `items[i]` (positional);\n * `itemId` is echoed for convenience (`null` when the incoming item was too\n * malformed to carry one). `updatedAt` is the server row's `updated_at` after\n * processing — present on applied creates/updates and on conflicts (so clients\n * can rebase), absent on deletes and rejections.\n */\nexport interface SyncApplyItemResult {\n itemId: string | null;\n id: string | null;\n status: SyncApplyStatus;\n reason?: SyncApplyReason;\n updatedAt?: string;\n}\n\n/** Response body of `POST …/sync/apply` (HTTP 200). */\nexport interface SyncApplyBatchResponse {\n results: SyncApplyItemResult[];\n}\n\n/** Batch-level error body (HTTP 400). */\nexport interface SyncApplyBatchError {\n error: {\n code: 'invalid_batch' | 'batch_too_large';\n message: string;\n };\n}\n\n/** Outcome of {@link processSyncApplyBatch}: HTTP status + JSON body. */\nexport type SyncApplyOutcome =\n | { status: 200; body: SyncApplyBatchResponse }\n | { status: 400; body: SyncApplyBatchError };\n\n/**\n * Minimal structural view of a persisted row as the processor needs it.\n * Matches `SmrtObject` structurally; kept structural so hosts and tests can\n * exercise the processor without importing ORM classes.\n */\nexport interface SyncApplyRowLike {\n id?: string | null;\n updated_at?: Date | string | null;\n save(): Promise<unknown>;\n delete(): Promise<unknown>;\n}\n\n/**\n * Minimal structural view of a collection (matches `SmrtCollection`).\n * `create()` must honor the `_insertOnly` option (strict INSERT — see\n * `SmrtCreateInput`), which the processor sets on every sync create.\n */\nexport interface SyncApplyCollectionLike {\n get(id: string): Promise<SyncApplyRowLike | null>;\n create(options: Record<string, unknown>): Promise<SyncApplyRowLike>;\n}\n\n/** Per-item authorization decision from the host. */\nexport type SyncApplyAuthzDecision = 'ok' | 'auth_required' | 'forbidden';\n\n/**\n * A resolved mutation target: everything the processor needs to apply one\n * item through the host's full stack. Hosts build these from their existing\n * machinery so sync items follow the exact same authorization, action-gating,\n * and mass-assignment rules as the host's plain CRUD routes.\n */\nexport interface SyncApplyTarget {\n /** Registry/class name, for diagnostics. */\n objectName: string;\n /** Collection whose get/create/save/delete run the full interceptor stack. */\n collection: SyncApplyCollectionLike;\n /** Whether the object's API config exposes this op (mirrors CRUD gating). */\n isOpAllowed(op: SyncApplyOp): boolean;\n /** Per-item authorization (mutating semantics, fail-closed). */\n authorize(\n op: SyncApplyOp,\n ): Promise<SyncApplyAuthzDecision> | SyncApplyAuthzDecision;\n /**\n * Mass-assignment guard (#1540): strip server-managed/readonly/disallowed\n * fields from the item payload. The row UUID is injected from the validated\n * envelope `id` AFTER this runs — never from the body.\n */\n prepare(payload: Record<string, unknown>): Record<string, unknown>;\n}\n\n/** Host adapter handed to {@link processSyncApplyBatch}. */\nexport interface SyncApplyHost {\n /**\n * Resolve an item's `object` segment to a target, or `null` when unknown.\n * Called once per item so per-item auth context stays accurate.\n */\n resolveTarget(\n objectSegment: string,\n ): Promise<SyncApplyTarget | null> | SyncApplyTarget | null;\n}\n\n/**\n * Writable-policy helper shared with the generated SvelteKit sync route: the\n * same stripping rules as the generated CRUD handlers (#1540) — server-managed\n * fields, `_`-prefixed keys, `@field({ readonly: true })` fields, and (when\n * configured) intersection with the `api.writable` allowlist.\n */\nexport function applySyncWritablePolicy(\n data: unknown,\n policy: {\n readonlyFields?: readonly string[];\n writableAllowlist?: readonly string[] | null;\n } = {},\n): Record<string, unknown> {\n if (!data || typeof data !== 'object') {\n return {};\n }\n\n const serverManaged = new Set([\n 'id',\n 'tenantId',\n 'tenant_id',\n 'createdAt',\n 'created_at',\n 'updatedAt',\n 'updated_at',\n ]);\n const readonly = new Set(policy.readonlyFields ?? []);\n const writable = policy.writableAllowlist ?? null;\n\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n if (key.startsWith('_')) continue;\n if (serverManaged.has(key)) continue;\n if (readonly.has(key)) continue;\n if (writable && !writable.includes(key)) continue;\n result[key] = value;\n }\n return result;\n}\n\n/**\n * Batch-level validation. Returns the raw items array or a 400-shaped error.\n */\nexport function parseSyncApplyBatch(\n body: unknown,\n): { items: unknown[] } | SyncApplyBatchError {\n if (\n !body ||\n typeof body !== 'object' ||\n Array.isArray(body) ||\n !Array.isArray((body as { items?: unknown }).items)\n ) {\n return {\n error: {\n code: 'invalid_batch',\n message: 'Request body must be an object with an \"items\" array',\n },\n };\n }\n\n const items = (body as { items: unknown[] }).items;\n if (items.length > MAX_SYNC_APPLY_BATCH_SIZE) {\n return {\n error: {\n code: 'batch_too_large',\n message: `Batch exceeds the maximum of ${MAX_SYNC_APPLY_BATCH_SIZE} items`,\n },\n };\n }\n\n return { items };\n}\n\ninterface ValidatedItem {\n ok: true;\n item: SyncApplyItem;\n}\n\ninterface InvalidItem {\n ok: false;\n result: SyncApplyItemResult;\n}\n\n/**\n * Per-item shape validation. Never throws; malformed items become `rejected`\n * results so one bad item cannot fail the batch.\n */\nexport function validateSyncApplyItem(\n raw: unknown,\n): ValidatedItem | InvalidItem {\n const record =\n raw && typeof raw === 'object' && !Array.isArray(raw)\n ? (raw as Record<string, unknown>)\n : null;\n\n const itemId =\n record && typeof record.itemId === 'string' && record.itemId.length > 0\n ? record.itemId\n : null;\n const id = record && typeof record.id === 'string' ? record.id : null;\n\n const reject = (reason: SyncApplyRejectionReason): InvalidItem => ({\n ok: false,\n result: { itemId, id, status: 'rejected', reason },\n });\n\n if (!record || !itemId) {\n return reject('invalid_item');\n }\n\n const op = record.op;\n if (typeof op !== 'string' || !SYNC_APPLY_OPS.includes(op as SyncApplyOp)) {\n return reject('invalid_item');\n }\n\n const object = record.object;\n if (typeof object !== 'string' || object.length === 0) {\n return reject('invalid_item');\n }\n\n const baseUpdatedAt = record.baseUpdatedAt;\n if (baseUpdatedAt !== undefined) {\n if (\n typeof baseUpdatedAt !== 'string' ||\n Number.isNaN(Date.parse(baseUpdatedAt))\n ) {\n return reject('invalid_item');\n }\n }\n\n // Client UUID validation: the row id must be a well-formed UUID.\n if (!id || !SYNC_APPLY_UUID_PATTERN.test(id)) {\n return reject('invalid_id');\n }\n\n const payload = record.payload;\n if (op === 'create' || op === 'update') {\n if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {\n return reject('invalid_payload');\n }\n }\n\n return {\n ok: true,\n item: {\n itemId,\n object,\n op: op as SyncApplyOp,\n id,\n payload: payload as Record<string, unknown> | undefined,\n baseUpdatedAt: baseUpdatedAt as string | undefined,\n },\n };\n}\n\nfunction toEpochMillis(value: Date | string | null | undefined): number | null {\n if (value instanceof Date) {\n const time = value.getTime();\n return Number.isNaN(time) ? null : time;\n }\n if (typeof value === 'string') {\n const time = Date.parse(value);\n return Number.isNaN(time) ? null : time;\n }\n return null;\n}\n\n/**\n * Updated-at conflict guard: true when the client's base is strictly older\n * than the server row's `updated_at`. No base (client opted out) or no\n * comparable server timestamp → not stale.\n */\nexport function isStaleWrite(\n baseUpdatedAt: string | undefined,\n serverUpdatedAt: Date | string | null | undefined,\n): boolean {\n const base = toEpochMillis(baseUpdatedAt ?? null);\n if (base === null) return false;\n const server = toEpochMillis(serverUpdatedAt);\n if (server === null) return false;\n return base < server;\n}\n\nfunction normalizeForComparison(value: unknown): unknown {\n if (value instanceof Date) return value.toISOString();\n return value;\n}\n\nfunction valuesEquivalent(rowValue: unknown, payloadValue: unknown): boolean {\n // Datetime fields hydrate as Date; clients send ISO strings (or epoch ms).\n if (rowValue instanceof Date) {\n const candidate =\n typeof payloadValue === 'string' || typeof payloadValue === 'number'\n ? new Date(payloadValue).getTime()\n : payloadValue instanceof Date\n ? payloadValue.getTime()\n : Number.NaN;\n return !Number.isNaN(candidate) && candidate === rowValue.getTime();\n }\n\n if (rowValue === null || rowValue === undefined) {\n return payloadValue === null || payloadValue === undefined;\n }\n\n if (typeof rowValue !== 'object' && typeof payloadValue !== 'object') {\n return rowValue === payloadValue;\n }\n\n try {\n return (\n JSON.stringify(normalizeForComparison(rowValue)) ===\n JSON.stringify(normalizeForComparison(payloadValue))\n );\n } catch {\n return false;\n }\n}\n\n/**\n * No-op detection: true when the row already reflects every (policy-stripped)\n * payload field. Payload keys that are not properties of the hydrated row are\n * ignored — they would never persist, so they must not defeat idempotent\n * replay. This check is what keeps replayed batches from churning\n * `updated_at` (and keeps their per-item results identical).\n */\nexport function payloadMatchesRow(\n payload: Record<string, unknown>,\n row: SyncApplyRowLike,\n): boolean {\n const record = row as unknown as Record<string, unknown>;\n for (const [key, value] of Object.entries(payload)) {\n if (!(key in record)) continue;\n if (!valuesEquivalent(record[key], value)) return false;\n }\n return true;\n}\n\nfunction rowUpdatedAtIso(row: SyncApplyRowLike): string | undefined {\n const value = row.updated_at;\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'string') {\n const epoch = Date.parse(value);\n return Number.isNaN(epoch) ? value : new Date(epoch).toISOString();\n }\n return undefined;\n}\n\n/**\n * Detect a unique/primary-key constraint failure anywhere in an error's cause\n * chain.\n *\n * Reduced to the framework contract in #2366: `save()` now classifies through\n * the driver-error cause chain, so this reports `true` for both the typed\n * `ValidationError` it raises and any still-wrapped adapter error that reaches\n * here by another path. The private chain walk and duplicated dialect regexes\n * this replaced are gone — `classifyDatabaseError()` is the single matcher.\n */\nfunction isUniqueConstraintError(error: unknown): boolean {\n return isUniqueViolationError(error);\n}\n\nasync function applyValidatedItem(\n item: SyncApplyItem,\n target: SyncApplyTarget,\n): Promise<SyncApplyItemResult> {\n const base = { itemId: item.itemId, id: item.id } as const;\n\n // Authorization first (mirrors CRUD: the auth middleware runs before the\n // per-action 405 gate), then action gating.\n const decision = await target.authorize(item.op);\n if (decision !== 'ok') {\n return { ...base, status: 'rejected', reason: decision };\n }\n\n if (!target.isOpAllowed(item.op)) {\n return { ...base, status: 'rejected', reason: 'op_not_allowed' };\n }\n\n // Payload passes through the host's writable policy; the row UUID comes\n // from the validated envelope `id`, never from the body — the #1540\n // mass-assignment guard stays intact on this path too.\n const data = item.op === 'delete' ? {} : target.prepare(item.payload ?? {});\n\n // Tenant isolation: collection.get runs the interceptor stack, so rows\n // outside the caller's tenant are simply not visible here.\n const row = await target.collection.get(item.id);\n\n switch (item.op) {\n case 'create': {\n if (row) {\n if (payloadMatchesRow(data, row)) {\n // Idempotent replay of an already-applied create.\n return {\n ...base,\n status: 'applied',\n updatedAt: rowUpdatedAtIso(row),\n };\n }\n // The id exists with different content — the server state wins (LWW\n // skip) and the client is told to rebase.\n return {\n ...base,\n status: 'conflict',\n reason: 'create_conflict',\n updatedAt: rowUpdatedAtIso(row),\n };\n }\n\n try {\n // Strict insert (`_insertOnly`): row identity is the envelope UUID\n // alone. Without it, `create()` saves new objects with natural-key\n // conflict resolution (slug/context or configured `conflictColumns`,\n // #1472's ingestion dedup), so a fresh-UUID create whose payload\n // derives an existing row's natural key would silently adopt and\n // rewrite that row — violating this contract.\n const created = await target.collection.create({\n ...data,\n id: item.id,\n _insertOnly: true,\n });\n return {\n ...base,\n status: 'applied',\n updatedAt: rowUpdatedAtIso(created),\n };\n } catch (error) {\n // A unique-constraint failure here means the id (or natural key)\n // belongs to a row this caller cannot see (e.g. another tenant's) or\n // a concurrent writer won the race. Never clobber it.\n if (isUniqueConstraintError(error)) {\n return { ...base, status: 'rejected', reason: 'id_conflict' };\n }\n throw error;\n }\n }\n\n case 'update': {\n if (!row) {\n return { ...base, status: 'rejected', reason: 'not_found' };\n }\n if (payloadMatchesRow(data, row)) {\n // Idempotent replay of an already-applied update (checked before the\n // stale guard: our own earlier apply advanced `updated_at`).\n return { ...base, status: 'applied', updatedAt: rowUpdatedAtIso(row) };\n }\n if (isStaleWrite(item.baseUpdatedAt, row.updated_at)) {\n return {\n ...base,\n status: 'conflict',\n reason: 'stale_write',\n updatedAt: rowUpdatedAtIso(row),\n };\n }\n Object.assign(row, data);\n await row.save();\n return { ...base, status: 'applied', updatedAt: rowUpdatedAtIso(row) };\n }\n\n case 'delete': {\n if (!row) {\n // Idempotent delete: already gone (or never visible) — reported\n // consistently on every replay.\n return { ...base, status: 'applied' };\n }\n if (isStaleWrite(item.baseUpdatedAt, row.updated_at)) {\n return {\n ...base,\n status: 'conflict',\n reason: 'stale_write',\n updatedAt: rowUpdatedAtIso(row),\n };\n }\n await row.delete();\n return { ...base, status: 'applied' };\n }\n }\n}\n\nasync function processOneItem(\n raw: unknown,\n host: SyncApplyHost,\n): Promise<SyncApplyItemResult> {\n const validated = validateSyncApplyItem(raw);\n if (!validated.ok) {\n return validated.result;\n }\n\n const item = validated.item;\n try {\n const target = await host.resolveTarget(item.object);\n if (!target) {\n return {\n itemId: item.itemId,\n id: item.id,\n status: 'rejected',\n reason: 'unknown_object',\n };\n }\n return await applyValidatedItem(item, target);\n } catch {\n // One item's failure must never fail the batch.\n return {\n itemId: item.itemId,\n id: item.id,\n status: 'rejected',\n reason: 'write_failed',\n };\n }\n}\n\n/**\n * Process a sync-apply batch: validate the envelope, then apply items FIFO\n * (strictly in order, awaiting each — items may depend on earlier items in\n * the same batch). Always resolves; item-level failures surface as `rejected`\n * results and only envelope-level problems produce a 400.\n */\nexport async function processSyncApplyBatch(\n body: unknown,\n host: SyncApplyHost,\n): Promise<SyncApplyOutcome> {\n const parsed = parseSyncApplyBatch(body);\n if ('error' in parsed) {\n return { status: 400, body: parsed };\n }\n\n const results: SyncApplyItemResult[] = [];\n for (const raw of parsed.items) {\n // Sequential on purpose: FIFO within the batch is part of the contract.\n results.push(await processOneItem(raw, host));\n }\n\n return { status: 200, body: { results } };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"apply.js","names":[],"sources":["../../src/sync/apply.ts"],"sourcesContent":["/**\n * Idempotent sync-apply batch processing (#1759).\n *\n * The single shared write contract for the web offline outbox (#1762) and the\n * KMP mobile write queue (#1739): an ordered batch of client-authored\n * mutations carrying client-generated row UUIDs, applied FIFO through the\n * host's full stack (authentication, tenant isolation, interceptors, writable\n * policy), returning per-item results (`applied` / `conflict` / `rejected`).\n *\n * This module is transport-agnostic: the runtime REST generator\n * (`generators/rest.ts`) and the generated SvelteKit route\n * (`vite-plugin/sync-apply-route.ts`) both delegate here, supplying a\n * {@link SyncApplyHost} that adapts their own collection resolution,\n * authorization, and writable-policy machinery. Keeping the logic here keeps\n * the two generators byte-minimal and the contract single-sourced.\n *\n * Contract documentation: docs/content/architecture/sync-apply-contract.md\n *\n * ## Why replay is idempotent\n *\n * - Row identity is the client-generated UUID (validated server-side), and\n * ONLY that UUID: creates persist via a strict INSERT\n * (`_insertOnly`, never natural-key dedup-upsert), so a create can never\n * adopt or overwrite a different row, and a re-delivered create finds its\n * own row via the id pre-check instead of duplicating it.\n * - Before writing, each create/update is compared against the current row;\n * when the row already reflects the item's payload the write is skipped and\n * reported `applied` (a no-op re-apply). This is what makes N replays of an\n * identical batch yield byte-identical database state (no `updated_at`\n * churn) AND identical per-item results, without a server-side idempotency\n * ledger.\n * - Deletes of rows that are already gone report `applied` consistently.\n *\n * ## Conflict policy v1 (server-authoritative LWW, updated-at guard)\n *\n * The stale guard applies to mutations that would actually change server\n * state: an update/delete whose `baseUpdatedAt` is older than the server\n * row's `updated_at` AND whose effect is not already present is the LOSING\n * write — it is **skipped** (the newer server state wins) and the item is\n * reported `conflict` with the server's current `updatedAt` so the client can\n * rebase. Two deliberate precedences soften \"stale ⇒ conflict\":\n * no-op detection runs first (a stale update whose payload already matches\n * the row is an idempotent replay of an applied write → `applied`), and a\n * delete of a row that is already gone is a no-op → `applied`. Skipping\n * (rather than clobbering) was chosen because it never destroys newer data,\n * and it is replay-stable: a replayed conflicting item reports `conflict`\n * again against unchanged state. Field-level merge is explicitly out of\n * scope for v1.\n */\n\nimport { isUniqueViolationError } from '../db-errors.js';\n\n/**\n * URL segments of the generated batch apply endpoint, relative to the API\n * base path: `POST {basePath}/sync/apply`. The `sync` segment is reserved —\n * a collection named `sync` cannot expose an item route named `apply`.\n */\nexport const SYNC_APPLY_ROUTE_SEGMENTS = ['sync', 'apply'] as const;\n\n/** Maximum items accepted in one batch; larger batches are rejected (400). */\nexport const MAX_SYNC_APPLY_BATCH_SIZE = 1000;\n\n/**\n * Client-generated row ids must be well-formed UUIDs. Same shape the ORM\n * accepts as an id filter in `SmrtCollection.get()`.\n */\nexport const SYNC_APPLY_UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Mutation kinds accepted by the batch apply endpoint. */\nexport type SyncApplyOp = 'create' | 'update' | 'delete';\n\nconst SYNC_APPLY_OPS: readonly SyncApplyOp[] = ['create', 'update', 'delete'];\n\n/** Per-item outcome status. */\nexport type SyncApplyStatus = 'applied' | 'conflict' | 'rejected';\n\n/** Machine-readable reasons for `rejected` items. */\nexport type SyncApplyRejectionReason =\n | 'invalid_item'\n | 'invalid_id'\n | 'invalid_payload'\n | 'unknown_object'\n | 'op_not_allowed'\n | 'auth_required'\n | 'forbidden'\n | 'not_found'\n | 'id_conflict'\n | 'write_failed';\n\n/** Machine-readable reasons for `conflict` items. */\nexport type SyncApplyConflictReason = 'stale_write' | 'create_conflict';\n\nexport type SyncApplyReason =\n | SyncApplyRejectionReason\n | SyncApplyConflictReason;\n\n/**\n * One client-authored mutation in a batch. See the contract doc for field\n * semantics; briefly:\n *\n * - `itemId` — the client's idempotency/correlation handle (the durable queue\n * row id). Echoed back verbatim; results are also positional.\n * - `object` — the collection route segment the model's CRUD routes use\n * (e.g. `products`).\n * - `id` — the client-generated row UUID. This is the ONLY channel for a\n * client-supplied id; payload `id` fields are stripped by the writable\n * policy exactly as on plain CRUD routes (#1540).\n * - `baseUpdatedAt` — the server `updated_at` the client last saw for this\n * row; drives the conflict guard on update/delete. Omit to opt out.\n */\nexport interface SyncApplyItem {\n itemId: string;\n object: string;\n op: SyncApplyOp;\n id: string;\n payload?: Record<string, unknown>;\n baseUpdatedAt?: string;\n}\n\n/** Request body of `POST …/sync/apply`. */\nexport interface SyncApplyBatchRequest {\n items: SyncApplyItem[];\n}\n\n/**\n * Per-item result. `results[i]` always corresponds to `items[i]` (positional);\n * `itemId` is echoed for convenience (`null` when the incoming item was too\n * malformed to carry one). `updatedAt` is the server row's `updated_at` after\n * processing — present on applied creates/updates and on conflicts (so clients\n * can rebase), absent on deletes and rejections.\n */\nexport interface SyncApplyItemResult {\n itemId: string | null;\n id: string | null;\n status: SyncApplyStatus;\n reason?: SyncApplyReason;\n updatedAt?: string;\n}\n\n/** Response body of `POST …/sync/apply` (HTTP 200). */\nexport interface SyncApplyBatchResponse {\n results: SyncApplyItemResult[];\n}\n\n/** Batch-level error body (HTTP 400). */\nexport interface SyncApplyBatchError {\n error: {\n code: 'invalid_batch' | 'batch_too_large';\n message: string;\n };\n}\n\n/** Outcome of {@link processSyncApplyBatch}: HTTP status + JSON body. */\nexport type SyncApplyOutcome =\n | { status: 200; body: SyncApplyBatchResponse }\n | { status: 400; body: SyncApplyBatchError };\n\n/**\n * Minimal structural view of a persisted row as the processor needs it.\n * Matches `SmrtObject` structurally; kept structural so hosts and tests can\n * exercise the processor without importing ORM classes.\n */\nexport interface SyncApplyRowLike {\n id?: string | null;\n updated_at?: Date | string | null;\n save(): Promise<unknown>;\n delete(): Promise<unknown>;\n}\n\n/**\n * Minimal structural view of a collection (matches `SmrtCollection`).\n * `create()` must honor the `_insertOnly` option (strict INSERT — see\n * `SmrtCreateInput`), which the processor sets on every sync create.\n */\nexport interface SyncApplyCollectionLike {\n get(id: string): Promise<SyncApplyRowLike | null>;\n create(options: Record<string, unknown>): Promise<SyncApplyRowLike>;\n}\n\n/** Per-item authorization decision from the host. */\nexport type SyncApplyAuthzDecision = 'ok' | 'auth_required' | 'forbidden';\n\n/**\n * A resolved mutation target: everything the processor needs to apply one\n * item through the host's full stack. Hosts build these from their existing\n * machinery so sync items follow the exact same authorization, action-gating,\n * and mass-assignment rules as the host's plain CRUD routes.\n */\nexport interface SyncApplyTarget {\n /** Host-owned transactional mutation seam (including opt-in audit recording). */\n mutate?: (\n row: SyncApplyRowLike,\n op: 'update' | 'delete',\n data: Record<string, unknown>,\n ) => Promise<void>;\n /** Registry/class name, for diagnostics. */\n objectName: string;\n /** Collection whose get/create/save/delete run the full interceptor stack. */\n collection: SyncApplyCollectionLike;\n /** Whether the object's API config exposes this op (mirrors CRUD gating). */\n isOpAllowed(op: SyncApplyOp): boolean;\n /** Per-item authorization (mutating semantics, fail-closed). */\n authorize(\n op: SyncApplyOp,\n ): Promise<SyncApplyAuthzDecision> | SyncApplyAuthzDecision;\n /**\n * Mass-assignment guard (#1540): strip server-managed/readonly/disallowed\n * fields from the item payload. The row UUID is injected from the validated\n * envelope `id` AFTER this runs — never from the body.\n */\n prepare(payload: Record<string, unknown>): Record<string, unknown>;\n}\n\n/** Host adapter handed to {@link processSyncApplyBatch}. */\nexport interface SyncApplyHost {\n /**\n * Resolve an item's `object` segment to a target, or `null` when unknown.\n * Called once per item so per-item auth context stays accurate.\n */\n resolveTarget(\n objectSegment: string,\n ): Promise<SyncApplyTarget | null> | SyncApplyTarget | null;\n}\n\n/**\n * Writable-policy helper shared with the generated SvelteKit sync route: the\n * same stripping rules as the generated CRUD handlers (#1540) — server-managed\n * fields, `_`-prefixed keys, `@field({ readonly: true })` fields, and (when\n * configured) intersection with the `api.writable` allowlist.\n */\nexport function applySyncWritablePolicy(\n data: unknown,\n policy: {\n readonlyFields?: readonly string[];\n writableAllowlist?: readonly string[] | null;\n } = {},\n): Record<string, unknown> {\n if (!data || typeof data !== 'object') {\n return {};\n }\n\n const serverManaged = new Set([\n 'id',\n 'tenantId',\n 'tenant_id',\n 'createdAt',\n 'created_at',\n 'updatedAt',\n 'updated_at',\n ]);\n const readonly = new Set(policy.readonlyFields ?? []);\n const writable = policy.writableAllowlist ?? null;\n\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n if (key.startsWith('_')) continue;\n if (serverManaged.has(key)) continue;\n if (readonly.has(key)) continue;\n if (writable && !writable.includes(key)) continue;\n result[key] = value;\n }\n return result;\n}\n\n/**\n * Batch-level validation. Returns the raw items array or a 400-shaped error.\n */\nexport function parseSyncApplyBatch(\n body: unknown,\n): { items: unknown[] } | SyncApplyBatchError {\n if (\n !body ||\n typeof body !== 'object' ||\n Array.isArray(body) ||\n !Array.isArray((body as { items?: unknown }).items)\n ) {\n return {\n error: {\n code: 'invalid_batch',\n message: 'Request body must be an object with an \"items\" array',\n },\n };\n }\n\n const items = (body as { items: unknown[] }).items;\n if (items.length > MAX_SYNC_APPLY_BATCH_SIZE) {\n return {\n error: {\n code: 'batch_too_large',\n message: `Batch exceeds the maximum of ${MAX_SYNC_APPLY_BATCH_SIZE} items`,\n },\n };\n }\n\n return { items };\n}\n\ninterface ValidatedItem {\n ok: true;\n item: SyncApplyItem;\n}\n\ninterface InvalidItem {\n ok: false;\n result: SyncApplyItemResult;\n}\n\n/**\n * Per-item shape validation. Never throws; malformed items become `rejected`\n * results so one bad item cannot fail the batch.\n */\nexport function validateSyncApplyItem(\n raw: unknown,\n): ValidatedItem | InvalidItem {\n const record =\n raw && typeof raw === 'object' && !Array.isArray(raw)\n ? (raw as Record<string, unknown>)\n : null;\n\n const itemId =\n record && typeof record.itemId === 'string' && record.itemId.length > 0\n ? record.itemId\n : null;\n const id = record && typeof record.id === 'string' ? record.id : null;\n\n const reject = (reason: SyncApplyRejectionReason): InvalidItem => ({\n ok: false,\n result: { itemId, id, status: 'rejected', reason },\n });\n\n if (!record || !itemId) {\n return reject('invalid_item');\n }\n\n const op = record.op;\n if (typeof op !== 'string' || !SYNC_APPLY_OPS.includes(op as SyncApplyOp)) {\n return reject('invalid_item');\n }\n\n const object = record.object;\n if (typeof object !== 'string' || object.length === 0) {\n return reject('invalid_item');\n }\n\n const baseUpdatedAt = record.baseUpdatedAt;\n if (baseUpdatedAt !== undefined) {\n if (\n typeof baseUpdatedAt !== 'string' ||\n Number.isNaN(Date.parse(baseUpdatedAt))\n ) {\n return reject('invalid_item');\n }\n }\n\n // Client UUID validation: the row id must be a well-formed UUID.\n if (!id || !SYNC_APPLY_UUID_PATTERN.test(id)) {\n return reject('invalid_id');\n }\n\n const payload = record.payload;\n if (op === 'create' || op === 'update') {\n if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {\n return reject('invalid_payload');\n }\n }\n\n return {\n ok: true,\n item: {\n itemId,\n object,\n op: op as SyncApplyOp,\n id,\n payload: payload as Record<string, unknown> | undefined,\n baseUpdatedAt: baseUpdatedAt as string | undefined,\n },\n };\n}\n\nfunction toEpochMillis(value: Date | string | null | undefined): number | null {\n if (value instanceof Date) {\n const time = value.getTime();\n return Number.isNaN(time) ? null : time;\n }\n if (typeof value === 'string') {\n const time = Date.parse(value);\n return Number.isNaN(time) ? null : time;\n }\n return null;\n}\n\n/**\n * Updated-at conflict guard: true when the client's base is strictly older\n * than the server row's `updated_at`. No base (client opted out) or no\n * comparable server timestamp → not stale.\n */\nexport function isStaleWrite(\n baseUpdatedAt: string | undefined,\n serverUpdatedAt: Date | string | null | undefined,\n): boolean {\n const base = toEpochMillis(baseUpdatedAt ?? null);\n if (base === null) return false;\n const server = toEpochMillis(serverUpdatedAt);\n if (server === null) return false;\n return base < server;\n}\n\nfunction normalizeForComparison(value: unknown): unknown {\n if (value instanceof Date) return value.toISOString();\n return value;\n}\n\nfunction valuesEquivalent(rowValue: unknown, payloadValue: unknown): boolean {\n // Datetime fields hydrate as Date; clients send ISO strings (or epoch ms).\n if (rowValue instanceof Date) {\n const candidate =\n typeof payloadValue === 'string' || typeof payloadValue === 'number'\n ? new Date(payloadValue).getTime()\n : payloadValue instanceof Date\n ? payloadValue.getTime()\n : Number.NaN;\n return !Number.isNaN(candidate) && candidate === rowValue.getTime();\n }\n\n if (rowValue === null || rowValue === undefined) {\n return payloadValue === null || payloadValue === undefined;\n }\n\n if (typeof rowValue !== 'object' && typeof payloadValue !== 'object') {\n return rowValue === payloadValue;\n }\n\n try {\n return (\n JSON.stringify(normalizeForComparison(rowValue)) ===\n JSON.stringify(normalizeForComparison(payloadValue))\n );\n } catch {\n return false;\n }\n}\n\n/**\n * No-op detection: true when the row already reflects every (policy-stripped)\n * payload field. Payload keys that are not properties of the hydrated row are\n * ignored — they would never persist, so they must not defeat idempotent\n * replay. This check is what keeps replayed batches from churning\n * `updated_at` (and keeps their per-item results identical).\n */\nexport function payloadMatchesRow(\n payload: Record<string, unknown>,\n row: SyncApplyRowLike,\n): boolean {\n const record = row as unknown as Record<string, unknown>;\n for (const [key, value] of Object.entries(payload)) {\n if (!(key in record)) continue;\n if (!valuesEquivalent(record[key], value)) return false;\n }\n return true;\n}\n\nfunction rowUpdatedAtIso(row: SyncApplyRowLike): string | undefined {\n const value = row.updated_at;\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'string') {\n const epoch = Date.parse(value);\n return Number.isNaN(epoch) ? value : new Date(epoch).toISOString();\n }\n return undefined;\n}\n\n/**\n * Detect a unique/primary-key constraint failure anywhere in an error's cause\n * chain.\n *\n * Reduced to the framework contract in #2366: `save()` now classifies through\n * the driver-error cause chain, so this reports `true` for both the typed\n * `ValidationError` it raises and any still-wrapped adapter error that reaches\n * here by another path. The private chain walk and duplicated dialect regexes\n * this replaced are gone — `classifyDatabaseError()` is the single matcher.\n */\nfunction isUniqueConstraintError(error: unknown): boolean {\n return isUniqueViolationError(error);\n}\n\nasync function applyValidatedItem(\n item: SyncApplyItem,\n target: SyncApplyTarget,\n): Promise<SyncApplyItemResult> {\n const base = { itemId: item.itemId, id: item.id } as const;\n\n // Authorization first (mirrors CRUD: the auth middleware runs before the\n // per-action 405 gate), then action gating.\n const decision = await target.authorize(item.op);\n if (decision !== 'ok') {\n return { ...base, status: 'rejected', reason: decision };\n }\n\n if (!target.isOpAllowed(item.op)) {\n return { ...base, status: 'rejected', reason: 'op_not_allowed' };\n }\n\n // Payload passes through the host's writable policy; the row UUID comes\n // from the validated envelope `id`, never from the body — the #1540\n // mass-assignment guard stays intact on this path too.\n const data = item.op === 'delete' ? {} : target.prepare(item.payload ?? {});\n\n // Tenant isolation: collection.get runs the interceptor stack, so rows\n // outside the caller's tenant are simply not visible here.\n const row = await target.collection.get(item.id);\n\n switch (item.op) {\n case 'create': {\n if (row) {\n if (payloadMatchesRow(data, row)) {\n // Idempotent replay of an already-applied create.\n return {\n ...base,\n status: 'applied',\n updatedAt: rowUpdatedAtIso(row),\n };\n }\n // The id exists with different content — the server state wins (LWW\n // skip) and the client is told to rebase.\n return {\n ...base,\n status: 'conflict',\n reason: 'create_conflict',\n updatedAt: rowUpdatedAtIso(row),\n };\n }\n\n try {\n // Strict insert (`_insertOnly`): row identity is the envelope UUID\n // alone. Without it, `create()` saves new objects with natural-key\n // conflict resolution (slug/context or configured `conflictColumns`,\n // #1472's ingestion dedup), so a fresh-UUID create whose payload\n // derives an existing row's natural key would silently adopt and\n // rewrite that row — violating this contract.\n const created = await target.collection.create({\n ...data,\n id: item.id,\n _insertOnly: true,\n });\n return {\n ...base,\n status: 'applied',\n updatedAt: rowUpdatedAtIso(created),\n };\n } catch (error) {\n // A unique-constraint failure here means the id (or natural key)\n // belongs to a row this caller cannot see (e.g. another tenant's) or\n // a concurrent writer won the race. Never clobber it.\n if (isUniqueConstraintError(error)) {\n return { ...base, status: 'rejected', reason: 'id_conflict' };\n }\n throw error;\n }\n }\n\n case 'update': {\n if (!row) {\n return { ...base, status: 'rejected', reason: 'not_found' };\n }\n if (payloadMatchesRow(data, row)) {\n // Idempotent replay of an already-applied update (checked before the\n // stale guard: our own earlier apply advanced `updated_at`).\n return { ...base, status: 'applied', updatedAt: rowUpdatedAtIso(row) };\n }\n if (isStaleWrite(item.baseUpdatedAt, row.updated_at)) {\n return {\n ...base,\n status: 'conflict',\n reason: 'stale_write',\n updatedAt: rowUpdatedAtIso(row),\n };\n }\n if (target.mutate) await target.mutate(row, 'update', data);\n else {\n Object.assign(row, data);\n await row.save();\n }\n return { ...base, status: 'applied', updatedAt: rowUpdatedAtIso(row) };\n }\n\n case 'delete': {\n if (!row) {\n // Idempotent delete: already gone (or never visible) — reported\n // consistently on every replay.\n return { ...base, status: 'applied' };\n }\n if (isStaleWrite(item.baseUpdatedAt, row.updated_at)) {\n return {\n ...base,\n status: 'conflict',\n reason: 'stale_write',\n updatedAt: rowUpdatedAtIso(row),\n };\n }\n if (target.mutate) await target.mutate(row, 'delete', data);\n else await row.delete();\n return { ...base, status: 'applied' };\n }\n }\n}\n\nasync function processOneItem(\n raw: unknown,\n host: SyncApplyHost,\n): Promise<SyncApplyItemResult> {\n const validated = validateSyncApplyItem(raw);\n if (!validated.ok) {\n return validated.result;\n }\n\n const item = validated.item;\n try {\n const target = await host.resolveTarget(item.object);\n if (!target) {\n return {\n itemId: item.itemId,\n id: item.id,\n status: 'rejected',\n reason: 'unknown_object',\n };\n }\n return await applyValidatedItem(item, target);\n } catch {\n // One item's failure must never fail the batch.\n return {\n itemId: item.itemId,\n id: item.id,\n status: 'rejected',\n reason: 'write_failed',\n };\n }\n}\n\n/**\n * Process a sync-apply batch: validate the envelope, then apply items FIFO\n * (strictly in order, awaiting each — items may depend on earlier items in\n * the same batch). Always resolves; item-level failures surface as `rejected`\n * results and only envelope-level problems produce a 400.\n */\nexport async function processSyncApplyBatch(\n body: unknown,\n host: SyncApplyHost,\n): Promise<SyncApplyOutcome> {\n const parsed = parseSyncApplyBatch(body);\n if ('error' in parsed) {\n return { status: 400, body: parsed };\n }\n\n const results: SyncApplyItemResult[] = [];\n for (const raw of parsed.items) {\n // Sequential on purpose: FIFO within the batch is part of the contract.\n results.push(await processOneItem(raw, host));\n }\n\n return { status: 200, body: { results } };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,IAAa,4BAA4B,CAAC,QAAQ,OAAO;;AAGzD,IAAa,4BAA4B;;;;;AAMzC,IAAa,0BACX;AAKF,IAAM,iBAAyC;CAAC;CAAU;CAAU;AAAQ;;;;;;;AA+J5E,SAAgB,wBACd,MACA,SAGI,CAAC,GACoB;CACzB,IAAI,CAAC,QAAQ,OAAO,SAAS,UAC3B,OAAO,CAAC;CAGV,MAAM,gCAAgB,IAAI,IAAI;EAC5B;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CACD,MAAM,WAAW,IAAI,IAAI,OAAO,kBAAkB,CAAC,CAAC;CACpD,MAAM,WAAW,OAAO,qBAAqB;CAE7C,MAAM,SAAkC,CAAC;CACzC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,GAAG;EAC/C,IAAI,IAAI,WAAW,GAAG,GAAG;EACzB,IAAI,cAAc,IAAI,GAAG,GAAG;EAC5B,IAAI,SAAS,IAAI,GAAG,GAAG;EACvB,IAAI,YAAY,CAAC,SAAS,SAAS,GAAG,GAAG;EACzC,OAAO,OAAO;CAChB;CACA,OAAO;AACT;;;;AAKA,SAAgB,oBACd,MAC4C;CAC5C,IACE,CAAC,QACD,OAAO,SAAS,YAChB,MAAM,QAAQ,IAAI,KAClB,CAAC,MAAM,QAAS,KAA6B,KAAK,GAElD,OAAO,EACL,OAAO;EACL,MAAM;EACN,SAAS;CACX,EACF;CAGF,MAAM,QAAS,KAA8B;CAC7C,IAAI,MAAM,SAAA,KACR,OAAO,EACL,OAAO;EACL,MAAM;EACN,SAAS,gCAAgC,0BAA0B;CACrE,EACF;CAGF,OAAO,EAAE,MAAM;AACjB;;;;;AAgBA,SAAgB,sBACd,KAC6B;CAC7B,MAAM,SACJ,OAAO,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG,IAC/C,MACD;CAEN,MAAM,SACJ,UAAU,OAAO,OAAO,WAAW,YAAY,OAAO,OAAO,SAAS,IAClE,OAAO,SACP;CACN,MAAM,KAAK,UAAU,OAAO,OAAO,OAAO,WAAW,OAAO,KAAK;CAEjE,MAAM,UAAU,YAAmD;EACjE,IAAI;EACJ,QAAQ;GAAE;GAAQ;GAAI,QAAQ;GAAY;EAAO;CACnD;CAEA,IAAI,CAAC,UAAU,CAAC,QACd,OAAO,OAAO,cAAc;CAG9B,MAAM,KAAK,OAAO;CAClB,IAAI,OAAO,OAAO,YAAY,CAAC,eAAe,SAAS,EAAiB,GACtE,OAAO,OAAO,cAAc;CAG9B,MAAM,SAAS,OAAO;CACtB,IAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAClD,OAAO,OAAO,cAAc;CAG9B,MAAM,gBAAgB,OAAO;CAC7B,IAAI,kBAAkB,KAAA;MAElB,OAAO,kBAAkB,YACzB,OAAO,MAAM,KAAK,MAAM,aAAa,CAAC,GAEtC,OAAO,OAAO,cAAc;CAAA;CAKhC,IAAI,CAAC,MAAM,CAAC,wBAAwB,KAAK,EAAE,GACzC,OAAO,OAAO,YAAY;CAG5B,MAAM,UAAU,OAAO;CACvB,IAAI,OAAO,YAAY,OAAO;MACxB,CAAC,WAAW,OAAO,YAAY,YAAY,MAAM,QAAQ,OAAO,GAClE,OAAO,OAAO,iBAAiB;CAAA;CAInC,OAAO;EACL,IAAI;EACJ,MAAM;GACJ;GACA;GACI;GACJ;GACS;GACM;EACjB;CACF;AACF;AAEA,SAAS,cAAc,OAAwD;CAC7E,IAAI,iBAAiB,MAAM;EACzB,MAAM,OAAO,MAAM,QAAQ;EAC3B,OAAO,OAAO,MAAM,IAAI,IAAI,OAAO;CACrC;CACA,IAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,OAAO,KAAK,MAAM,KAAK;EAC7B,OAAO,OAAO,MAAM,IAAI,IAAI,OAAO;CACrC;CACA,OAAO;AACT;;;;;;AAOA,SAAgB,aACd,eACA,iBACS;CACT,MAAM,OAAO,cAAc,iBAAiB,IAAI;CAChD,IAAI,SAAS,MAAM,OAAO;CAC1B,MAAM,SAAS,cAAc,eAAe;CAC5C,IAAI,WAAW,MAAM,OAAO;CAC5B,OAAO,OAAO;AAChB;AAEA,SAAS,uBAAuB,OAAyB;CACvD,IAAI,iBAAiB,MAAM,OAAO,MAAM,YAAY;CACpD,OAAO;AACT;AAEA,SAAS,iBAAiB,UAAmB,cAAgC;CAE3E,IAAI,oBAAoB,MAAM;EAC5B,MAAM,YACJ,OAAO,iBAAiB,YAAY,OAAO,iBAAiB,WACxD,IAAI,KAAK,YAAY,CAAC,CAAC,QAAQ,IAC/B,wBAAwB,OACtB,aAAa,QAAQ,IACrB;EACR,OAAO,CAAC,OAAO,MAAM,SAAS,KAAK,cAAc,SAAS,QAAQ;CACpE;CAEA,IAAI,aAAa,QAAQ,aAAa,KAAA,GACpC,OAAO,iBAAiB,QAAQ,iBAAiB,KAAA;CAGnD,IAAI,OAAO,aAAa,YAAY,OAAO,iBAAiB,UAC1D,OAAO,aAAa;CAGtB,IAAI;EACF,OACE,KAAK,UAAU,uBAAuB,QAAQ,CAAC,MAC/C,KAAK,UAAU,uBAAuB,YAAY,CAAC;CAEvD,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;AASA,SAAgB,kBACd,SACA,KACS;CACT,MAAM,SAAS;CACf,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,GAAG;EAClD,IAAI,EAAE,OAAO,SAAS;EACtB,IAAI,CAAC,iBAAiB,OAAO,MAAM,KAAK,GAAG,OAAO;CACpD;CACA,OAAO;AACT;AAEA,SAAS,gBAAgB,KAA2C;CAClE,MAAM,QAAQ,IAAI;CAClB,IAAI,iBAAiB,MAAM,OAAO,MAAM,YAAY;CACpD,IAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,QAAQ,KAAK,MAAM,KAAK;EAC9B,OAAO,OAAO,MAAM,KAAK,IAAI,QAAQ,IAAI,KAAK,KAAK,CAAC,CAAC,YAAY;CACnE;AAEF;;;;;;;;;;;AAYA,SAAS,wBAAwB,OAAyB;CACxD,OAAO,uBAAuB,KAAK;AACrC;AAEA,eAAe,mBACb,MACA,QAC8B;CAC9B,MAAM,OAAO;EAAE,QAAQ,KAAK;EAAQ,IAAI,KAAK;CAAG;CAIhD,MAAM,WAAW,MAAM,OAAO,UAAU,KAAK,EAAE;CAC/C,IAAI,aAAa,MACf,OAAO;EAAE,GAAG;EAAM,QAAQ;EAAY,QAAQ;CAAS;CAGzD,IAAI,CAAC,OAAO,YAAY,KAAK,EAAE,GAC7B,OAAO;EAAE,GAAG;EAAM,QAAQ;EAAY,QAAQ;CAAiB;CAMjE,MAAM,OAAO,KAAK,OAAO,WAAW,CAAC,IAAI,OAAO,QAAQ,KAAK,WAAW,CAAC,CAAC;CAI1E,MAAM,MAAM,MAAM,OAAO,WAAW,IAAI,KAAK,EAAE;CAE/C,QAAQ,KAAK,IAAb;EACE,KAAK;GACH,IAAI,KAAK;IACP,IAAI,kBAAkB,MAAM,GAAG,GAE7B,OAAO;KACL,GAAG;KACH,QAAQ;KACR,WAAW,gBAAgB,GAAG;IAChC;IAIF,OAAO;KACL,GAAG;KACH,QAAQ;KACR,QAAQ;KACR,WAAW,gBAAgB,GAAG;IAChC;GACF;GAEA,IAAI;IAOF,MAAM,UAAU,MAAM,OAAO,WAAW,OAAO;KAC7C,GAAG;KACH,IAAI,KAAK;KACT,aAAa;IACf,CAAC;IACD,OAAO;KACL,GAAG;KACH,QAAQ;KACR,WAAW,gBAAgB,OAAO;IACpC;GACF,SAAS,OAAO;IAId,IAAI,wBAAwB,KAAK,GAC/B,OAAO;KAAE,GAAG;KAAM,QAAQ;KAAY,QAAQ;IAAc;IAE9D,MAAM;GACR;EAGF,KAAK;GACH,IAAI,CAAC,KACH,OAAO;IAAE,GAAG;IAAM,QAAQ;IAAY,QAAQ;GAAY;GAE5D,IAAI,kBAAkB,MAAM,GAAG,GAG7B,OAAO;IAAE,GAAG;IAAM,QAAQ;IAAW,WAAW,gBAAgB,GAAG;GAAE;GAEvE,IAAI,aAAa,KAAK,eAAe,IAAI,UAAU,GACjD,OAAO;IACL,GAAG;IACH,QAAQ;IACR,QAAQ;IACR,WAAW,gBAAgB,GAAG;GAChC;GAEF,IAAI,OAAO,QAAQ,MAAM,OAAO,OAAO,KAAK,UAAU,IAAI;QACrD;IACH,OAAO,OAAO,KAAK,IAAI;IACvB,MAAM,IAAI,KAAK;GACjB;GACA,OAAO;IAAE,GAAG;IAAM,QAAQ;IAAW,WAAW,gBAAgB,GAAG;GAAE;EAGvE,KAAK;GACH,IAAI,CAAC,KAGH,OAAO;IAAE,GAAG;IAAM,QAAQ;GAAU;GAEtC,IAAI,aAAa,KAAK,eAAe,IAAI,UAAU,GACjD,OAAO;IACL,GAAG;IACH,QAAQ;IACR,QAAQ;IACR,WAAW,gBAAgB,GAAG;GAChC;GAEF,IAAI,OAAO,QAAQ,MAAM,OAAO,OAAO,KAAK,UAAU,IAAI;QACrD,MAAM,IAAI,OAAO;GACtB,OAAO;IAAE,GAAG;IAAM,QAAQ;GAAU;CAExC;AACF;AAEA,eAAe,eACb,KACA,MAC8B;CAC9B,MAAM,YAAY,sBAAsB,GAAG;CAC3C,IAAI,CAAC,UAAU,IACb,OAAO,UAAU;CAGnB,MAAM,OAAO,UAAU;CACvB,IAAI;EACF,MAAM,SAAS,MAAM,KAAK,cAAc,KAAK,MAAM;EACnD,IAAI,CAAC,QACH,OAAO;GACL,QAAQ,KAAK;GACb,IAAI,KAAK;GACT,QAAQ;GACR,QAAQ;EACV;EAEF,OAAO,MAAM,mBAAmB,MAAM,MAAM;CAC9C,QAAQ;EAEN,OAAO;GACL,QAAQ,KAAK;GACb,IAAI,KAAK;GACT,QAAQ;GACR,QAAQ;EACV;CACF;AACF;;;;;;;AAQA,eAAsB,sBACpB,MACA,MAC2B;CAC3B,MAAM,SAAS,oBAAoB,IAAI;CACvC,IAAI,WAAW,QACb,OAAO;EAAE,QAAQ;EAAK,MAAM;CAAO;CAGrC,MAAM,UAAiC,CAAC;CACxC,KAAK,MAAM,OAAO,OAAO,OAEvB,QAAQ,KAAK,MAAM,eAAe,KAAK,IAAI,CAAC;CAG9C,OAAO;EAAE,QAAQ;EAAK,MAAM,EAAE,QAAQ;CAAE;AAC1C"}
@@ -101,5 +101,14 @@ export interface ScannerModule {
101
101
  }>;
102
102
  uiToolPrefixes?: readonly string[];
103
103
  }): ScannerAgentSurface['diagnostics'];
104
+ /** Parse one module's source; used to locate decorated classes (#3490). */
105
+ parseSource(sourceText: string, filename?: string): {
106
+ classes: Array<{
107
+ className: string;
108
+ startLine: number;
109
+ bodyStart?: number;
110
+ hasSmartDecorator: boolean;
111
+ }>;
112
+ };
104
113
  }
105
114
  //# 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":"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"}
1
+ {"version":3,"file":"scanner-module.d.ts","sourceRoot":"","sources":["../../src/utils/scanner-module.ts"],"names":[],"mappings":"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"}
@@ -176,6 +176,20 @@ export declare function collectAgentSurfaceToolNameCollisions(input: {
176
176
  uiToolPrefixes?: readonly string[];
177
177
  }): DomainKnowledgeAgentSurface['diagnostics'];
178
178
  export declare function isRegisterShimModuleId(id: string): boolean;
179
+ /**
180
+ * The manifest a library build publishes: it records that the package stamps
181
+ * its classes (`stampsConstructors`, #3490) when it has a scoped name.
182
+ */
183
+ export declare function stampedLibraryManifest(manifest: SmartObjectManifest): SmartObjectManifest;
184
+ /**
185
+ * Library builds: stamp each `@smrt()` class a package source module declares
186
+ * with its package (`injectPackageStamps`, #3490). Returns null when the
187
+ * module declares none.
188
+ */
189
+ export declare function stampPackageModule(code: string, id: string, manifest: SmartObjectManifest): Promise<{
190
+ code: string;
191
+ map: null;
192
+ } | null>;
179
193
  /**
180
194
  * Generate the replacement `__smrt-register__` module with the build-time
181
195
  * manifest inlined as data (issues #1506/#1507).
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EACV,2BAA2B,EAC3B,qBAAqB,EAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,MAAM,EAAiC,MAAM,MAAM,CAAC;AAElE,OAAO,EAEL,KAAK,6BAA6B,EACnC,MAAM,2BAA2B,CAAC;AAQnC,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EACV,mBAAmB,EACnB,aAAa,EACd,MAAM,4BAA4B,CAAC;AAiCpC,OAAO,EACL,kCAAkC,EAClC,KAAK,sCAAsC,EAC3C,KAAK,8BAA8B,EACnC,KAAK,6BAA6B,EAClC,KAAK,0BAA0B,EAC/B,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,2BAA2B,CAAC;AACnC,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;AAClC,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAalE,MAAM,WAAW,iBAAiB;IAChC,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,6BAA6B,CAAC;IACnD,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;QAC7D;;;;;;WAMG;QACH,cAAc,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;KACxC,CAAC;IACF,mEAAmE;IACnE,SAAS,CAAC,EAAE,qBAAqB,GAAG,KAAK,CAAC;IAC1C;;;;;;OAMG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE;QACP,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,kBAAkB,CAAC,EAAE,6BAA6B,CAAC;QACnD,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,aAAa,EAAE,OAAO,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;IACF,sBAAsB,CACpB,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAClC;;;;OAIG;IACH,4BAA4B,IAAI,OAAO,CACrC,2BAA2B,GAAG,SAAS,CACxC,CAAC;CACH;AAYD;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,mBAAmB,GAAG,SAAS,GACvC,2BAA2B,GAAG,SAAS,CAgBzC;AAyBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qCAAqC,CAAC,KAAK,EAAE;IAC3D,OAAO,EAAE,mBAAmB,CAAC;IAC7B,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,0BAA0B,EAAE,aAAa,CAAC,4BAA4B,CAAC,CAAC;IACxE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC,GAAG,2BAA2B,CAAC,aAAa,CAAC,CAY7C;AAsED,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAG1D;AAKD;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,mBAAmB,GAC5B,mBAAmB,CAIrB;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,IAAI,CAAA;CAAE,GAAG,IAAI,CAAC,CAuC7C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,mBAAmB,GAC5B,MAAM,CAaR;AA+HD,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAsoClE;AAiRD;;;GAGG;AACH,wBAAsB,2BAA2B,CAC/C,QAAQ,EAAE,mBAAmB,EAC7B,WAAW,EAAE,MAAM,EACnB,oBAAoB,EAAE,MAAM,GAC3B,OAAO,CAAC,IAAI,CAAC,CAoaf"}
@@ -11,6 +11,7 @@ import { loadVerifiedSmrtGenerationSnapshot, serializeSmrtGenerationSnapshot, sh
11
11
  import { renderApiClientCrudType, renderApiClientCustomMethodParameters, selectApiClientEntries } from "./api-client-entries.js";
12
12
  import { GENERATED_CLIENT_BASE_METHODS, GENERATED_CLIENT_CRUD_METHODS, generateClientModule } from "./generated-client.js";
13
13
  import { importBuildAwareModule } from "./import-build-aware.js";
14
+ import { injectPackageStamps, packageStampText } from "./package-stamp.js";
14
15
  import { injectSvelteKitRegistration } from "./sveltekit-register-injection.js";
15
16
  import { assertSvelteKitRouteCoordinationComplete, contributeSvelteKitRoutes, expectedSvelteKitRouteOwners, markSvelteKitRouteParticipant } from "./sveltekit-route-coordinator.js";
16
17
  import { buildWorkerRegistration } from "./worker-registration.js";
@@ -157,6 +158,41 @@ function isRegisterShimModuleId(id) {
157
158
  const cleanId = id.split("?")[0];
158
159
  return REGISTER_SHIM_RE.test(cleanId) && !cleanId.includes("node_modules");
159
160
  }
161
+ /** Source modules a library build may stamp (#3490). */
162
+ var STAMPABLE_MODULE_RE = /\.(?:ts|mts|tsx|js|mjs|jsx)$/;
163
+ /**
164
+ * The manifest a library build publishes: it records that the package stamps
165
+ * its classes (`stampsConstructors`, #3490) when it has a scoped name.
166
+ */
167
+ function stampedLibraryManifest(manifest) {
168
+ return manifest.packageName?.startsWith("@") ? {
169
+ ...manifest,
170
+ stampsConstructors: true
171
+ } : manifest;
172
+ }
173
+ /**
174
+ * Library builds: stamp each `@smrt()` class a package source module declares
175
+ * with its package (`injectPackageStamps`, #3490). Returns null when the
176
+ * module declares none.
177
+ */
178
+ async function stampPackageModule(code, id, manifest) {
179
+ const packageName = manifest.packageName;
180
+ const cleanId = id.split("?")[0];
181
+ if (!packageName?.startsWith("@") || cleanId.startsWith("\0") || cleanId.includes("node_modules") || !STAMPABLE_MODULE_RE.test(cleanId) || !code.includes("smrt")) return null;
182
+ const scanner = await importScanner();
183
+ const decorated = (source) => scanner.parseSource(source, cleanId).classes.filter((candidate) => candidate.hasSmartDecorator);
184
+ const targets = decorated(code);
185
+ const stamped = injectPackageStamps(code, targets, packageName, cleanId);
186
+ if (stamped === null) return null;
187
+ const stamp = packageStampText(packageName);
188
+ const after = decorated(stamped);
189
+ const stampCount = stamped.split(stamp).length - 1;
190
+ if (after.length !== targets.length || stampCount !== targets.length || after.some(({ bodyStart }) => typeof bodyStart !== "number" || !stamped.startsWith(stamp, bodyStart + 1))) throw new Error(`[smrt] Package stamping of ${cleanId} did not stamp exactly its ${targets.length} @smrt() class(es) (#3490).`);
191
+ return {
192
+ code: stamped,
193
+ map: null
194
+ };
195
+ }
160
196
  /**
161
197
  * Generate the replacement `__smrt-register__` module with the build-time
162
198
  * manifest inlined as data (issues #1506/#1507).
@@ -542,24 +578,27 @@ function smrtPlugin(options = {}) {
542
578
  if (activeBuildStart === build) activeBuildStart = void 0;
543
579
  }
544
580
  },
545
- transform(code, id) {
546
- if (svelteKit.enabled && !config?.build?.lib) {
547
- const injected = injectSvelteKitRegistration(code, id, {
548
- projectRoot: configuredProjectRoot ?? projectRoot,
549
- configPath: svelteKit.configPath,
550
- configFileName: svelteKit.configFileName
551
- });
552
- return injected === null ? null : {
553
- code: injected,
581
+ transform: {
582
+ order: "pre",
583
+ async handler(code, id) {
584
+ if (svelteKit.enabled && !config?.build?.lib) {
585
+ const injected = injectSvelteKitRegistration(code, id, {
586
+ projectRoot: configuredProjectRoot ?? projectRoot,
587
+ configPath: svelteKit.configPath,
588
+ configFileName: svelteKit.configFileName
589
+ });
590
+ return injected === null ? null : {
591
+ code: injected,
592
+ map: null
593
+ };
594
+ }
595
+ if (!config?.build?.lib || !manifest) return null;
596
+ if (isRegisterShimModuleId(id)) return {
597
+ code: generateInlineRegisterModule(stampedLibraryManifest(manifest)),
554
598
  map: null
555
599
  };
600
+ return stampPackageModule(code, id, manifest);
556
601
  }
557
- if (!config?.build?.lib || !manifest) return null;
558
- if (!isRegisterShimModuleId(id)) return null;
559
- return {
560
- code: generateInlineRegisterModule(manifest),
561
- map: null
562
- };
563
602
  },
564
603
  shouldTransformCachedModule({ id }) {
565
604
  if (config?.build?.lib && isRegisterShimModuleId(id)) return true;
@@ -697,11 +736,12 @@ function smrtPlugin(options = {}) {
697
736
  const manifestPath = resolve(projectRoot, outDir, "manifest.json");
698
737
  const knowledgePath = resolve(projectRoot, outDir, "smrt-knowledge.json");
699
738
  mkdirSync(dirname(manifestPath), { recursive: true });
739
+ const published = stampedLibraryManifest(manifest);
700
740
  publishAtomicArtifact({
701
741
  path: manifestPath,
702
- content: JSON.stringify(manifest, null, 2)
742
+ content: JSON.stringify(published, null, 2)
703
743
  });
704
- await writeDomainKnowledgeArtifact(manifest, projectRoot, knowledgePath, manifestPath, true);
744
+ await writeDomainKnowledgeArtifact(published, projectRoot, knowledgePath, manifestPath, true);
705
745
  const objectCount = Object.keys(manifest.objects).length;
706
746
  console.log(`[smrt] Wrote manifest with ${objectCount} objects to ${manifestPath}`);
707
747
  } catch (error) {
@@ -1429,6 +1469,6 @@ async function loadDefaultUI() {
1429
1469
  return getDefaultUIModule();
1430
1470
  }
1431
1471
  //#endregion
1432
- export { buildWebMcpToolDefinitions, collectAgentSurfaceToolNameCollisions, findCliApiCoherenceViolations, generateInlineRegisterModule, generateSvelteKitRoutes, generateTypeDeclarationFile, isRegisterShimModuleId, loadVerifiedSmrtGenerationSnapshot, methodNameToKebab, resolveApiActionRouteConfig, resolveApiActionSet, serializeSmrtGenerationSnapshot, sha256SmrtGenerationSnapshot, smrtPlugin, toKnowledgeAgentSurface, validateCliIncludeAgainstApi };
1472
+ export { buildWebMcpToolDefinitions, collectAgentSurfaceToolNameCollisions, findCliApiCoherenceViolations, generateInlineRegisterModule, generateSvelteKitRoutes, generateTypeDeclarationFile, isRegisterShimModuleId, loadVerifiedSmrtGenerationSnapshot, methodNameToKebab, resolveApiActionRouteConfig, resolveApiActionSet, serializeSmrtGenerationSnapshot, sha256SmrtGenerationSnapshot, smrtPlugin, stampPackageModule, stampedLibraryManifest, toKnowledgeAgentSurface, validateCliIncludeAgainstApi };
1433
1473
 
1434
1474
  //# sourceMappingURL=index.js.map