@lostgradient/weft 0.23.1 → 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 (294) 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.js +49 -6
  281. package/dist/storage/outbox-keys.d.ts +77 -0
  282. package/dist/storage/outbox-keys.js +14 -0
  283. package/dist/storage/postgres.js +2 -2
  284. package/dist/storage/resolve.js +1 -1
  285. package/dist/storage/scoped-storage.js +1 -1
  286. package/dist/storage/storage-configuration.d.ts +6 -0
  287. package/dist/storage/testing.js +1 -1
  288. package/dist/storage/turso.js +2 -2
  289. package/dist/version.d.ts +1 -1
  290. package/dist/version.js +1 -1
  291. package/dist/web-extension.js +1 -1
  292. package/dist/worker/manifest/registry-contract-builder.d.ts +21 -3
  293. package/dist/worker/manifest/registry-contract-builder.js +40 -31
  294. package/package.json +1 -1
@@ -0,0 +1,346 @@
1
+ /**
2
+ * The public input, result, option, and adapter types for the durable
3
+ * application delivery outbox (WFT-85).
4
+ *
5
+ * Two rules shape every signature here, as they do the mailbox's. Expected
6
+ * outcomes are discriminated results, never exceptions: an idempotency
7
+ * conflict, a full backlog, a stale attempt token, and an already-terminal
8
+ * delivery are ordinary control flow. Exceptions are reserved for caller
9
+ * mistakes (`ApplicationDeliveryValidationError`) and corrupt persisted state
10
+ * (`PersistedDataCorruptError`). And every read is non-consuming: `receipt`,
11
+ * `list`, `capacity`, and `cleanupState` never claim, start, or advance work.
12
+ *
13
+ * @module core/outbox-contract
14
+ */
15
+ import type { Storage } from '../storage/interface.ts';
16
+ import type { ApplicationEventSink } from './application-primitive-commit.ts';
17
+ import type { JSONValue } from './json.ts';
18
+ import type { ApplicationDeliveryClaimedPayload } from './outbox-claims.ts';
19
+ import type { ApplicationDeliveryCausation, ApplicationDeliveryFailure, ApplicationDeliveryPayload, ApplicationDeliveryRecord, ApplicationDeliveryState, ApplicationDeliveryUnknownOutcomePolicy } from './outbox-types.ts';
20
+ /**
21
+ * The transaction-composable append contract the outbox needs from a durable
22
+ * event feed (WFT-83). Structurally identical to the mailbox's; a real
23
+ * `FleetEventFeed` satisfies it as-is.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import { MemoryStorage } from '@lostgradient/weft';
28
+ * import type { OutboxEventSink } from '@lostgradient/weft';
29
+ * import { createFleetEventFeed } from '@lostgradient/weft/server/handler';
30
+ *
31
+ * const events: OutboxEventSink = createFleetEventFeed(new MemoryStorage());
32
+ * void events;
33
+ * ```
34
+ */
35
+ export type OutboxEventSink = ApplicationEventSink;
36
+ /**
37
+ * What a transport adapter reports for one send.
38
+ *
39
+ * `acknowledged` means the remote system confirmed the effect; `evidence` is
40
+ * bounded acknowledgement evidence persisted on the terminal receipt.
41
+ * `retryable` means nothing was confirmed and a retry is safe. `rejected`
42
+ * means the remote system refused permanently. `unknown` means the adapter
43
+ * cannot say whether the effect happened — the outbox never retries that
44
+ * without the policy and evidence to do so safely.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * import type { ApplicationDeliveryOutcome } from '@lostgradient/weft';
49
+ *
50
+ * const outcome: ApplicationDeliveryOutcome = { status: 'acknowledged', evidence: { id: 'msg-1' } };
51
+ * console.log(outcome.status); // 'acknowledged'
52
+ * ```
53
+ */
54
+ export type ApplicationDeliveryOutcome = {
55
+ readonly status: 'acknowledged';
56
+ readonly evidence?: JSONValue | undefined;
57
+ } | {
58
+ readonly status: 'retryable';
59
+ readonly message?: string | undefined;
60
+ readonly details?: JSONValue | undefined;
61
+ /** A transport-suggested delay; the outbox uses the larger of this and its own backoff. */
62
+ readonly retryAfterMs?: number | undefined;
63
+ } | {
64
+ readonly status: 'rejected';
65
+ readonly message?: string | undefined;
66
+ readonly details?: JSONValue | undefined;
67
+ } | {
68
+ readonly status: 'unknown';
69
+ readonly message?: string | undefined;
70
+ };
71
+ /**
72
+ * One send request handed to a transport adapter.
73
+ *
74
+ * `attemptToken` is unique per attempt: a per-attempt request id or fence the
75
+ * adapter may present, never a cross-attempt idempotency key — a retry mints a
76
+ * new one. Deduplication across attempts needs
77
+ * `delivery.externalIdempotencyKey`. `signal` aborts when
78
+ * cancellation is requested in this process, when the runner stops waiting
79
+ * at the attempt deadline and releases the attempt, or when the outbox is
80
+ * disposed. `credentialRef`
81
+ * is the opaque credential reference the delivery was enqueued with; only the
82
+ * adapter ever sees it.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * import type { ApplicationDeliverySendRequest } from '@lostgradient/weft';
87
+ *
88
+ * declare const request: ApplicationDeliverySendRequest;
89
+ * console.log(request.delivery.destinationRef, request.attemptToken);
90
+ * ```
91
+ */
92
+ export type ApplicationDeliverySendRequest = Readonly<{
93
+ delivery: ApplicationDeliveryReceipt;
94
+ payload: ApplicationDeliveryClaimedPayload;
95
+ credentialRef?: string | undefined;
96
+ attemptToken: string;
97
+ signal: AbortSignal;
98
+ }>;
99
+ /**
100
+ * A caller-supplied transport. Weft ships no connector; the adapter owns the
101
+ * wire, credential resolution, and how the attempt token reaches the remote
102
+ * system.
103
+ *
104
+ * Returning is transport evidence, not settlement: the outbox commits the
105
+ * matching durable disposition, fenced on the attempt, and only that commit
106
+ * moves the record. A thrown error is treated as `unknown`, because the
107
+ * request may already have left the process.
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * import type { ApplicationDeliveryAdapter } from '@lostgradient/weft';
112
+ *
113
+ * const adapter: ApplicationDeliveryAdapter = {
114
+ * async send(request) {
115
+ * void request;
116
+ * return { status: 'acknowledged' };
117
+ * },
118
+ * };
119
+ * console.log(typeof adapter.send); // 'function'
120
+ * ```
121
+ */
122
+ export type ApplicationDeliveryAdapter = {
123
+ send(request: ApplicationDeliverySendRequest): Promise<ApplicationDeliveryOutcome>;
124
+ };
125
+ /**
126
+ * Construction options for one `(namespace, ownerId)` outbox.
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * import { MemoryStorage, type OutboxOptions } from '@lostgradient/weft';
131
+ *
132
+ * const options: OutboxOptions = {
133
+ * storage: new MemoryStorage(),
134
+ * namespace: 'bureau',
135
+ * ownerId: 'agent-7',
136
+ * };
137
+ * console.log(options.namespace); // 'bureau'
138
+ * ```
139
+ */
140
+ export type OutboxOptions = {
141
+ /** Durable backend. Must report `conditionalBatch` support and snapshot scans. */
142
+ readonly storage: Storage;
143
+ /** Opaque application namespace. Weft never interprets it. */
144
+ readonly namespace: string;
145
+ /** Opaque owner identifier. One outbox per owner. */
146
+ readonly ownerId: string;
147
+ /**
148
+ * The transport `deliverNext()` and `drain()` run. Optional: a host that
149
+ * drives claims itself through `claim()`, `beginAttempt()`, and the settle
150
+ * methods needs none.
151
+ */
152
+ readonly adapter?: ApplicationDeliveryAdapter | undefined;
153
+ /** Optional durable event feed; every transition commits atomically with its event. */
154
+ readonly events?: OutboxEventSink | undefined;
155
+ /** Maximum open (non-terminal) deliveries. Enqueue past it is rejected before any write. Default 1000. */
156
+ readonly maxBacklog?: number | undefined;
157
+ /** Default lease renewal window in milliseconds. Default 30000. */
158
+ readonly visibilityTimeoutMs?: number | undefined;
159
+ /** Default ceiling on one attempt in milliseconds from its claim. Default 300000. */
160
+ readonly attemptTimeoutMs?: number | undefined;
161
+ /** Default maximum claims per delivery before dead-lettering. Default 3. */
162
+ readonly maxAttempts?: number | undefined;
163
+ /** Retry backoff base in milliseconds. Default 1000. */
164
+ readonly retryBackoffMs?: number | undefined;
165
+ /** Ceiling on retry backoff in milliseconds. Default 60000. */
166
+ readonly maxRetryBackoffMs?: number | undefined;
167
+ /** How long a terminal receipt is retained before a maintenance sweep may delete it. Default 86400000. */
168
+ readonly terminalRetentionMs?: number | undefined;
169
+ /** Maximum bytes an inline payload may encode to. Default 262144. */
170
+ readonly maxInlinePayloadBytes?: number | undefined;
171
+ /** How many delivery records one maintenance scan page reads. Default 500. */
172
+ readonly maintenanceBatchSize?: number | undefined;
173
+ /** Default disposition for a lost transport result. Default `'park'`. */
174
+ readonly unknownOutcomePolicy?: ApplicationDeliveryUnknownOutcomePolicy | undefined;
175
+ /**
176
+ * Whether the outbox runs its own maintenance interval. Default `'manual'`:
177
+ * the host calls `runMaintenance()` and nothing runs on a hidden timer. With
178
+ * `'automatic'` one interval runs a maintenance pass every
179
+ * `maintenanceIntervalMs`; `dispose()` clears it. Delivery is never driven
180
+ * by the interval — `deliverNext()` and `drain()` remain the host's call.
181
+ */
182
+ readonly backgroundTasks?: 'automatic' | 'manual' | undefined;
183
+ /** Interval between automatic maintenance passes in milliseconds. Default 1000. */
184
+ readonly maintenanceIntervalMs?: number | undefined;
185
+ /** Where an automatic maintenance pass reports a failure. Default `console.error`. */
186
+ readonly onMaintenanceError?: ((error: unknown) => void) | undefined;
187
+ /** Injected clock. Defaults to `Date.now`. */
188
+ readonly now?: (() => number) | undefined;
189
+ /** Injected identifier source, for deterministic tests. Defaults to `crypto.randomUUID`. */
190
+ readonly generateId?: (() => string) | undefined;
191
+ };
192
+ /**
193
+ * A delivery offered to the outbox.
194
+ *
195
+ * `deliveryId` is minted by the outbox; `idempotencyKey` is the caller's retry
196
+ * handle and binds to `(destinationRef, kind, payloadDigest)`.
197
+ *
198
+ * @example
199
+ * ```ts
200
+ * import type { ApplicationDeliveryInput } from '@lostgradient/weft';
201
+ *
202
+ * const input: ApplicationDeliveryInput = {
203
+ * destinationRef: 'webhook:orders',
204
+ * kind: 'order.shipped',
205
+ * payload: { form: 'inline', value: { orderId: 42 } },
206
+ * idempotencyKey: 'order-42-shipped',
207
+ * };
208
+ * console.log(input.kind); // 'order.shipped'
209
+ * ```
210
+ */
211
+ export type ApplicationDeliveryInput = {
212
+ /** Opaque destination reference. Part of the idempotency binding. */
213
+ readonly destinationRef: string;
214
+ /** Opaque credential reference. Never part of any binding, receipt, or event. */
215
+ readonly credentialRef?: string | undefined;
216
+ /** Opaque delivery kind. Part of the idempotency binding. */
217
+ readonly kind: string;
218
+ readonly payload: ApplicationDeliveryPayload;
219
+ readonly idempotencyKey?: string | undefined;
220
+ readonly payloadMediaType?: string | undefined;
221
+ readonly payloadSchema?: string | undefined;
222
+ readonly causation?: ApplicationDeliveryCausation | undefined;
223
+ /** Stable external idempotency evidence. Required for `retry-with-idempotency`. */
224
+ readonly externalIdempotencyKey?: string | undefined;
225
+ readonly unknownOutcomePolicy?: ApplicationDeliveryUnknownOutcomePolicy | undefined;
226
+ /** Delay before the delivery is due. Default 0. */
227
+ readonly availableAfterMs?: number | undefined;
228
+ readonly maxAttempts?: number | undefined;
229
+ readonly visibilityTimeoutMs?: number | undefined;
230
+ readonly attemptTimeoutMs?: number | undefined;
231
+ };
232
+ /**
233
+ * The outcome of offering a delivery.
234
+ *
235
+ * @example
236
+ * ```ts
237
+ * import type { ApplicationDeliveryAdmission } from '@lostgradient/weft';
238
+ *
239
+ * declare const admission: ApplicationDeliveryAdmission;
240
+ * if (admission.status === 'enqueued') console.log(admission.receipt.deliveryId);
241
+ * ```
242
+ */
243
+ export type ApplicationDeliveryAdmission = {
244
+ readonly status: 'enqueued';
245
+ readonly receipt: ApplicationDeliveryReceipt;
246
+ } | {
247
+ readonly status: 'duplicate';
248
+ readonly receipt: ApplicationDeliveryReceipt;
249
+ } | {
250
+ readonly status: 'conflict';
251
+ readonly receipt: ApplicationDeliveryReceipt;
252
+ readonly reason: 'idempotency-identity-mismatch';
253
+ } | {
254
+ readonly status: 'rejected';
255
+ readonly reason: 'backlog-full';
256
+ readonly capacity: OutboxCapacity;
257
+ };
258
+ /**
259
+ * An immutable point-in-time view of a delivery. Safe to share across
260
+ * observers: reading one never claims, starts, or advances work.
261
+ *
262
+ * The attempt token and the credential reference are deliberately absent. The
263
+ * token is a fencing credential and the reference is a secret locator; a
264
+ * receipt is readable by any observer.
265
+ *
266
+ * @example
267
+ * ```ts
268
+ * import type { ApplicationDeliveryReceipt } from '@lostgradient/weft';
269
+ *
270
+ * declare const receipt: ApplicationDeliveryReceipt;
271
+ * console.log(receipt.state, receipt.attempt);
272
+ * ```
273
+ */
274
+ export type ApplicationDeliveryReceipt = Readonly<{
275
+ deliveryId: string;
276
+ namespace: string;
277
+ ownerId: string;
278
+ sequence: number;
279
+ state: ApplicationDeliveryState;
280
+ destinationRef: string;
281
+ kind: string;
282
+ payloadDigest: string;
283
+ payloadForm: ApplicationDeliveryPayload['form'];
284
+ payloadMediaType?: string | undefined;
285
+ payloadSchema?: string | undefined;
286
+ idempotencyKey?: string | undefined;
287
+ externalIdempotencyKey?: string | undefined;
288
+ unknownOutcomePolicy: ApplicationDeliveryUnknownOutcomePolicy;
289
+ causation?: ApplicationDeliveryCausation | undefined;
290
+ enqueuedAt: number;
291
+ availableAt: number;
292
+ attempt: number;
293
+ retryCount: number;
294
+ maxAttempts: number;
295
+ generation: number;
296
+ claimedAt?: number | undefined;
297
+ visibilityExpiresAt?: number | undefined;
298
+ attemptDeadlineAt?: number | undefined;
299
+ lastActivityAt?: number | undefined;
300
+ transportActivity?: JSONValue | undefined;
301
+ attemptStartedAt?: number | undefined;
302
+ lastFailure?: ApplicationDeliveryFailure | undefined;
303
+ cancellationRequestedAt?: number | undefined;
304
+ cancellationReason?: string | undefined;
305
+ terminalAt?: number | undefined;
306
+ evidence?: JSONValue | undefined;
307
+ failure?: ApplicationDeliveryFailure | undefined;
308
+ /** True when the delivery terminalized while an attempt still held it and never settled. */
309
+ cleanupPending?: boolean | undefined;
310
+ }>;
311
+ /** Bounded backlog accounting. Counts only.
312
+ *
313
+ * @example
314
+ * ```ts
315
+ * import type { OutboxCapacity } from '@lostgradient/weft';
316
+ *
317
+ * declare const capacity: OutboxCapacity;
318
+ * console.log(capacity.open, capacity.remaining, capacity.limit);
319
+ * ```
320
+ */
321
+ export type OutboxCapacity = Readonly<{
322
+ open: number;
323
+ limit: number;
324
+ remaining: number;
325
+ enqueued: number;
326
+ }>;
327
+ /** Bounded listing options. `limit` is clamped to 1000.
328
+ *
329
+ * @example
330
+ * ```ts
331
+ * import type { OutboxListOptions } from '@lostgradient/weft';
332
+ *
333
+ * const options: OutboxListOptions = { limit: 50, states: ['unknown-outcome'] };
334
+ * console.log(options.limit); // 50
335
+ * ```
336
+ */
337
+ export type OutboxListOptions = {
338
+ readonly limit?: number | undefined;
339
+ readonly states?: readonly ApplicationDeliveryState[] | undefined;
340
+ };
341
+ export type { ApplicationDeliveryCancellationResult, ApplicationDeliveryClaim, ApplicationDeliveryClaimedPayload, ApplicationDeliveryCleanupResult, ApplicationDeliveryHeartbeatResult, ApplicationDeliveryOperatorResult, ApplicationDeliverySettleResult, OutboxClaimResult, OutboxDeliverResult, OutboxDrainReport, OutboxMaintenanceReport, OutboxWaitOptions, } from './outbox-claims.ts';
342
+ /** Internal helper alias: the decoded record plus the exact bytes it was read as. */
343
+ export type LoadedDeliveryRecord = {
344
+ readonly record: ApplicationDeliveryRecord;
345
+ readonly bytes: Uint8Array;
346
+ };
File without changes
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Claims for the application delivery outbox (WFT-85): leasing the earliest
3
+ * due delivery to one attempt and handing that attempt a digest-verified
4
+ * payload, the credential reference, and an attempt-scoped abort signal.
5
+ *
6
+ * The due index is time-keyed, so a claim considers the earliest entries and
7
+ * takes the first one that is due now. A delivery in retry backoff never holds
8
+ * back one that is due, which is the deliberate difference from the mailbox's
9
+ * strict FIFO.
10
+ *
11
+ * @module core/outbox-delivery
12
+ */
13
+ import type { ApplicationDeliveryClaimedPayload, OutboxClaimResult } from './outbox-contract.ts';
14
+ import { type OutboxRuntime } from './outbox-internals.ts';
15
+ import { type ApplicationDeliveryRecord } from './outbox-types.ts';
16
+ /** How many due-index entries a non-mutating due-work observation looks past. */
17
+ export declare const DUE_HEAD_LOOKAHEAD = 8;
18
+ /**
19
+ * Recompute an inline payload's digest and fail closed on a mismatch. A
20
+ * reference payload is handed over unverified, with the stored digest.
21
+ *
22
+ * @throws {PersistedDataCorruptError} When a stored inline payload no longer
23
+ * matches the digest enqueue recorded for it.
24
+ */
25
+ export declare function verifyClaimedPayload(runtime: OutboxRuntime, record: ApplicationDeliveryRecord): Promise<ApplicationDeliveryClaimedPayload>;
26
+ /**
27
+ * Lease the earliest due delivery to one attempt.
28
+ *
29
+ * Only a lost compare-and-swap counts toward contention; housekeeping that
30
+ * discarded an orphaned entry is progress.
31
+ */
32
+ export declare function claimNextDelivery(runtime: OutboxRuntime, options?: {
33
+ readonly signal?: AbortSignal | undefined;
34
+ }): Promise<OutboxClaimResult>;
@@ -0,0 +1,149 @@
1
+ import { storageConditionalBatch } from "../storage/interface.js";
2
+ import { computePayloadDigest } from "./application-payload-digest.js";
3
+ import { raceAbort } from "./application-primitive-abort.js";
4
+ import {
5
+ nextLeaseCommitSerial
6
+ } from "./application-primitive-attempt-registry.js";
7
+ import { requireClockInstant, requireGeneratedIdentifier } from "./outbox-guards.js";
8
+ import {
9
+ commitDeliveryTransition,
10
+ MAX_OUTBOX_TRANSITION_ATTEMPTS,
11
+ OutboxContentionError,
12
+ toApplicationDeliveryReceipt
13
+ } from "./outbox-internals.js";
14
+ import { loadDelivery, loadDueHead } from "./outbox-storage.js";
15
+ import { claimWaitingDelivery } from "./outbox-transitions.js";
16
+ import { isApplicationDeliveryWaiting } from "./outbox-types.js";
17
+ import { PersistedDataCorruptError } from "./persisted-data-incompatible-error.js";
18
+ export const DUE_HEAD_LOOKAHEAD = 8;
19
+ let localClaimSerial = 0;
20
+ export async function verifyClaimedPayload(runtime, record) {
21
+ if (record.payload.form === "reference")
22
+ return {
23
+ form: "reference",
24
+ reference: record.payload.reference,
25
+ digest: record.payload.digest,
26
+ byteLength: record.payload.byteLength,
27
+ verified: !1
28
+ };
29
+ const digest = await computePayloadDigest(record.payload.value);
30
+ if (digest !== record.payloadDigest)
31
+ throw new PersistedDataCorruptError(runtime.keys.delivery(record.deliveryId));
32
+ return { form: "inline", value: record.payload.value, digest, verified: !0 };
33
+ }
34
+ function isOrphanedEntry(runtime, entry, loaded) {
35
+ return !isApplicationDeliveryWaiting(loaded.record) || entry.key !== runtime.keys.due(loaded.record.availableAt, loaded.record.deliveryId);
36
+ }
37
+ async function discardOrphanedEntry(runtime, entry, observed) {
38
+ return storageConditionalBatch(runtime.storage, [
39
+ { key: entry.key, expectedValue: entry.bytes },
40
+ {
41
+ key: runtime.keys.delivery(entry.deliveryId),
42
+ expectedValue: observed === null ? null : observed.bytes
43
+ }
44
+ ], [{ type: "delete", key: entry.key }]);
45
+ }
46
+ async function resolveDeliverableHead(runtime, now) {
47
+ const [entry] = await loadDueHead(runtime.storage, runtime.keys, 1);
48
+ if (entry === void 0)
49
+ return { status: "empty" };
50
+ const loaded = await loadDelivery(runtime.storage, runtime.keys, entry.deliveryId);
51
+ if (loaded === null || isOrphanedEntry(runtime, entry, loaded))
52
+ return { status: "retry", progressed: await discardOrphanedEntry(runtime, entry, loaded) };
53
+ if (now < loaded.record.availableAt)
54
+ return { status: "held", availableAt: loaded.record.availableAt };
55
+ return { status: "claimable", loaded };
56
+ }
57
+ function registerAttemptController(runtime, attemptToken, deliveryId) {
58
+ const controller = new AbortController, release = runtime.adoptAttempt(attemptToken);
59
+ if (release === null) {
60
+ controller.abort(Error("The application outbox was disposed while this claim committed."));
61
+ return { controller, registration: null };
62
+ }
63
+ const registration = {
64
+ controller,
65
+ release,
66
+ subjectId: deliveryId,
67
+ committedSerial: null
68
+ };
69
+ runtime.attemptControllers.set(attemptToken, registration);
70
+ return { controller, registration };
71
+ }
72
+ function releaseOwnRegistration(runtime, attemptToken, registration, reason) {
73
+ if (registration === null)
74
+ return;
75
+ if (runtime.attemptControllers.get(attemptToken) === registration)
76
+ runtime.attemptControllers.delete(attemptToken);
77
+ registration.release();
78
+ if (!registration.controller.signal.aborted)
79
+ registration.controller.abort(Error(reason));
80
+ }
81
+ export async function claimNextDelivery(runtime, options) {
82
+ let losses = 0;
83
+ while (losses < MAX_OUTBOX_TRANSITION_ATTEMPTS) {
84
+ options?.signal?.throwIfAborted();
85
+ const outcome = await attemptClaim(runtime, options?.signal);
86
+ if ("status" in outcome)
87
+ return outcome;
88
+ if (outcome.lost)
89
+ losses += 1;
90
+ }
91
+ throw new OutboxContentionError("claim", null);
92
+ }
93
+ async function attemptClaim(runtime, signal) {
94
+ const now = runtime.now(), observed = await raceAbort(() => resolveDeliverableHead(runtime, now), signal);
95
+ if (observed.aborted)
96
+ throw observed.reason;
97
+ signal?.throwIfAborted();
98
+ const head = observed.value;
99
+ if (head.status === "empty")
100
+ return { status: "empty" };
101
+ if (head.status === "held")
102
+ return { status: "held", availableAt: head.availableAt };
103
+ if (head.status === "retry")
104
+ return { lost: !head.progressed };
105
+ return await leaseDelivery(runtime, head.loaded, signal) ?? { lost: !0 };
106
+ }
107
+ async function leaseDelivery(runtime, loaded, requestSignal) {
108
+ const payload = await verifyClaimedPayload(runtime, loaded.record), committedAt = requireClockInstant(runtime.now());
109
+ requestSignal?.throwIfAborted();
110
+ const generated = requireGeneratedIdentifier(runtime.generateId(), "attemptToken");
111
+ localClaimSerial += 1;
112
+ const attemptToken = `${loaded.record.sequence}.${loaded.record.attempt + 1}.${localClaimSerial}.${generated}`, transition = claimWaitingDelivery(loaded.record, { now: committedAt, attemptToken });
113
+ if (!transition.ok)
114
+ return null;
115
+ const { controller, registration } = registerAttemptController(runtime, attemptToken, loaded.record.deliveryId);
116
+ let committed;
117
+ try {
118
+ committed = await commitDeliveryTransition(runtime, {
119
+ previous: loaded.record,
120
+ expectedBytes: loaded.bytes,
121
+ next: transition.next,
122
+ now: committedAt
123
+ });
124
+ } catch (error) {
125
+ releaseOwnRegistration(runtime, attemptToken, registration, "The claim commit failed.");
126
+ throw error;
127
+ }
128
+ if (!committed) {
129
+ releaseOwnRegistration(runtime, attemptToken, registration, "The claim lost its compare-and-swap.");
130
+ return null;
131
+ }
132
+ if (registration !== null)
133
+ registration.committedSerial = nextLeaseCommitSerial();
134
+ if (requestSignal?.aborted === !0 && !controller.signal.aborted)
135
+ controller.abort(Error("The claim request was aborted while this claim committed."));
136
+ return {
137
+ status: "claimed",
138
+ claim: {
139
+ receipt: toApplicationDeliveryReceipt(transition.next),
140
+ payload,
141
+ credentialRef: transition.next.credentialRef,
142
+ attemptToken,
143
+ attempt: transition.next.attempt,
144
+ visibilityExpiresAt: transition.next.visibilityExpiresAt,
145
+ attemptDeadlineAt: transition.next.attemptDeadlineAt,
146
+ signal: controller.signal
147
+ }
148
+ };
149
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The bounded drain for the application delivery outbox (WFT-85): deliver
3
+ * everything that is due, then everything that becomes due within the budget,
4
+ * running a maintenance pass between rounds so lapsed leases are recovered,
5
+ * and report counts only.
6
+ *
7
+ * Split from `outbox-runner.ts` to keep both files under this
8
+ * repository's file-size ceiling.
9
+ *
10
+ * @module core/outbox-drain
11
+ */
12
+ import type { OutboxDrainReport } from './outbox-contract.ts';
13
+ import type { OutboxRuntime } from './outbox-internals.ts';
14
+ /**
15
+ * Deliver everything that is due, then everything that becomes due within the
16
+ * budget, running a maintenance pass before the first round and whenever a
17
+ * round finds nothing due, so lapsed leases are recovered without rescanning
18
+ * the outbox before every send. Reports counts only, and `pending` from the
19
+ * durable header, so a forced stop never claims anything it did not commit.
20
+ *
21
+ * The budget is one stop signal for the whole drain: it ends the sleeps, the
22
+ * maintenance passes, and an in-flight send alike, so a drain asked to stop at
23
+ * a deadline stops there rather than after the current delivery.
24
+ */
25
+ export declare function drainOutbox(runtime: OutboxRuntime, options: {
26
+ readonly timeoutMs: number;
27
+ readonly signal?: AbortSignal | undefined;
28
+ readonly pollIntervalMs?: number | undefined;
29
+ }): Promise<OutboxDrainReport>;