@lostgradient/weft 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/README.md +72 -4
  2. package/dist/cli/codegen-emit-dedup.d.ts +81 -0
  3. package/dist/cli/codegen-emit-dedup.js +45 -0
  4. package/dist/cli/codegen-emit-registry.d.ts +90 -0
  5. package/dist/cli/codegen-emit-registry.js +50 -0
  6. package/dist/cli/codegen-emit.d.ts +26 -39
  7. package/dist/cli/codegen-emit.js +4 -27
  8. package/dist/cli/codegen-validate.d.ts +49 -0
  9. package/dist/cli/codegen-validate.js +186 -0
  10. package/dist/cli/codegen.js +6 -87
  11. package/dist/cli/generated/operation-catalog.snapshot.json +287 -3
  12. package/dist/cli/generated/operation-client.generated.d.ts +324 -10
  13. package/dist/cli/generated/operation-client.generated.js +12 -0
  14. package/dist/cli/output.js +1 -1
  15. package/dist/cli/storage-factory.js +2 -0
  16. package/dist/cli/workflow-commands.js +2 -0
  17. package/dist/cli-main.js +1 -1
  18. package/dist/core/application-payload-digest.d.ts +42 -0
  19. package/dist/core/application-payload-digest.js +76 -0
  20. package/dist/core/application-primitive-abort.d.ts +50 -0
  21. package/dist/core/application-primitive-abort.js +40 -0
  22. package/dist/core/application-primitive-attempt-registry.d.ts +74 -0
  23. package/dist/core/application-primitive-attempt-registry.js +73 -0
  24. package/dist/core/application-primitive-codec.d.ts +36 -0
  25. package/dist/core/application-primitive-codec.js +56 -0
  26. package/dist/core/application-primitive-commit.d.ts +67 -0
  27. package/dist/core/application-primitive-commit.js +66 -0
  28. package/dist/core/application-primitive-guards.d.ts +62 -0
  29. package/dist/core/application-primitive-guards.js +89 -0
  30. package/dist/core/application-primitive-payload.d.ts +29 -0
  31. package/dist/core/application-primitive-payload.js +65 -0
  32. package/dist/core/application-primitive-timing.d.ts +18 -0
  33. package/dist/core/application-primitive-timing.js +22 -0
  34. package/dist/core/atomic-state.js +2 -0
  35. package/dist/core/catalog/codec.d.ts +40 -0
  36. package/dist/core/catalog/codec.js +45 -0
  37. package/dist/core/catalog/errors.d.ts +104 -0
  38. package/dist/core/catalog/errors.js +41 -0
  39. package/dist/core/catalog/index.d.ts +25 -0
  40. package/dist/core/catalog/index.js +15 -0
  41. package/dist/core/catalog/reference-counts.d.ts +105 -0
  42. package/dist/core/catalog/reference-counts.js +28 -0
  43. package/dist/core/catalog/removal.d.ts +52 -0
  44. package/dist/core/catalog/removal.js +14 -0
  45. package/dist/core/catalog/storage-io.d.ts +78 -0
  46. package/dist/core/catalog/storage-io.js +106 -0
  47. package/dist/core/catalog/types.d.ts +108 -0
  48. package/dist/core/catalog/types.js +0 -0
  49. package/dist/core/catalog/workflow-catalog.d.ts +165 -0
  50. package/dist/core/catalog/workflow-catalog.js +198 -0
  51. package/dist/core/compare-codepoint.d.ts +13 -0
  52. package/dist/core/compare-codepoint.js +7 -0
  53. package/dist/core/compression.js +2 -0
  54. package/dist/core/context/attributes.d.ts +1 -1
  55. package/dist/core/context/durable-operations.d.ts +2 -2
  56. package/dist/core/context/index.js +5 -3
  57. package/dist/core/context/state-namespace.d.ts +1 -1
  58. package/dist/core/contract/build.d.ts +64 -0
  59. package/dist/core/contract/build.js +99 -0
  60. package/dist/core/contract/compatibility.d.ts +187 -0
  61. package/dist/core/contract/compatibility.js +37 -0
  62. package/dist/core/contract/failure.d.ts +64 -0
  63. package/dist/core/contract/failure.js +4 -0
  64. package/dist/core/contract/hash.d.ts +92 -0
  65. package/dist/core/contract/hash.js +34 -0
  66. package/dist/core/contract/index.d.ts +21 -0
  67. package/dist/core/contract/index.js +22 -0
  68. package/dist/core/contract/limits.d.ts +74 -0
  69. package/dist/core/contract/limits.js +1 -0
  70. package/dist/core/contract/manifest-parse-schema.d.ts +37 -0
  71. package/dist/core/contract/manifest-parse-schema.js +106 -0
  72. package/dist/core/contract/manifest-parse.d.ts +76 -0
  73. package/dist/core/contract/manifest-parse.js +189 -0
  74. package/dist/core/contract/manifest.d.ts +56 -0
  75. package/dist/core/contract/manifest.js +31 -0
  76. package/dist/core/contract/normalize.d.ts +97 -0
  77. package/dist/core/contract/normalize.js +127 -0
  78. package/dist/core/contract/revision.d.ts +29 -0
  79. package/dist/core/contract/revision.js +5 -0
  80. package/dist/core/contract/types.d.ts +274 -0
  81. package/dist/core/contract/types.js +1 -0
  82. package/dist/core/engine/catalog-activation.d.ts +40 -0
  83. package/dist/core/engine/catalog-activation.js +17 -0
  84. package/dist/core/engine/catalog-events.d.ts +36 -0
  85. package/dist/core/engine/catalog-events.js +15 -0
  86. package/dist/core/engine/catalog-readiness.d.ts +49 -0
  87. package/dist/core/engine/catalog-readiness.js +59 -0
  88. package/dist/core/engine/catalog-removal.d.ts +132 -0
  89. package/dist/core/engine/catalog-removal.js +70 -0
  90. package/dist/core/engine/construction.d.ts +1 -1
  91. package/dist/core/engine/construction.js +4 -2
  92. package/dist/core/engine/disposal.js +9 -0
  93. package/dist/core/engine/engine-workflows-namespace.d.ts +130 -0
  94. package/dist/core/engine/engine-workflows-namespace.js +49 -0
  95. package/dist/core/engine/finalizer-metadata.d.ts +22 -0
  96. package/dist/core/engine/finalizer-metadata.js +12 -0
  97. package/dist/core/engine/handle-result.js +2 -0
  98. package/dist/core/engine/index.d.ts +83 -0
  99. package/dist/core/engine/index.js +61 -0
  100. package/dist/core/engine/internals.d.ts +44 -1
  101. package/dist/core/engine/internals.js +6 -0
  102. package/dist/core/engine/lifecycle/start.js +3 -0
  103. package/dist/core/engine/registration.d.ts +76 -0
  104. package/dist/core/engine/registration.js +12 -6
  105. package/dist/core/engine/source-registration.d.ts +42 -0
  106. package/dist/core/engine/source-registration.js +36 -0
  107. package/dist/core/engine/source-resolution.d.ts +85 -0
  108. package/dist/core/engine/source-resolution.js +119 -0
  109. package/dist/core/engine/validation.js +2 -2
  110. package/dist/core/events/catalog-events.d.ts +117 -0
  111. package/dist/core/events/catalog-events.js +62 -0
  112. package/dist/core/events/event-map.d.ts +6 -0
  113. package/dist/core/events/index.d.ts +1 -0
  114. package/dist/core/events/index.js +1 -0
  115. package/dist/core/mailbox-admission.d.ts +26 -0
  116. package/dist/core/mailbox-admission.js +130 -0
  117. package/dist/core/mailbox-claims.d.ts +251 -0
  118. package/dist/core/mailbox-claims.js +0 -0
  119. package/dist/core/mailbox-codec.d.ts +32 -0
  120. package/dist/core/mailbox-codec.js +221 -0
  121. package/dist/core/mailbox-contract.d.ts +290 -0
  122. package/dist/core/mailbox-contract.js +0 -0
  123. package/dist/core/mailbox-delivery.d.ts +37 -0
  124. package/dist/core/mailbox-delivery.js +193 -0
  125. package/dist/core/mailbox-guards.d.ts +78 -0
  126. package/dist/core/mailbox-guards.js +58 -0
  127. package/dist/core/mailbox-index-codec.d.ts +41 -0
  128. package/dist/core/mailbox-index-codec.js +71 -0
  129. package/dist/core/mailbox-internals.d.ts +156 -0
  130. package/dist/core/mailbox-internals.js +154 -0
  131. package/dist/core/mailbox-maintenance.d.ts +29 -0
  132. package/dist/core/mailbox-maintenance.js +196 -0
  133. package/dist/core/mailbox-settlement.d.ts +69 -0
  134. package/dist/core/mailbox-settlement.js +206 -0
  135. package/dist/core/mailbox-storage.d.ts +117 -0
  136. package/dist/core/mailbox-storage.js +115 -0
  137. package/dist/core/mailbox-transition-helpers.d.ts +70 -0
  138. package/dist/core/mailbox-transition-helpers.js +43 -0
  139. package/dist/core/mailbox-transitions-recovery.d.ts +28 -0
  140. package/dist/core/mailbox-transitions-recovery.js +64 -0
  141. package/dist/core/mailbox-transitions.d.ts +138 -0
  142. package/dist/core/mailbox-transitions.js +180 -0
  143. package/dist/core/mailbox-types.d.ts +406 -0
  144. package/dist/core/mailbox-types.js +16 -0
  145. package/dist/core/mailbox-validation.d.ts +65 -0
  146. package/dist/core/mailbox-validation.js +73 -0
  147. package/dist/core/mailbox-waits.d.ts +50 -0
  148. package/dist/core/mailbox-waits.js +97 -0
  149. package/dist/core/mailbox.d.ts +163 -0
  150. package/dist/core/mailbox.js +211 -0
  151. package/dist/core/outbox-claims.d.ts +307 -0
  152. package/dist/core/outbox-claims.js +0 -0
  153. package/dist/core/outbox-codec.d.ts +24 -0
  154. package/dist/core/outbox-codec.js +257 -0
  155. package/dist/core/outbox-contract.d.ts +346 -0
  156. package/dist/core/outbox-contract.js +0 -0
  157. package/dist/core/outbox-delivery.d.ts +34 -0
  158. package/dist/core/outbox-delivery.js +149 -0
  159. package/dist/core/outbox-drain.d.ts +29 -0
  160. package/dist/core/outbox-drain.js +158 -0
  161. package/dist/core/outbox-enqueue.d.ts +24 -0
  162. package/dist/core/outbox-enqueue.js +115 -0
  163. package/dist/core/outbox-guards.d.ts +76 -0
  164. package/dist/core/outbox-guards.js +60 -0
  165. package/dist/core/outbox-index-codec.d.ts +33 -0
  166. package/dist/core/outbox-index-codec.js +71 -0
  167. package/dist/core/outbox-internals.d.ts +99 -0
  168. package/dist/core/outbox-internals.js +159 -0
  169. package/dist/core/outbox-listing.d.ts +12 -0
  170. package/dist/core/outbox-listing.js +38 -0
  171. package/dist/core/outbox-maintenance.d.ts +28 -0
  172. package/dist/core/outbox-maintenance.js +191 -0
  173. package/dist/core/outbox-operations.d.ts +18 -0
  174. package/dist/core/outbox-operations.js +46 -0
  175. package/dist/core/outbox-runner.d.ts +30 -0
  176. package/dist/core/outbox-runner.js +217 -0
  177. package/dist/core/outbox-settlement.d.ts +57 -0
  178. package/dist/core/outbox-settlement.js +183 -0
  179. package/dist/core/outbox-storage.d.ts +107 -0
  180. package/dist/core/outbox-storage.js +127 -0
  181. package/dist/core/outbox-transition-helpers.d.ts +83 -0
  182. package/dist/core/outbox-transition-helpers.js +63 -0
  183. package/dist/core/outbox-transitions-recovery.d.ts +30 -0
  184. package/dist/core/outbox-transitions-recovery.js +42 -0
  185. package/dist/core/outbox-transitions.d.ts +145 -0
  186. package/dist/core/outbox-transitions.js +236 -0
  187. package/dist/core/outbox-types.d.ts +400 -0
  188. package/dist/core/outbox-types.js +20 -0
  189. package/dist/core/outbox-validation.d.ts +101 -0
  190. package/dist/core/outbox-validation.js +127 -0
  191. package/dist/core/outbox-waits.d.ts +39 -0
  192. package/dist/core/outbox-waits.js +88 -0
  193. package/dist/core/outbox.d.ts +156 -0
  194. package/dist/core/outbox.js +234 -0
  195. package/dist/core/registry-limits.d.ts +49 -0
  196. package/dist/core/registry-limits.js +10 -0
  197. package/dist/core/registry-schema-conversion.d.ts +20 -0
  198. package/dist/core/registry-schema-conversion.js +22 -0
  199. package/dist/core/registry-snapshot.d.ts +91 -42
  200. package/dist/core/registry-snapshot.js +39 -77
  201. package/dist/core/registry-workflow-contract-draft.d.ts +32 -0
  202. package/dist/core/registry-workflow-contract-draft.js +28 -0
  203. package/dist/core/registry-workflow-manifest.d.ts +122 -0
  204. package/dist/core/registry-workflow-manifest.js +92 -0
  205. package/dist/core/source/errors.d.ts +55 -0
  206. package/dist/core/source/errors.js +13 -0
  207. package/dist/core/source/index.d.ts +19 -0
  208. package/dist/core/source/index.js +4 -0
  209. package/dist/core/source/resolvers.d.ts +45 -0
  210. package/dist/core/source/resolvers.js +9 -0
  211. package/dist/core/source/types.d.ts +211 -0
  212. package/dist/core/source/types.js +0 -0
  213. package/dist/core/source/validate.d.ts +91 -0
  214. package/dist/core/source/validate.js +62 -0
  215. package/dist/core/source/workflow-source.d.ts +43 -0
  216. package/dist/core/source/workflow-source.js +16 -0
  217. package/dist/core/types/workflow-registries.d.ts +7 -0
  218. package/dist/core/types/workflow-registry.d.ts +12 -0
  219. package/dist/core/versioning.js +2 -0
  220. package/dist/core/weft-error.d.ts +1 -1
  221. package/dist/core/weft-error.js +9 -1
  222. package/dist/http.js +2 -2
  223. package/dist/index.d.ts +23 -2
  224. package/dist/index.js +50 -0
  225. package/dist/indexeddb.js +1 -1
  226. package/dist/json-schema.js +3 -3
  227. package/dist/mcp/resources.js +2 -0
  228. package/dist/runtime/portable.d.ts +9 -1
  229. package/dist/server/fault-to-json-rpc.js +3 -1
  230. package/dist/server/handler/auth-context-principal.js +4 -0
  231. package/dist/server/json-rpc-http.js +2 -0
  232. package/dist/server/json-rpc-websocket.js +6 -4
  233. package/dist/server/operation-catalog/index.d.ts +1 -1
  234. package/dist/server/operation-catalog/pipeline-helpers.js +2 -0
  235. package/dist/server/operation-catalog/types.d.ts +17 -0
  236. package/dist/server/operation-fault.d.ts +3 -0
  237. package/dist/server/operation-fault.js +3 -1
  238. package/dist/server/operations/activate-workflow-revision.d.ts +30 -0
  239. package/dist/server/operations/activate-workflow-revision.js +69 -0
  240. package/dist/server/operations/fleet-events-subscription.js +1 -1
  241. package/dist/server/operations/fork-workflow.js +1 -1
  242. package/dist/server/operations/get-active-workflow-revision.d.ts +32 -0
  243. package/dist/server/operations/get-active-workflow-revision.js +46 -0
  244. package/dist/server/operations/get-catalog-diagnostics.d.ts +85 -0
  245. package/dist/server/operations/get-catalog-diagnostics.js +57 -0
  246. package/dist/server/operations/get-registry.js +17 -4
  247. package/dist/server/operations/get-workflow-revision.d.ts +25 -0
  248. package/dist/server/operations/get-workflow-revision.js +50 -0
  249. package/dist/server/operations/install-workflow-revision.d.ts +27 -0
  250. package/dist/server/operations/install-workflow-revision.js +46 -0
  251. package/dist/server/operations/list-workflow-revisions.d.ts +20 -0
  252. package/dist/server/operations/list-workflow-revisions.js +40 -0
  253. package/dist/server/operations/start-or-signal-workflow.js +1 -1
  254. package/dist/server/operations/start-workflow.js +1 -1
  255. package/dist/server/operations/static-registrations.js +36 -0
  256. package/dist/server/operations/storage.js +2 -2
  257. package/dist/server/operations/submit-review-decision.js +1 -1
  258. package/dist/server/operations/workflow-catalog-operation-helpers.d.ts +59 -0
  259. package/dist/server/operations/workflow-catalog-operation-helpers.js +135 -0
  260. package/dist/server/runtime/websocket-upgrade.js +2 -0
  261. package/dist/storage/bun-sql.js +49 -6
  262. package/dist/storage/catalog-keys.d.ts +45 -0
  263. package/dist/storage/catalog-keys.js +6 -0
  264. package/dist/storage/compressed-storage.js +1 -1
  265. package/dist/storage/derived-operations.js +1 -1
  266. package/dist/storage/index.d.ts +26 -1
  267. package/dist/storage/interface.d.ts +26 -1
  268. package/dist/storage/interface.js +1 -1
  269. package/dist/storage/key-encoding.d.ts +15 -0
  270. package/dist/storage/key-encoding.js +3 -0
  271. package/dist/storage/key-prefixes.d.ts +1 -1
  272. package/dist/storage/key-prefixes.js +15 -0
  273. package/dist/storage/lmdb.d.ts +26 -1
  274. package/dist/storage/lmdb.js +1 -1
  275. package/dist/storage/mailbox-keys.d.ts +80 -0
  276. package/dist/storage/mailbox-keys.js +14 -0
  277. package/dist/storage/memory.js +1 -1
  278. package/dist/storage/neon.js +2 -2
  279. package/dist/storage/node-sqlite-loader.d.ts +1 -1
  280. package/dist/storage/node-sqlite-loader.js +2 -2
  281. package/dist/storage/node-sqlite.js +51 -8
  282. package/dist/storage/outbox-keys.d.ts +77 -0
  283. package/dist/storage/outbox-keys.js +14 -0
  284. package/dist/storage/postgres.js +2 -2
  285. package/dist/storage/resolve.js +1 -1
  286. package/dist/storage/scoped-storage.js +1 -1
  287. package/dist/storage/storage-configuration.d.ts +6 -0
  288. package/dist/storage/testing.js +1 -1
  289. package/dist/storage/turso.js +2 -2
  290. package/dist/version.d.ts +1 -1
  291. package/dist/version.js +1 -1
  292. package/dist/web-extension.js +1 -1
  293. package/dist/worker/manifest/registry-contract-builder.d.ts +21 -3
  294. package/dist/worker/manifest/registry-contract-builder.js +40 -31
  295. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Weft
2
2
 
3
- A Bun-native durable execution engine. Current release: `0.23.0`.
3
+ A Bun-native durable execution engine. Current release: `0.24.0`.
4
4
 
5
5
  Install the library from npm as `@lostgradient/weft`:
6
6
 
@@ -253,6 +253,74 @@ For stable-id re-sync flows, `engine.startOrSignal()` can replace a terminal pri
253
253
 
254
254
  When a workflow drains signals with `ctx.race([ctx.waitForSignal(name), ctx.sleep(0)])` and then returns, use that stable `id`, a deterministic per-event `signalId`, and `onTerminalConflict: 'start-new'` for the corresponding `startOrSignal` calls. Delivery is serialized against terminal completion, so a signal arriving across the completion boundary is consumed by the current run or handed to its successor; a positive drain window is not needed for correctness.
255
255
 
256
+ ### Durable Application Command Mailbox
257
+
258
+ Signals are workflow control. When the thing receiving a command is an application _resource_ rather than a workflow run — an agent taking steering input, a resource fielding requests from a peer service — `Mailbox` gives it a durable, strictly FIFO command queue with real receipts.
259
+
260
+ ```typescript
261
+ import { Mailbox, MemoryStorage } from '@lostgradient/weft';
262
+
263
+ await using storage = new MemoryStorage();
264
+ using mailbox = new Mailbox({ storage, namespace: 'bureau', resourceId: 'agent-7' });
265
+
266
+ const admission = await mailbox.admit({
267
+ caller: 'user:42',
268
+ target: 'agent:7',
269
+ kind: 'steer',
270
+ payload: { form: 'inline', value: { text: 'stop and summarize' } },
271
+ idempotencyKey: 'steer-1',
272
+ });
273
+
274
+ const claimed = await mailbox.claim();
275
+ if (claimed.status === 'claimed' && admission.status === 'admitted') {
276
+ await mailbox.acknowledge({
277
+ commandId: claimed.claim.receipt.commandId,
278
+ attemptToken: claimed.claim.attemptToken,
279
+ outcome: { applied: true },
280
+ });
281
+ }
282
+ ```
283
+
284
+ Admission returns a receipt that, on a persistent backend such as `BunSQLiteStorage`, survives process restart; `MemoryStorage` above keeps the example self-contained and is gone with the process. Idempotency binds to `(caller, target, kind, payloadDigest)`, so an exact retry returns the original receipt and a conflicting reuse of the key returns a stable conflict without touching the original. Claims are attempt-fenced, so two consumers sharing one durable store can never both hold a valid claim _on the same command_ — delivery is strictly FIFO, but a later command may be claimed while an earlier one is still in flight, so completion order is up to the consumers. Cancellation is durable before it reaches anyone, and reports honestly whether cleanup is still outstanding rather than claiming an uncooperative handler stopped.
285
+
286
+ Delivery intent, ordering, ownership, and disposition are durable; external side effects are not made exactly-once. See [Application Mailbox](documentation/guides/mailbox.md).
287
+
288
+ ### Durable Application Delivery Outbox
289
+
290
+ The mailbox is for commands coming _in_ to a resource. For work going _out_ — a webhook, a notification, a message to a peer service — `Outbox` is the matching primitive: a durable, at-least-once delivery queue whose enqueue, attempt leases, transport outcomes, retry schedule, cancellation, and dead-letter state are all durable and observable, without ever confusing a transport write with an acknowledgement.
291
+
292
+ ```typescript
293
+ import { Outbox, MemoryStorage } from '@lostgradient/weft';
294
+
295
+ await using storage = new MemoryStorage();
296
+ using outbox = new Outbox({
297
+ storage,
298
+ namespace: 'bureau',
299
+ ownerId: 'agent-7',
300
+ adapter: {
301
+ async send(request) {
302
+ // Put the request on the wire. Present request.delivery.externalIdempotencyKey
303
+ // as the remote idempotency key when the delivery carries one; the attempt
304
+ // token changes per attempt and is only a request id or fence.
305
+ void request;
306
+ return { status: 'acknowledged', evidence: { messageId: 'm-1' } };
307
+ },
308
+ },
309
+ });
310
+
311
+ await outbox.enqueue({
312
+ destinationRef: 'webhook:orders',
313
+ kind: 'order.shipped',
314
+ payload: { form: 'inline', value: { orderId: 42 } },
315
+ idempotencyKey: 'order-42-shipped',
316
+ });
317
+
318
+ const delivered = await outbox.deliverNext();
319
+ if (delivered.status === 'settled') console.log(delivered.receipt.state); // 'acknowledged'
320
+ ```
321
+
322
+ Every attempt is durably marked `attempting` _before_ the adapter is called, so a crash before the send is a safe retry and a crash after it is an unknown outcome — which is parked or dead-lettered by explicit policy, and retried automatically only when the delivery carries stable external idempotency evidence. Returning from `send()` never settles anything by itself; the outbox commits the matching disposition, fenced on the attempt. See [Application Outbox](documentation/guides/outbox.md).
323
+
256
324
  ### Search Attributes
257
325
 
258
326
  Attach indexed metadata to a workflow at runtime, then list and filter on it.
@@ -335,7 +403,7 @@ A small `Storage` interface over string keys and `Uint8Array` values: five requi
335
403
  - **`SQLiteStorage`** (subpath `@lostgradient/weft/storage/sqlite`) for SQLite persistence; Bun resolves to `BunSQLiteStorage`, Node resolves to `NodeSQLiteStorage`
336
404
  - **`BunSQLiteStorage`** (subpath `@lostgradient/weft/storage/sqlite/bun`) for an explicit Bun SQLite override
337
405
  - **`NodeSQLiteStorage`** (subpath `@lostgradient/weft/storage/sqlite/node`) for an explicit Node.js SQLite override via `better-sqlite3`
338
- - **`LMDBStorage`** (subpath `@lostgradient/weft/storage/lmdb`) for embedded high-throughput workloads
406
+ - **`LMDBStorage`** (subpath `@lostgradient/weft/storage/lmdb`) for embedded high-throughput workloads; accepts a `durability: 'relaxed'` option that skips `fsync` on every commit, trading crash durability for write latency—use it for test fixtures, not production storage. `capabilities().persistence` reports `'ephemeral'` for a relaxed-durability instance (instead of `'local'`), so `assertDurableStorageForRecovery()` correctly rejects it
339
407
  - **`TursoStorage`** (subpath `@lostgradient/weft/storage/turso`) for distributed libSQL deployments
340
408
  - **`NeonStorage`** (subpath `@lostgradient/weft/storage/neon`) for durable remote Neon/Postgres deployments
341
409
  - **`IndexedDBStorage`** (subpath `@lostgradient/weft/storage/indexeddb`) for browser environments
@@ -367,7 +435,7 @@ await using server = serve({ engine, port: 7233 });
367
435
  // server.url is e.g. "http://0.0.0.0:7233"
368
436
  ```
369
437
 
370
- Endpoints under `/api/v1/` cover the full lifecycle: start workflows, list, signal, update, query, cancel, fork, and stream events. JSON-RPC over WebSocket also exposes workflow and fleet event subscriptions for operator UIs that need live state without polling. Content negotiation supports JSON and MessagePack. The server can also mount an externally supplied dashboard shell at known page routes; see the [server guide](documentation/guides/server.md#external-dashboard-mounting) for the hosting contract.
438
+ Endpoints under `/api/v1/` cover the full lifecycle: start workflows, list, signal, update, query, cancel, fork, and stream events. JSON-RPC over WebSocket also exposes workflow and fleet event subscriptions for operator UIs that need live state without polling. Content negotiation supports JSON and MessagePack. `engine.workflows` and the matching `/v1/registry/` operations expose the durable workflow catalog for admin tooling — install a revision's manifest and activate it as the advertised active revision — bookkeeping only, never workflow execution routing; see [`workflow-versioning.md`](documentation/guides/workflow-versioning.md#engineworkflows-public-catalog-control). `workflowSource()` pairs a typed, serializable source descriptor with a never-serialized loader capability, so one workflow can register lazily while another stays eagerly registered; `engine.registerSource()` records the candidate without importing it, and `engine.resolveWorkflowSource()` loads, validates, and installs it into the same catalog with single-flight deduplication per `(name, revision)`; see [Dynamic Workflow Sources](documentation/guides/workflow-versioning.md#dynamic-workflow-sources). The server can also mount an externally supplied dashboard shell at known page routes; see the [server guide](documentation/guides/server.md#external-dashboard-mounting) for the hosting contract.
371
439
 
372
440
  ### Remote Workers
373
441
 
@@ -565,7 +633,7 @@ Guides:
565
633
  - [Durable Timers](documentation/guides/durable-timers.md), [Timeouts](documentation/guides/timeouts.md), [Parallel Execution](documentation/guides/parallel-execution.md)
566
634
  - [Search Attributes](documentation/guides/search-attributes.md), [Workflow Visibility Backfill](documentation/guides/workflow-visibility-backfill.md), [State](documentation/guides/state.md), [Session State](documentation/guides/session-state.md), [Events](documentation/guides/events.md)
567
635
  - [Interceptors](documentation/guides/interceptors.md), [Observability](documentation/guides/observability.md), [Testing](documentation/guides/testing.md)
568
- - [Workflow Versioning](documentation/guides/workflow-versioning.md), [Remote Workers](documentation/guides/remote-workers.md), [Service Worker](documentation/guides/service-worker.md), [Resource Management](documentation/guides/resource-management.md), [Concurrency: Mutex and Semaphore](documentation/guides/concurrency.md)
636
+ - [Workflow Versioning](documentation/guides/workflow-versioning.md), [Remote Workers](documentation/guides/remote-workers.md), [Service Worker](documentation/guides/service-worker.md), [Resource Management](documentation/guides/resource-management.md), [Concurrency: Mutex and Semaphore](documentation/guides/concurrency.md), [Application Mailbox](documentation/guides/mailbox.md), [Application Outbox](documentation/guides/outbox.md)
569
637
 
570
638
  Architecture and reference:
571
639
 
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Schema-deduplication for `weft codegen`'s generated `.d.ts`.
3
+ *
4
+ * `codegen-emit-registry.ts` walks every active workflow's `inputSchema`/
5
+ * `outputSchema` in a fixed, deterministic order (workflows sorted by name,
6
+ * then input before output) and hands this module the resulting sequence of
7
+ * {@link SchemaFragmentOccurrence}s — each fragment's independently-emitted
8
+ * TypeScript text (`jsonSchemaToTypeScript`). {@link buildSchemaAliasTable}
9
+ * groups those by that emitted text and decides, for each group, whether the
10
+ * repeated type is worth hoisting into a single shared `type` alias
11
+ * referenced from every entry that uses it, rather than repeating the same
12
+ * TypeScript text inline at every call site.
13
+ *
14
+ * Grouping by the emitted TypeScript text itself — not by a normalization of
15
+ * the source JSON Schema — is deliberate: two JSON Schema fragments that
16
+ * differ only in an order-insensitive construct (`required: ['a', 'b']` vs
17
+ * `required: ['b', 'a']`; `properties` key order) render to byte-identical
18
+ * TypeScript through `jsonSchemaToTypeScript` (which sorts object keys), so
19
+ * grouping by a JSON-level canonical key would miss deduplicating them even
20
+ * though they are exactly the same type a consumer would want aliased
21
+ * together. Grouping by the rendered text instead means "these occurrences
22
+ * produce the same alias" and "these occurrences are the same TypeScript
23
+ * type" are the same question, answered once, so there is no JSON-vs-text
24
+ * disagreement to reconcile: any two occurrences landing in the same group
25
+ * are equal by construction (`Map` keys are compared by value), not merely
26
+ * expected to agree.
27
+ *
28
+ * A schema only qualifies for hoisting when it recurs (count >= 2) and the
29
+ * emitted type is not trivial — hoisting `unknown` or `string` behind an
30
+ * opaque alias name would only make the generated file harder to read for
31
+ * zero benefit, and would trivially "dedupe" every schema-less workflow
32
+ * (`inputSchema` undefined → emitted type `unknown`) into a shared alias the
33
+ * moment two workflows both lack an input schema, which is the common case,
34
+ * not an edge case.
35
+ *
36
+ * @module cli/codegen-emit-dedup
37
+ */
38
+ export { CodegenEmitError } from './codegen-emit-keywords.ts';
39
+ /** One schema fragment's independently-emitted TypeScript text. */
40
+ export type SchemaFragmentOccurrence = {
41
+ tsType: string;
42
+ };
43
+ /** A hoisted schema's alias name and the TypeScript type it aliases. */
44
+ export type SchemaAliasEntry = {
45
+ alias: string;
46
+ tsType: string;
47
+ };
48
+ /**
49
+ * Emitted TypeScript type expressions that are never worth hoisting behind
50
+ * an alias, no matter how many times they recur: bare primitives and the
51
+ * handful of compound "no meaningful shape" forms `jsonSchemaToTypeScript`
52
+ * produces for an absent, empty, or fully-open/closed schema.
53
+ */
54
+ export declare const TRIVIAL_TYPESCRIPT_TYPES: ReadonlySet<string>;
55
+ /** Options for {@link buildSchemaAliasTable}, injectable for deterministic testing. */
56
+ export type BuildSchemaAliasTableOptions = {
57
+ /** Alias-name hash function. Defaults to {@link hashString} (FNV-1a). */
58
+ hash?: (key: string) => string;
59
+ };
60
+ /** Prefix for every generated schema alias name — distinctive and double-underscored so it never collides with a real consumer type name. */
61
+ export declare const ALIAS_PREFIX = "__WeftSchema_";
62
+ /**
63
+ * Group a fixed, deterministically-ordered sequence of schema occurrences
64
+ * by their emitted TypeScript text and decide which recurring, non-trivial
65
+ * types should be hoisted into a shared `type` alias.
66
+ *
67
+ * Returns a map keyed by `tsType` (not by alias) so a caller emitting one
68
+ * workflow entry's `input`/`output` field can look up "does this schema's
69
+ * emitted type have a hoisted alias?" directly, without a second reverse
70
+ * index — the caller computes the same `jsonSchemaToTypeScript` text it
71
+ * would otherwise emit inline, and either finds an alias or emits that text.
72
+ *
73
+ * Alias names are deterministic: `ALIAS_PREFIX` plus a hash of the emitted
74
+ * type text (FNV-1a via {@link hashString} by default, injectable via
75
+ * `options.hash` for testing). A real hash collision between two different
76
+ * emitted types is astronomically unlikely but not impossible, so it is
77
+ * checked and throws {@link CodegenEmitError} rather than silently letting
78
+ * one type's alias declaration shadow another's — `codegen.ts` already
79
+ * converts that error to `exitCode: 1`.
80
+ */
81
+ export declare function buildSchemaAliasTable(occurrences: readonly SchemaFragmentOccurrence[], options?: BuildSchemaAliasTableOptions): ReadonlyMap<string, SchemaAliasEntry>;
@@ -0,0 +1,45 @@
1
+ import { hashString } from "../runtime/portable.js";
2
+ import { CodegenEmitError } from "./codegen-emit-keywords.js";
3
+ export { CodegenEmitError } from "./codegen-emit-keywords.js";
4
+ export const TRIVIAL_TYPESCRIPT_TYPES = new Set([
5
+ "string",
6
+ "number",
7
+ "boolean",
8
+ "null",
9
+ "unknown",
10
+ "never",
11
+ "Record<string, never>",
12
+ "Record<string, unknown>",
13
+ "Array<unknown>"
14
+ ]);
15
+ const LITERAL_TOKEN_PATTERN = /^(-?[0-9]+(\.[0-9]+)?|true|false|null|"([^"\\]|\\.)*")$/;
16
+ function isTrivialTsType(tsType) {
17
+ return TRIVIAL_TYPESCRIPT_TYPES.has(tsType) || LITERAL_TOKEN_PATTERN.test(tsType);
18
+ }
19
+ export const ALIAS_PREFIX = "__WeftSchema_";
20
+ function groupOccurrences(occurrences) {
21
+ const groups = new Map;
22
+ occurrences.forEach((occurrence, index) => {
23
+ const existing = groups.get(occurrence.tsType);
24
+ if (existing === void 0)
25
+ groups.set(occurrence.tsType, { count: 1, firstIndex: index });
26
+ else
27
+ existing.count += 1;
28
+ });
29
+ return groups;
30
+ }
31
+ export function buildSchemaAliasTable(occurrences, options) {
32
+ const hash = options?.hash ?? hashString, groups = groupOccurrences(occurrences), orderedTypes = [...groups.keys()].toSorted((a, b) => groups.get(a).firstIndex - groups.get(b).firstIndex), aliasOwners = new Map, table = new Map;
33
+ for (const tsType of orderedTypes) {
34
+ if (groups.get(tsType).count < 2)
35
+ continue;
36
+ if (isTrivialTsType(tsType))
37
+ continue;
38
+ const alias = `${ALIAS_PREFIX}${hash(tsType)}`, owner = aliasOwners.get(alias);
39
+ if (owner !== void 0 && owner !== tsType)
40
+ throw new CodegenEmitError(`codegen: schema alias collision: emitted-type hashes ${JSON.stringify(hash(owner))} and ${JSON.stringify(hash(tsType))} both resolved to alias "${alias}"`);
41
+ aliasOwners.set(alias, tsType);
42
+ table.set(tsType, { alias, tsType });
43
+ }
44
+ return table;
45
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * `.d.ts` assembly for `weft codegen`'s active-workflow projection.
3
+ *
4
+ * Consumes the active workflow projection of a registry snapshot
5
+ * (`Record<name, CodegenWorkflowEntry>`, projected from its v2 manifest
6
+ * array by `codegen-validate.ts`'s `resolveActiveWorkflowEntries`) and
7
+ * produces a single `.d.ts` string that augments the public
8
+ * `'@lostgradient/weft'` module with typed `WorkflowRegistry` entries. The
9
+ * output is byte-stable across runs with the same input: keys are sorted
10
+ * with explicit codepoint comparators, property names and string-literal
11
+ * values are uniformly double-quoted via `emitStringLiteral`, and there are
12
+ * no timestamps or environment-dependent paths.
13
+ *
14
+ * The augmented `WorkflowRegistry` interface is the single source of truth
15
+ * for per-workflow input/output typing across the whole public surface:
16
+ * `engine.start`, `WorkflowHandle.result()`, AND the client
17
+ * (`WeftClient.start`/`schedule` and `ClientHandle.result()`). The client
18
+ * overloads key off this interface, so emitting one declaration narrows
19
+ * both engine and client call sites — there is no separate client-specific
20
+ * emission, and skipping codegen leaves both usable with plain string
21
+ * names. Every entry also carries `revision`/`workflowVersion` as
22
+ * string-literal fields for compile-time introspection; neither is
23
+ * required by `engine.start`/`WeftClient.start`/`.schedule()`, which read
24
+ * only `input`/`output` structurally (`WorkflowInput`/`WorkflowOutput` in
25
+ * `core/types/workflow-registries.ts`) — an ordinary start needs no
26
+ * caller-supplied revision.
27
+ *
28
+ * Activity names are no longer emitted as a global `ActivityTypes` module
29
+ * augmentation — that interface was removed when the chained workflow
30
+ * builder made activity names a per-workflow concern (typed at the
31
+ * builder's `.activities({...})` step). The snapshot still carries
32
+ * activity schemas because the same registry feeds discovery and MCP
33
+ * tooling, but the emitter intentionally drops them from the generated
34
+ * `.d.ts`.
35
+ *
36
+ * When two or more workflow entries' `inputSchema`/`outputSchema` render to
37
+ * the same non-trivial TypeScript text (see `codegen-emit-dedup.ts` — grouped
38
+ * by the emitted text itself, not by a JSON-level normalization of the
39
+ * source schema, so schemas differing only in an order-insensitive JSON
40
+ * construct like `required` array order still dedupe correctly), that text
41
+ * is hoisted into a single, unexported, file-top-level
42
+ * `type __WeftSchema_<hash> = <TS type>;` alias declared BEFORE
43
+ * `declare module '@lostgradient/weft' { ... }` — never inside it.
44
+ * Placing an alias inside the augmentation block would make it a
45
+ * pseudo-public exported type name of `@lostgradient/weft` (autocomplete
46
+ * pollution for every consumer), and because TypeScript `type` aliases
47
+ * don't structurally merge the way `interface` does, two independently
48
+ * generated `.d.ts` files declaring the same content-derived alias name
49
+ * inside the same augmented interface would collide with a real
50
+ * "Duplicate identifier" compile error the moment both landed in one
51
+ * TypeScript program. File-scoped placement avoids both problems.
52
+ *
53
+ * @module cli/codegen-emit-registry
54
+ */
55
+ export { CodegenEmitError } from './codegen-emit-keywords.ts';
56
+ /**
57
+ * One active workflow's codegen-relevant metadata: its input/output
58
+ * schemas plus the `revision`/`workflowVersion` identity fields every
59
+ * generated entry now carries. Deliberately narrower than, and separate
60
+ * from, `core/registry-workflow-manifest.ts`'s `RegistryWorkflowEntry` —
61
+ * that type also describes the full registration-time shape fed into
62
+ * `buildWorkflowRevisionManifest` itself (which doesn't yet have a
63
+ * `revision` to attach — a manifest can't carry the revision of the
64
+ * manifest used to build it), so widening it here would be circular.
65
+ * `description`/`tags` are carried through for parity with the source
66
+ * manifest and future codegen features, but this batch does not emit them
67
+ * into the `.d.ts` (see the module's `openQuestions` in its planning
68
+ * record — emitting free-text description as a JSDoc comment reopens the
69
+ * same injection-safety surface WFT-5 closed for the contract module, and
70
+ * is out of scope here).
71
+ */
72
+ export type CodegenWorkflowEntry = {
73
+ inputSchema?: Record<string, unknown>;
74
+ outputSchema?: Record<string, unknown>;
75
+ description?: string;
76
+ tags?: ReadonlyArray<string>;
77
+ revision: string;
78
+ workflowVersion: string;
79
+ };
80
+ /**
81
+ * Emit the full `.d.ts` declaration string for a registry's active workflow
82
+ * projection.
83
+ *
84
+ * The output is deterministic: keys are sorted by codepoint, property
85
+ * names and string-literal values go through {@link emitPropertyKey}/
86
+ * {@link emitStringLiteral}, unions/intersections are always parenthesized
87
+ * so they compose correctly when nested, and hoisted schema aliases are
88
+ * sorted by alias name.
89
+ */
90
+ export declare function emitRegistryDeclaration(activeWorkflows: Record<string, CodegenWorkflowEntry>): string;
@@ -0,0 +1,50 @@
1
+ import { compareCodepoint } from "../core/compare-codepoint.js";
2
+ import {
3
+ buildSchemaAliasTable
4
+ } from "./codegen-emit-dedup.js";
5
+ import { emitPropertyKey, emitStringLiteral, jsonSchemaToTypeScript } from "./codegen-emit.js";
6
+ export { CodegenEmitError } from "./codegen-emit-keywords.js";
7
+ const WEFT_PACKAGE_NAME = "@lostgradient/weft";
8
+ function sortedWorkflowEntries(workflows) {
9
+ return Object.entries(workflows).toSorted(([a], [b]) => compareCodepoint(a, b));
10
+ }
11
+ function collectSchemaOccurrences(workflows) {
12
+ const occurrences = [], workflowsWithTypes = [];
13
+ for (const [name, entry] of workflows) {
14
+ const inputType = jsonSchemaToTypeScript(entry.inputSchema), outputType = jsonSchemaToTypeScript(entry.outputSchema);
15
+ occurrences.push({ tsType: inputType }, { tsType: outputType });
16
+ workflowsWithTypes.push({ name, entry, inputType, outputType });
17
+ }
18
+ return { occurrences, workflowsWithTypes };
19
+ }
20
+ function schemaTypeReference(tsType, aliasTable) {
21
+ const aliasEntry = aliasTable.get(tsType);
22
+ return aliasEntry !== void 0 ? aliasEntry.alias : tsType;
23
+ }
24
+ function emitWorkflowEntry(workflow, aliasTable) {
25
+ const { name, entry, inputType, outputType } = workflow, input = schemaTypeReference(inputType, aliasTable), output = schemaTypeReference(outputType, aliasTable), revision = emitStringLiteral(entry.revision), workflowVersion = emitStringLiteral(entry.workflowVersion);
26
+ return ` ${emitPropertyKey(name)}: { input: ${input}; output: ${output}; revision: ${revision}; workflowVersion: ${workflowVersion} };`;
27
+ }
28
+ function emitAliasDeclarations(aliasTable) {
29
+ return [...aliasTable.values()].toSorted((a, b) => compareCodepoint(a.alias, b.alias)).map((entry) => `type ${entry.alias} = ${entry.tsType};`);
30
+ }
31
+ export function emitRegistryDeclaration(activeWorkflows) {
32
+ const workflows = sortedWorkflowEntries(activeWorkflows), { occurrences, workflowsWithTypes } = collectSchemaOccurrences(workflows), aliasTable = buildSchemaAliasTable(occurrences), aliasLines = emitAliasDeclarations(aliasTable), workflowLines = workflowsWithTypes.map((workflow) => emitWorkflowEntry(workflow, aliasTable)), workflowBlock = workflowLines.length === 0 ? " interface WorkflowRegistry {}" : [" interface WorkflowRegistry {", ...workflowLines, " }"].join(`
33
+ `);
34
+ return [
35
+ "// Generated by `weft codegen`. Do not edit by hand.",
36
+ "// Augments `WorkflowRegistry` to type engine and client call sites",
37
+ "// (start/schedule input and handle.result() output) per workflow.",
38
+ "/* eslint-disable */",
39
+ "",
40
+ ...aliasLines,
41
+ ...aliasLines.length > 0 ? [""] : [],
42
+ `declare module '${WEFT_PACKAGE_NAME}' {`,
43
+ workflowBlock,
44
+ "}",
45
+ "",
46
+ "export {};",
47
+ ""
48
+ ].join(`
49
+ `);
50
+ }
@@ -1,50 +1,37 @@
1
1
  /**
2
- * Deterministic JSON-Schema → TypeScript emitter for `weft codegen`.
2
+ * Deterministic JSON-Schema → TypeScript expression converter for
3
+ * `weft codegen`.
3
4
  *
4
- * Consumes a {@link RegistrySnapshot} (the same shape served by
5
- * `GET /v1/registry`) and produces a single `.d.ts` string that
6
- * augments the public `'@lostgradient/weft'` module with typed `WorkflowRegistry`
7
- * entries. The output is byte-stable across runs with the same input:
8
- * keys are sorted with explicit codepoint comparators, property names
9
- * are uniformly double-quoted via `JSON.stringify`, and there are no
10
- * timestamps or environment-dependent paths.
5
+ * Converts a single JSON Schema fragment ({@link jsonSchemaToTypeScript})
6
+ * into a TypeScript type expression, and emits a safely-quoted TypeScript
7
+ * string literal or property key ({@link emitStringLiteral},
8
+ * {@link emitPropertyKey}). The JSON Schema subset supported here covers
9
+ * what `definitionSchemaToJsonSchema` actually produces today (Zod via
10
+ * `z.toJSONSchema` and Valibot via `@valibot/to-json-schema`). Anything
11
+ * outside that subset degrades to `unknown` so the converter never claims
12
+ * a type it cannot justify.
11
13
  *
12
- * The augmented `WorkflowRegistry` interface is the single source of
13
- * truth for per-workflow input/output typing across the whole public
14
- * surface: `engine.start`, `WorkflowHandle.result()`, AND the client
15
- * (`WeftClient.start`/`schedule` and `ClientHandle.result()`). The
16
- * client overloads key off this interface, so emitting one declaration
17
- * narrows both engine and client call sites — there is no separate
18
- * client-specific emission, and skipping codegen leaves both usable with
19
- * plain string names.
20
- *
21
- * Activity names are no longer emitted as a global `ActivityTypes`
22
- * module augmentation — that interface was removed when the chained
23
- * workflow builder made activity names a per-workflow concern (typed at
24
- * the builder's `.activities({...})` step). The snapshot still carries
25
- * activity schemas because the same registry feeds discovery and MCP tooling,
26
- * but the emitter intentionally drops them from the
27
- * generated `.d.ts`.
28
- *
29
- * The JSON Schema subset supported here covers what
30
- * `definitionSchemaToJsonSchema` actually produces today (Zod via
31
- * `z.toJSONSchema` and Valibot via `@valibot/to-json-schema`).
32
- * Anything outside that subset degrades to `unknown` so the emitter
33
- * never claims a type it cannot justify.
14
+ * `codegen-emit-registry.ts` is the module that actually assembles a full
15
+ * `.d.ts` file (the `WorkflowRegistry` module augmentation, revision/
16
+ * workflowVersion literals, and schema-alias hoisting) from these
17
+ * primitives this module has no knowledge of the registry snapshot shape
18
+ * or the augmented module's structure, only of JSON Schema → TypeScript
19
+ * conversion.
34
20
  *
35
21
  * @module cli/codegen-emit
36
22
  */
37
- import type { RegistrySnapshot } from '../core/registry-snapshot.ts';
38
23
  export { CodegenEmitError } from './codegen-emit-keywords.ts';
24
+ /**
25
+ * Emit a TypeScript string literal type for an arbitrary string value.
26
+ * Shared by {@link emitPropertyKey} (a literal used as a property key) and
27
+ * `codegen-emit-registry.ts`'s `revision`/`workflowVersion` emission (a
28
+ * literal used as a value type) — both need the same `JSON.stringify`
29
+ * safety property: quotes, backslashes, control characters, and any other
30
+ * hostile content are always safely embedded inside the double-quoted
31
+ * string, so neither call site can inject generated TypeScript.
32
+ */
33
+ export declare function emitStringLiteral(value: string): string;
39
34
  /** Emit a TypeScript property key as a double-quoted string literal. */
40
35
  export declare function emitPropertyKey(name: string): string;
41
36
  /** Convert a single JSON Schema fragment to a TypeScript type expression. */
42
37
  export declare function jsonSchemaToTypeScript(schema: unknown): string;
43
- /**
44
- * Emit the full `.d.ts` declaration string for a registry snapshot.
45
- *
46
- * The output is deterministic: keys are sorted by codepoint, property
47
- * names go through {@link emitPropertyKey}, and unions/intersections
48
- * are always parenthesized so they compose correctly when nested.
49
- */
50
- export declare function emitRegistryDeclaration(snapshot: RegistrySnapshot): string;
@@ -6,7 +6,6 @@ import {
6
6
  PRIMITIVE_SUPPORTED_KEYS
7
7
  } from "./codegen-emit-keywords.js";
8
8
  export { CodegenEmitError } from "./codegen-emit-keywords.js";
9
- const WEFT_PACKAGE_NAME = "@lostgradient/weft";
10
9
  function codepointCompare(a, b) {
11
10
  if (a < b)
12
11
  return -1;
@@ -14,8 +13,11 @@ function codepointCompare(a, b) {
14
13
  return 1;
15
14
  return 0;
16
15
  }
16
+ export function emitStringLiteral(value) {
17
+ return JSON.stringify(value);
18
+ }
17
19
  export function emitPropertyKey(name) {
18
- return JSON.stringify(name);
20
+ return emitStringLiteral(name);
19
21
  }
20
22
  function primitiveTypeFor(typeKeyword) {
21
23
  switch (typeKeyword) {
@@ -253,28 +255,3 @@ function indexSignatureForObject(additionalRaw, namedValueTypes, hasOptionalName
253
255
  }
254
256
  return "unknown";
255
257
  }
256
- function sortedWorkflowEntries(workflows) {
257
- return Object.entries(workflows).toSorted(([a], [b]) => codepointCompare(a, b));
258
- }
259
- function emitWorkflowEntry(name, entry) {
260
- const input = jsonSchemaToTypeScript(entry.inputSchema), output = jsonSchemaToTypeScript(entry.outputSchema);
261
- return ` ${emitPropertyKey(name)}: { input: ${input}; output: ${output} };`;
262
- }
263
- export function emitRegistryDeclaration(snapshot) {
264
- const workflowLines = sortedWorkflowEntries(snapshot.workflows).map(([name, entry]) => emitWorkflowEntry(name, entry)), workflowBlock = workflowLines.length === 0 ? " interface WorkflowRegistry {}" : [" interface WorkflowRegistry {", ...workflowLines, " }"].join(`
265
- `);
266
- return [
267
- "// Generated by `weft codegen`. Do not edit by hand.",
268
- "// Augments `WorkflowRegistry` to type engine and client call sites",
269
- "// (start/schedule input and handle.result() output) per workflow.",
270
- "/* eslint-disable */",
271
- "",
272
- `declare module '${WEFT_PACKAGE_NAME}' {`,
273
- workflowBlock,
274
- "}",
275
- "",
276
- "export {};",
277
- ""
278
- ].join(`
279
- `);
280
- }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Envelope and per-manifest validation for a `weft codegen` registry
3
+ * snapshot source (fetched from a live server or vendored as `--from`
4
+ * JSON).
5
+ *
6
+ * Split out of `codegen.ts` specifically to stay under the repository's
7
+ * implementation-file-size ceiling once WFT-6's manifest validation
8
+ * (reusing `core/contract`'s hostile-input `parseWorkflowRevisionManifest`)
9
+ * was added — `codegen.ts` was already 460 of 500 lines before this module
10
+ * existed.
11
+ *
12
+ * @module cli/codegen-validate
13
+ */
14
+ import { type RegistryActivityEntry } from '../core/registry-snapshot.ts';
15
+ import type { CodegenWorkflowEntry } from './codegen-emit-registry.ts';
16
+ /**
17
+ * Ceiling on the raw `workflows` array's length, checked before a single
18
+ * element is parsed or cryptographically hashed, and on the number of
19
+ * entries in `activeRevisions`, checked before a single key/value pair is
20
+ * read (`activeRevisions` can never usefully point at more names than
21
+ * `workflows` could ever declare, so the same ceiling applies to both).
22
+ * Shared with `core/registry-snapshot.ts`'s producer-side enforcement via
23
+ * {@link MAX_REGISTRY_WORKFLOW_COUNT} rather than each defining its own
24
+ * number: a consumer ceiling looser than the producer's would be dead code,
25
+ * and one tighter would make `weft codegen --server` reject a
26
+ * legitimately-generated snapshot from a same-release server.
27
+ */
28
+ export type ValidateSnapshotResult<T> = {
29
+ ok: true;
30
+ value: T;
31
+ } | {
32
+ ok: false;
33
+ error: string;
34
+ };
35
+ /** The active workflow projection `weft codegen` emits from, plus the activity count for the CLI's summary line. */
36
+ export interface ActiveRegistryProjection {
37
+ workflows: Record<string, CodegenWorkflowEntry>;
38
+ activities: Record<string, RegistryActivityEntry>;
39
+ }
40
+ /**
41
+ * Validate an untrusted registry snapshot end-to-end: the version/envelope
42
+ * shape, every `workflows` manifest, and the `activeRevisions` pointer map,
43
+ * projecting the result down to what `weft codegen` actually emits from.
44
+ *
45
+ * Surfaces a clear version-mismatch diagnostic before delegating to the
46
+ * full envelope schema, since the version check is the most likely failure
47
+ * when consumers vendor a snapshot from an older or newer server.
48
+ */
49
+ export declare function validateRegistrySnapshot(value: unknown): Promise<ValidateSnapshotResult<ActiveRegistryProjection>>;