@lostgradient/weft 0.19.0 → 0.21.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 (146) hide show
  1. package/README.md +2 -2
  2. package/dist/cli/generated/operation-catalog.snapshot.json +1334 -0
  3. package/dist/cli/generated/operation-client.generated.d.ts +9 -2
  4. package/dist/cli/generated/operation-client.generated.js +2 -0
  5. package/dist/core/engine/activity-reconciliation.d.ts +2 -2
  6. package/dist/core/engine/activity-reconciliation.js +12 -8
  7. package/dist/core/engine/async-activity-completion.js +12 -4
  8. package/dist/core/engine/async-activity-records.d.ts +11 -1
  9. package/dist/core/engine/async-activity-records.js +4 -3
  10. package/dist/core/engine/attributes-tags.d.ts +11 -1
  11. package/dist/core/engine/attributes-tags.js +4 -3
  12. package/dist/core/engine/bulk-operations-purge.js +5 -2
  13. package/dist/core/engine/bulk-operations.js +17 -5
  14. package/dist/core/engine/callback-creators-bundles.js +1 -1
  15. package/dist/core/engine/callback-creators-core.js +4 -2
  16. package/dist/core/engine/checkpoint-io.js +1 -1
  17. package/dist/core/engine/child-workflow.js +2 -1
  18. package/dist/core/engine/completed-review-storage.js +1 -1
  19. package/dist/core/engine/condition-waiters.d.ts +45 -3
  20. package/dist/core/engine/condition-waiters.js +12 -3
  21. package/dist/core/engine/disposal.js +6 -0
  22. package/dist/core/engine/engine-internal-types.d.ts +12 -4
  23. package/dist/core/engine/fenced-write.d.ts +46 -15
  24. package/dist/core/engine/fenced-write.js +39 -6
  25. package/dist/core/engine/finalizer-state.d.ts +3 -1
  26. package/dist/core/engine/handle-result.d.ts +49 -1
  27. package/dist/core/engine/handle-result.js +95 -20
  28. package/dist/core/engine/index.d.ts +13 -5
  29. package/dist/core/engine/index.js +155 -5
  30. package/dist/core/engine/inline-launch-queue.js +9 -0
  31. package/dist/core/engine/internals.d.ts +73 -0
  32. package/dist/core/engine/invoke-update-handler.d.ts +19 -0
  33. package/dist/core/engine/invoke-update-handler.js +7 -0
  34. package/dist/core/engine/lease-deposition.d.ts +132 -0
  35. package/dist/core/engine/lease-deposition.js +31 -0
  36. package/dist/core/engine/lease-errors.d.ts +80 -9
  37. package/dist/core/engine/lease-errors.js +28 -2
  38. package/dist/core/engine/lifecycle/resume.js +3 -1
  39. package/dist/core/engine/lifecycle/standalone-claim-acquire.d.ts +33 -0
  40. package/dist/core/engine/lifecycle/standalone-claim-acquire.js +25 -0
  41. package/dist/core/engine/lifecycle/start-commit.js +20 -6
  42. package/dist/core/engine/lifecycle/transition.d.ts +15 -1
  43. package/dist/core/engine/lifecycle/transition.js +6 -3
  44. package/dist/core/engine/operations-activity.js +1 -1
  45. package/dist/core/engine/operations-time.js +21 -7
  46. package/dist/core/engine/owner-side-signal-poll.d.ts +132 -0
  47. package/dist/core/engine/owner-side-signal-poll.js +22 -0
  48. package/dist/core/engine/owner-side-update-poll.d.ts +116 -0
  49. package/dist/core/engine/owner-side-update-poll.js +22 -0
  50. package/dist/core/engine/ownership-bootstrap.d.ts +280 -0
  51. package/dist/core/engine/ownership-bootstrap.js +89 -0
  52. package/dist/core/engine/ownership-mode-marker.d.ts +82 -0
  53. package/dist/core/engine/ownership-mode-marker.js +52 -0
  54. package/dist/core/engine/ownership-options.d.ts +22 -6
  55. package/dist/core/engine/ownership-options.js +42 -12
  56. package/dist/core/engine/pending-updates.js +35 -24
  57. package/dist/core/engine/queries.d.ts +70 -0
  58. package/dist/core/engine/queries.js +58 -4
  59. package/dist/core/engine/schedules.js +1 -1
  60. package/dist/core/engine/signals.d.ts +11 -1
  61. package/dist/core/engine/signals.js +11 -7
  62. package/dist/core/engine/sleep-timer-acknowledgements.d.ts +68 -0
  63. package/dist/core/engine/sleep-timer-acknowledgements.js +19 -0
  64. package/dist/core/engine/storage-io.d.ts +58 -10
  65. package/dist/core/engine/storage-io.js +27 -9
  66. package/dist/core/engine/termination/cleanup.d.ts +5 -4
  67. package/dist/core/engine/termination/complete.js +23 -4
  68. package/dist/core/engine/termination/finalizer-claim.js +4 -4
  69. package/dist/core/engine/termination/state-commit-callbacks.d.ts +15 -0
  70. package/dist/core/engine/termination/state-commit-callbacks.js +0 -0
  71. package/dist/core/engine/termination/suspend.js +1 -1
  72. package/dist/core/engine/update-validation.d.ts +28 -0
  73. package/dist/core/engine/update-validation.js +37 -0
  74. package/dist/core/engine/updates.d.ts +17 -16
  75. package/dist/core/engine/updates.js +24 -47
  76. package/dist/core/engine/wake-ownership-check.d.ts +86 -0
  77. package/dist/core/engine/wake-ownership-check.js +19 -0
  78. package/dist/core/engine/wake-ownership-guard.d.ts +46 -0
  79. package/dist/core/engine/wake-ownership-guard.js +23 -0
  80. package/dist/core/engine/workflow-claim-codec.d.ts +64 -0
  81. package/dist/core/engine/workflow-claim-codec.js +44 -0
  82. package/dist/core/engine/workflow-claim-cooldown.d.ts +60 -0
  83. package/dist/core/engine/workflow-claim-cooldown.js +17 -0
  84. package/dist/core/engine/workflow-claim-fold.d.ts +82 -0
  85. package/dist/core/engine/workflow-claim-fold.js +53 -0
  86. package/dist/core/engine/workflow-claim-metrics.d.ts +131 -0
  87. package/dist/core/engine/workflow-claim-metrics.js +62 -0
  88. package/dist/core/engine/workflow-claim-reclaim-scan.d.ts +96 -0
  89. package/dist/core/engine/workflow-claim-reclaim-scan.js +66 -0
  90. package/dist/core/engine/workflow-claim-reclaim-target.d.ts +107 -0
  91. package/dist/core/engine/workflow-claim-reclaim-target.js +155 -0
  92. package/dist/core/engine/workflow-claim-registry.d.ts +227 -0
  93. package/dist/core/engine/workflow-claim-registry.js +198 -0
  94. package/dist/core/engine/workflow-claim-renewal-interval.d.ts +50 -0
  95. package/dist/core/engine/workflow-claim-renewal-interval.js +132 -0
  96. package/dist/core/engine/workflow-claim-renewal-subpasses.d.ts +265 -0
  97. package/dist/core/engine/workflow-claim-renewal-subpasses.js +104 -0
  98. package/dist/core/engine/workflow-claim-renewal-task.d.ts +159 -0
  99. package/dist/core/engine/workflow-claim-renewal-task.js +65 -0
  100. package/dist/core/engine/workflow-claim-transitions.d.ts +186 -0
  101. package/dist/core/engine/workflow-claim-transitions.js +120 -0
  102. package/dist/core/inline-execution-strategy.d.ts +20 -0
  103. package/dist/core/inline-execution-strategy.js +15 -4
  104. package/dist/core/scheduler/scheduler-class.js +3 -3
  105. package/dist/core/scheduler/timer-sources.d.ts +13 -0
  106. package/dist/core/scheduler/timer-sources.js +1 -1
  107. package/dist/core/types/options.d.ts +17 -1
  108. package/dist/core/weft-error.d.ts +1 -1
  109. package/dist/core/weft-error.js +2 -1
  110. package/dist/http.js +2 -2
  111. package/dist/index.d.ts +2 -1
  112. package/dist/index.js +8 -0
  113. package/dist/indexeddb.js +1 -1
  114. package/dist/json-schema.js +3 -3
  115. package/dist/observability/index.js +2 -2
  116. package/dist/server/operations/get-task-detail-schema.d.ts +341 -0
  117. package/dist/server/operations/get-task-detail-schema.js +118 -0
  118. package/dist/server/operations/get-task-detail.d.ts +346 -0
  119. package/dist/server/operations/get-task-detail.js +215 -0
  120. package/dist/server/operations/static-registrations.js +3 -0
  121. package/dist/storage/auto.js +1 -1
  122. package/dist/storage/bun-sql.js +113 -0
  123. package/dist/storage/compressed-storage.js +1 -1
  124. package/dist/storage/index.d.ts +38 -35
  125. package/dist/storage/interface.d.ts +87 -125
  126. package/dist/storage/interface.js +1 -1
  127. package/dist/storage/key-encoding.d.ts +51 -0
  128. package/dist/storage/key-encoding.js +13 -0
  129. package/dist/storage/key-prefixes.d.ts +1 -1
  130. package/dist/storage/key-prefixes.js +3 -0
  131. package/dist/storage/lmdb.js +1 -1
  132. package/dist/storage/memory.js +1 -1
  133. package/dist/storage/neon.js +2 -2
  134. package/dist/storage/node-sqlite.js +113 -0
  135. package/dist/storage/ownership-keys.d.ts +55 -0
  136. package/dist/storage/ownership-keys.js +6 -0
  137. package/dist/storage/postgres.js +2 -2
  138. package/dist/storage/resolve.js +1 -1
  139. package/dist/storage/scoped-storage.js +1 -1
  140. package/dist/storage/testing.js +1 -1
  141. package/dist/storage/turso.js +2 -2
  142. package/dist/version.d.ts +1 -1
  143. package/dist/version.js +1 -1
  144. package/dist/web-extension.js +1 -1
  145. package/dist/worker/protocol.js +1 -1
  146. package/package.json +1 -1
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Pure, storage-agnostic builders for the `wf-owner-epoch:<id>` /
3
+ * `wf-owner-holder:<id>` compare-and-swap fragments described in
4
+ * [ADR 0002 § Ownership transitions](../../../documentation/contributing/architecture-decisions/0002-multiengine-per-workflow-ownership.md#ownership-transitions).
5
+ *
6
+ * Every function here returns a `{ conditions, operations }` fragment meant to
7
+ * be passed straight to {@link storageConditionalBatch}, or folded into a
8
+ * larger batch alongside an enabling write (a start, a delayed-start fire, a
9
+ * terminal-state commit). None of them perform IO, read a clock, or import
10
+ * `EngineInternals` — `now` and every previously-read byte value are supplied
11
+ * by the caller, which is what makes every CAS branch here exhaustively
12
+ * unit-testable without a real `Storage` or a live claim manager (a later
13
+ * stage). This mirrors why `lease-codec.ts` was split out of
14
+ * `lease-manager.ts`: the transition math has no engine state of its own.
15
+ *
16
+ * **Why raw bytes, not re-encoded values, for every condition.** A
17
+ * `conditionalBatch` precondition compares the whole stored value as bytes.
18
+ * Re-deriving expected bytes from a decoded value (`encode(decode(raw))`)
19
+ * only round-trips by luck — a storage adapter's byte-level transport (e.g. a
20
+ * `BYTEA` column) or an older writer's differently-ordered JSON keys can be
21
+ * logically equal but byte-different, which would spuriously fail a CAS that
22
+ * should have succeeded. Every builder below therefore takes the exact bytes
23
+ * the caller most recently read or wrote, never a value reconstructed from a
24
+ * decoded record. See `lease-manager.ts`'s `readState`/`takeOwnership` for the
25
+ * same discipline applied to the global lease.
26
+ *
27
+ * @module core/engine/workflow-claim-transitions
28
+ */
29
+ import type { BatchOperation, ConditionalBatchCondition } from '../../storage/interface.ts';
30
+ /**
31
+ * `workflowClaimTtl` must be at least this many multiples of
32
+ * `workflowClaimRenewInterval`, enforced at `Engine` construction. Proposed
33
+ * default per ADR 0002; not yet a fixed final value (see the ADR's Open
34
+ * questions), but exported here so callers reference the constant rather than
35
+ * a repeated literal.
36
+ */
37
+ export declare const WORKFLOW_CLAIM_TTL_SAFETY_MULTIPLIER = 3;
38
+ /**
39
+ * Grace multiplier applied to `workflowClaimRenewInterval` and added to a
40
+ * holder's `expiresAt` before the `expire` judgment considers it stale. This
41
+ * dampens clock-skew-driven false-expiry judgments. Proposed default per ADR
42
+ * 0002 (see the ADR's Open questions for the not-yet-fixed final value).
43
+ */
44
+ export declare const WORKFLOW_CLAIM_TAKEOVER_GRACE_MULTIPLIER = 2;
45
+ /** A `{ conditions, operations }` fragment for `storageConditionalBatch`. */
46
+ export type WorkflowClaimTransitionFragment = {
47
+ conditions: ConditionalBatchCondition[];
48
+ operations: BatchOperation[];
49
+ };
50
+ /** Input to {@link buildWorkflowClaimAcquireTransition}. */
51
+ export type WorkflowClaimAcquireInput = {
52
+ workflowId: string;
53
+ /** This engine's identity, minted once per process. */
54
+ engineId: string;
55
+ now: number;
56
+ claimTtlMs: number;
57
+ /**
58
+ * The exact bytes last read for `wf-owner-epoch:<workflowId>`, or `null`
59
+ * when genuinely never written. `acquire` always reads this key first — it
60
+ * never assumes absence — so a never-seen id observes `null` and mints
61
+ * epoch `1`, while a reused id (e.g. after release, or `start-new` reusing
62
+ * a purged id) observes its true prior epoch and mints one past it. This is
63
+ * the ABA-safety property: the epoch counter is never reset, so a stale
64
+ * zombie's cached epoch from an earlier generation can never coincide with
65
+ * a later one.
66
+ */
67
+ observedEpochBytes: Uint8Array | null;
68
+ };
69
+ /**
70
+ * `acquire`: holder expected absent, epoch expected the exact bytes just
71
+ * read. Grants a fresh claim at `(observedEpoch ?? 0) + 1`. Meant to be
72
+ * folded into the same atomic batch as the enabling write (a create-batch or
73
+ * a pending-to-running transition), except for the named `acquire (standalone
74
+ * resume)` exception described in the ADR, which commits this fragment alone.
75
+ */
76
+ export declare function buildWorkflowClaimAcquireTransition(input: WorkflowClaimAcquireInput): WorkflowClaimTransitionFragment;
77
+ /** Input to {@link buildWorkflowClaimRenewTransition}. */
78
+ export type WorkflowClaimRenewInput = {
79
+ workflowId: string;
80
+ now: number;
81
+ claimTtlMs: number;
82
+ /**
83
+ * The exact holder bytes this engine last wrote (from its own prior
84
+ * acquire, takeover, or renew) — the sole CAS condition. The epoch key is
85
+ * not conditioned on separately: any epoch change already changes these
86
+ * bytes, since `epoch` is one of the holder record's fields.
87
+ */
88
+ currentHolderBytes: Uint8Array;
89
+ };
90
+ /**
91
+ * `renew`: holder expected the exact bytes this engine last wrote. Rewrites
92
+ * the holder with the SAME `engineId`, SAME `epoch`, and SAME `claimedAt`,
93
+ * and a fresh `expiresAt`. `currentHolderBytes` must decode (it is this
94
+ * engine's own prior write); a caller that no longer has a valid cached
95
+ * holder should not attempt renewal.
96
+ */
97
+ export declare function buildWorkflowClaimRenewTransition(input: WorkflowClaimRenewInput): WorkflowClaimTransitionFragment;
98
+ /** Input to {@link buildWorkflowClaimReleaseTransition}. */
99
+ export type WorkflowClaimReleaseInput = {
100
+ workflowId: string;
101
+ /** This engine's cached epoch bytes — CAS condition on `wf-owner-epoch:<workflowId>`. */
102
+ currentEpochBytes: Uint8Array;
103
+ /** This engine's last-known holder bytes — CAS condition on `wf-owner-holder:<workflowId>`. */
104
+ currentHolderBytes: Uint8Array;
105
+ };
106
+ /**
107
+ * `release`: epoch expected this engine's cached epoch bytes AND holder
108
+ * expected last-known holder bytes. Deletes ONLY the holder — the epoch key
109
+ * is never deleted, so a successor's next `acquire` reads the true prior
110
+ * epoch and mints one past it rather than re-minting a stale generation.
111
+ */
112
+ export declare function buildWorkflowClaimReleaseTransition(input: WorkflowClaimReleaseInput): WorkflowClaimTransitionFragment;
113
+ /** Input to {@link buildWorkflowClaimTakeoverTransition}. */
114
+ export type WorkflowClaimTakeoverInput = {
115
+ workflowId: string;
116
+ engineId: string;
117
+ now: number;
118
+ claimTtlMs: number;
119
+ /** The exact stale holder bytes read during the `expire` judgment — CAS condition. */
120
+ observedHolderBytes: Uint8Array;
121
+ /** The exact epoch bytes read alongside `observedHolderBytes` — CAS condition and the source for the minted epoch, never self-reported by the stale holder. */
122
+ observedEpochBytes: Uint8Array;
123
+ };
124
+ /**
125
+ * `takeover`: holder expected the exact stale holder bytes read AND epoch
126
+ * expected the exact epoch bytes read alongside it. Grants a fresh claim at
127
+ * `readEpoch + 1`, minted from the epoch bytes just read — never from the
128
+ * stale holder's self-reported `epoch` field, which a corrupt or hostile
129
+ * record could understate.
130
+ */
131
+ export declare function buildWorkflowClaimTakeoverTransition(input: WorkflowClaimTakeoverInput): WorkflowClaimTransitionFragment;
132
+ /** Input to {@link buildWorkflowClaimExternalTerminalRotationTransition}. */
133
+ export type WorkflowClaimExternalTerminalRotationInput = {
134
+ workflowId: string;
135
+ /**
136
+ * The exact bytes last read for `wf-owner-epoch:<workflowId>`, or `null`
137
+ * when the workflow was never claimed (e.g. cancelling a workflow that
138
+ * never resumed). The rotated epoch is `(observedEpoch ?? 0) + 1`, the same
139
+ * never-a-literal minting rule `acquire` uses.
140
+ */
141
+ observedEpochBytes: Uint8Array | null;
142
+ };
143
+ /**
144
+ * External terminal rotation: rotates `wf-owner-epoch:<workflowId>` to
145
+ * `readEpoch + 1` and deletes `wf-owner-holder:<workflowId>`, in one fragment
146
+ * meant to be folded into the SAME atomic batch that writes a cancel,
147
+ * timeout, suspend, or purge terminal/suspended state. Any engine may commit
148
+ * this — that is what makes these transitions "intentionally external" per
149
+ * the ADR's entry-point classification. Rotation is what deposes a still-running
150
+ * owner: its next write carries the now-stale epoch and loses its CAS.
151
+ *
152
+ * This must NOT be used for non-terminal external mutations (signal delivery,
153
+ * tag/search-attribute edits) — those do not end the run, so the owner's
154
+ * continued execution is correct and the epoch must not rotate under it.
155
+ */
156
+ export declare function buildWorkflowClaimExternalTerminalRotationTransition(input: WorkflowClaimExternalTerminalRotationInput): WorkflowClaimTransitionFragment;
157
+ /** Input to {@link isWorkflowClaimExpired}. */
158
+ export type WorkflowClaimExpiryInput = {
159
+ /** `expiresAt` from the holder bytes last read. */
160
+ expiresAt: number;
161
+ /** This engine's own clock. */
162
+ now: number;
163
+ /** The configured `workflowClaimRenewInterval`, scaled by {@link WORKFLOW_CLAIM_TAKEOVER_GRACE_MULTIPLIER}. */
164
+ renewIntervalMs: number;
165
+ };
166
+ /**
167
+ * `expire`: an observed condition, not a storage transition. A holder is
168
+ * eligible for `takeover` once its `expiresAt` plus a grace term
169
+ * (`WORKFLOW_CLAIM_TAKEOVER_GRACE_MULTIPLIER * renewIntervalMs`) is strictly
170
+ * earlier than `now` — matching the ADR's "is earlier than this engine's own
171
+ * clock" wording exactly, so a grace-adjusted deadline equal to `now` is NOT
172
+ * yet expired. The grace term dampens clock-skew-driven false-expiry
173
+ * judgments; it carries no write-safety weight on its own — only the
174
+ * subsequent `takeover` CAS does.
175
+ */
176
+ export declare function isWorkflowClaimExpired(input: WorkflowClaimExpiryInput): boolean;
177
+ /**
178
+ * Pull the exact bytes a just-built transition fragment wrote for `key`,
179
+ * rather than re-encoding a value from the fields the caller happens to
180
+ * know. This is what lets {@link WorkflowClaimRegistry} cache "the bytes it
181
+ * actually wrote" without silently drifting if `workflow-claim-transitions.ts`'s
182
+ * internal object-literal field order ever changed. Exported so the
183
+ * not-found branch — unreachable through the registry itself, since every
184
+ * fragment it extracts from is one it just built — has direct unit coverage.
185
+ */
186
+ export declare function extractPutOperationValue(operations: BatchOperation[], key: string): Uint8Array;
@@ -0,0 +1,120 @@
1
+ import { KEYS } from "../../storage/interface.js";
2
+ import {
3
+ decodeEpoch,
4
+ decodeWorkflowClaimHolder,
5
+ encodeEpoch,
6
+ encodeWorkflowClaimHolder
7
+ } from "./workflow-claim-codec.js";
8
+ export const WORKFLOW_CLAIM_TTL_SAFETY_MULTIPLIER = 3, WORKFLOW_CLAIM_TAKEOVER_GRACE_MULTIPLIER = 2;
9
+ function nextEpochFromObservedBytes(observedEpochBytes) {
10
+ return ((observedEpochBytes === null ? null : decodeEpoch(observedEpochBytes)) ?? 0) + 1;
11
+ }
12
+ function buildClaimGrantOperations(input) {
13
+ const holder = {
14
+ engineId: input.engineId,
15
+ epoch: input.nextEpoch,
16
+ expiresAt: input.now + input.claimTtlMs,
17
+ claimedAt: input.now
18
+ };
19
+ return [
20
+ {
21
+ type: "put",
22
+ key: KEYS.workflowOwnerEpoch(input.workflowId),
23
+ value: encodeEpoch(input.nextEpoch)
24
+ },
25
+ {
26
+ type: "put",
27
+ key: KEYS.workflowOwnerHolder(input.workflowId),
28
+ value: encodeWorkflowClaimHolder(holder)
29
+ }
30
+ ];
31
+ }
32
+ export function buildWorkflowClaimAcquireTransition(input) {
33
+ const nextEpoch = nextEpochFromObservedBytes(input.observedEpochBytes);
34
+ return {
35
+ conditions: [
36
+ { key: KEYS.workflowOwnerHolder(input.workflowId), expectedValue: null },
37
+ { key: KEYS.workflowOwnerEpoch(input.workflowId), expectedValue: input.observedEpochBytes }
38
+ ],
39
+ operations: buildClaimGrantOperations({
40
+ workflowId: input.workflowId,
41
+ engineId: input.engineId,
42
+ now: input.now,
43
+ claimTtlMs: input.claimTtlMs,
44
+ nextEpoch
45
+ })
46
+ };
47
+ }
48
+ export function buildWorkflowClaimRenewTransition(input) {
49
+ const currentHolder = decodeWorkflowClaimHolder(input.currentHolderBytes);
50
+ if (currentHolder === null)
51
+ throw Error("buildWorkflowClaimRenewTransition: currentHolderBytes did not decode to a valid holder record");
52
+ const renewedHolder = {
53
+ engineId: currentHolder.engineId,
54
+ epoch: currentHolder.epoch,
55
+ expiresAt: input.now + input.claimTtlMs,
56
+ claimedAt: currentHolder.claimedAt
57
+ };
58
+ return {
59
+ conditions: [
60
+ { key: KEYS.workflowOwnerHolder(input.workflowId), expectedValue: input.currentHolderBytes }
61
+ ],
62
+ operations: [
63
+ {
64
+ type: "put",
65
+ key: KEYS.workflowOwnerHolder(input.workflowId),
66
+ value: encodeWorkflowClaimHolder(renewedHolder)
67
+ }
68
+ ]
69
+ };
70
+ }
71
+ export function buildWorkflowClaimReleaseTransition(input) {
72
+ return {
73
+ conditions: [
74
+ { key: KEYS.workflowOwnerEpoch(input.workflowId), expectedValue: input.currentEpochBytes },
75
+ { key: KEYS.workflowOwnerHolder(input.workflowId), expectedValue: input.currentHolderBytes }
76
+ ],
77
+ operations: [{ type: "delete", key: KEYS.workflowOwnerHolder(input.workflowId) }]
78
+ };
79
+ }
80
+ export function buildWorkflowClaimTakeoverTransition(input) {
81
+ const nextEpoch = nextEpochFromObservedBytes(input.observedEpochBytes);
82
+ return {
83
+ conditions: [
84
+ { key: KEYS.workflowOwnerHolder(input.workflowId), expectedValue: input.observedHolderBytes },
85
+ { key: KEYS.workflowOwnerEpoch(input.workflowId), expectedValue: input.observedEpochBytes }
86
+ ],
87
+ operations: buildClaimGrantOperations({
88
+ workflowId: input.workflowId,
89
+ engineId: input.engineId,
90
+ now: input.now,
91
+ claimTtlMs: input.claimTtlMs,
92
+ nextEpoch
93
+ })
94
+ };
95
+ }
96
+ export function buildWorkflowClaimExternalTerminalRotationTransition(input) {
97
+ const nextEpoch = nextEpochFromObservedBytes(input.observedEpochBytes);
98
+ return {
99
+ conditions: [
100
+ { key: KEYS.workflowOwnerEpoch(input.workflowId), expectedValue: input.observedEpochBytes }
101
+ ],
102
+ operations: [
103
+ {
104
+ type: "put",
105
+ key: KEYS.workflowOwnerEpoch(input.workflowId),
106
+ value: encodeEpoch(nextEpoch)
107
+ },
108
+ { type: "delete", key: KEYS.workflowOwnerHolder(input.workflowId) }
109
+ ]
110
+ };
111
+ }
112
+ export function isWorkflowClaimExpired(input) {
113
+ return input.expiresAt + WORKFLOW_CLAIM_TAKEOVER_GRACE_MULTIPLIER * input.renewIntervalMs < input.now;
114
+ }
115
+ export function extractPutOperationValue(operations, key) {
116
+ const operation = operations.find((candidate) => candidate.type === "put" && candidate.key === key);
117
+ if (operation === void 0)
118
+ throw Error(`workflow-claim-registry: expected a "put" operation for key "${key}"`);
119
+ return operation.value;
120
+ }
@@ -66,6 +66,26 @@ export declare class InlineExecutionStrategy implements ExecutionStrategy {
66
66
  * activity failures, etc.).
67
67
  */
68
68
  throwIntoWorkflow(workflowId: string, error: unknown, operationFailureCategory?: FailureCategory): void;
69
+ /**
70
+ * Synchronously evict a workflow's live/parked context, generator, and
71
+ * abort controller the moment a same-engine reclaim begins (WFT-79) —
72
+ * BEFORE `onReclaimed`'s `recoverPendingAsyncActivities`/`resumeFromLifecycle`
73
+ * await anything. `takeover()` installing a new local epoch does not, by
74
+ * itself, evict this engine's OLD in-memory state for a workflow it
75
+ * previously held and was deposed from (deposition only drops the claim
76
+ * registry entry; contexts/generators survive — see `index.ts`'s
77
+ * `onWorkflowClaimReclaimed`). Without this, `query()` and
78
+ * `tryInlineUpdateHandler()`'s engine-id-only staleness check
79
+ * (`isWorkflowClaimedByAnotherEngine`) sees the SAME engine as both the old
80
+ * and new holder and answers "not stale," serving reads/handler invocations
81
+ * from the pre-reclaim closure throughout the async-activity reload and
82
+ * force-replay preparation window. Evicting here makes that window
83
+ * observably contextless instead — `query()` falls through to its
84
+ * documented not-locally-owned/undefined handling, and
85
+ * `tryInlineUpdateHandler()` falls through to `'not-owned-locally'` — until
86
+ * `adoptWorkflow` installs the fresh generation.
87
+ */
88
+ evictContextForReclaim(workflowId: string): void;
69
89
  /**
70
90
  * Store a context and generator created externally (engine resume path).
71
91
  * Clears any retained parked context so a concurrent query sees the new
@@ -115,6 +115,12 @@ export class InlineExecutionStrategy {
115
115
  return;
116
116
  this.#throwIntoGenerator(workflowId, generator, error, operationFailureCategory);
117
117
  }
118
+ evictContextForReclaim(workflowId) {
119
+ this.#generators.delete(workflowId);
120
+ this.#abortControllers.delete(workflowId);
121
+ this.#contexts.delete(workflowId);
122
+ this.#parkedContexts.delete(workflowId);
123
+ }
118
124
  adoptWorkflow(workflowId, generator, context, abortController) {
119
125
  this.#parkedContexts.delete(workflowId);
120
126
  this.#generators.set(workflowId, generator);
@@ -135,18 +141,23 @@ export class InlineExecutionStrategy {
135
141
  this[Symbol.dispose]();
136
142
  }
137
143
  #driveGenerator(workflowId, generator, lastResult) {
138
- return this.#advanceGenerator(workflowId, () => generator.next(lastResult), void 0);
144
+ return this.#advanceGenerator(workflowId, generator, () => generator.next(lastResult), void 0);
139
145
  }
140
146
  #throwIntoGenerator(workflowId, generator, error, operationFailureCategory) {
141
- return this.#advanceGenerator(workflowId, () => generator.throw(error), operationFailureCategory);
147
+ return this.#advanceGenerator(workflowId, generator, () => generator.throw(error), operationFailureCategory);
142
148
  }
143
- #advanceGenerator(workflowId, advance, fallbackFailureCategory) {
149
+ #advanceGenerator(workflowId, generator, advance, fallbackFailureCategory) {
144
150
  return this.#trackWorkflowAdvance(workflowId, (async () => {
145
151
  try {
146
152
  if (this.#abortControllers.get(workflowId)?.signal.aborted)
147
153
  return;
148
- this.#handleIterationResult(workflowId, await advance());
154
+ const iterationResult = await advance();
155
+ if (this.#generators.get(workflowId) !== generator)
156
+ return;
157
+ this.#handleIterationResult(workflowId, iterationResult);
149
158
  } catch (error) {
159
+ if (this.#generators.get(workflowId) !== generator)
160
+ return;
150
161
  this.#cleanup(workflowId, {
151
162
  preserveTrackedAdvance: !0,
152
163
  preserveTrackedTurn: !0
@@ -178,11 +178,11 @@ export class Scheduler {
178
178
  }
179
179
  async#buildTimerIndexDeleteOperation(nextEntry) {
180
180
  const indexKey = `timer-idx:${nextEntry.entry.id}`;
181
- if (nextEntry.entry.kind === "schedule")
182
- return this.#buildScheduleTimerIndexDeleteOperation(nextEntry, indexKey);
181
+ if (nextEntry.entry.kind === "schedule" || nextEntry.entry.kind === "sleep" || nextEntry.entry.kind === "wait-condition")
182
+ return this.#buildLookupBasedTimerIndexDeleteOperation(nextEntry, indexKey);
183
183
  return shouldDeleteTimerIndexWithoutLookup(nextEntry.entry) ? { type: "delete", key: indexKey } : null;
184
184
  }
185
- async#buildScheduleTimerIndexDeleteOperation(nextEntry, indexKey) {
185
+ async#buildLookupBasedTimerIndexDeleteOperation(nextEntry, indexKey) {
186
186
  const indexValue = await this.#storage.get(indexKey);
187
187
  if (indexValue === null)
188
188
  return null;
@@ -15,4 +15,17 @@ export declare function readNextTerminalCleanupTimerEntry(iterator: AsyncIterato
15
15
  export declare function readNextTeardownTimerEntry(iterator: AsyncIterator<[string, Uint8Array]>, storage: Storage): Promise<ScannedTimerEntry | null>;
16
16
  export declare function advanceTimerSource(timerSource: TimerSource, storage: Storage): Promise<void>;
17
17
  export declare function selectNextTimerSource(timerSources: TimerSource[]): TimerSource | undefined;
18
+ /**
19
+ * `true` for a timer kind whose `timer-idx:` reverse-index entry is safe to
20
+ * delete unconditionally on fire, with no read-and-compare first.
21
+ * `'terminal-cleanup'`/`'teardown'` get no reverse index at all (never
22
+ * cancelled out-of-band). `'schedule'`/`'sleep'`/`'wait-condition'` are
23
+ * EXCLUDED even though this function's signature still accepts them —
24
+ * `scheduler-class.ts`'s `#buildTimerIndexDeleteOperation` checks those three
25
+ * kinds and routes them through the lookup-and-compare path BEFORE ever
26
+ * calling this function, because each can have its deterministic id re-armed
27
+ * by a legitimate successor (schedule's own re-arm-on-every-tick; a
28
+ * `start-new` replacement reusing a sleep/wait-condition id) while the
29
+ * original fire is still being cleaned up.
30
+ */
18
31
  export declare function shouldDeleteTimerIndexWithoutLookup(entry: TimerEntry): boolean;
@@ -63,5 +63,5 @@ export function selectNextTimerSource(timerSources) {
63
63
  return selectedSource;
64
64
  }
65
65
  export function shouldDeleteTimerIndexWithoutLookup(entry) {
66
- return entry.kind !== "schedule" && entry.kind !== "terminal-cleanup" && entry.kind !== "teardown";
66
+ return entry.kind !== "schedule" && entry.kind !== "terminal-cleanup" && entry.kind !== "teardown" && entry.kind !== "sleep" && entry.kind !== "wait-condition";
67
67
  }
@@ -282,8 +282,14 @@ export interface EngineOptions<TServices = unknown> {
282
282
  * writing after its lease expired. Epoch fencing of durable writes is what
283
283
  * closes that gap. Keep infrastructure-level single-instance enforcement as the
284
284
  * real control.
285
+ *
286
+ * `'workflow-lease'` (ADR 0002) fences execution per workflow instead of the
287
+ * whole store, so multiple engines may share one durable store. Mutually
288
+ * exclusive with `'lease'` (enforced across processes via a store-wide
289
+ * marker) and rejected with `detectSecondInstance: true`; unlike `'lease'`,
290
+ * compatible with `backgroundTasks: 'manual'` via `runMaintenance()`.
285
291
  */
286
- ownership?: 'none' | 'lease';
292
+ ownership?: 'none' | 'lease' | 'workflow-lease';
287
293
  /**
288
294
  * Lease time-to-live for `ownership: 'lease'` (default `30s`). A holder renews
289
295
  * well within this window; once it lapses without renewal, a waiting instance
@@ -308,6 +314,16 @@ export interface EngineOptions<TServices = unknown> {
308
314
  * Ignored when `ownership` is not `'lease'`.
309
315
  */
310
316
  leaseWaitTimeout?: Duration;
317
+ /**
318
+ * Claim time-to-live for `ownership: 'workflow-lease'` (default `30s`); like
319
+ * {@link EngineOptions.leaseTtl} but scoped per workflow. Ignored otherwise.
320
+ */
321
+ workflowClaimTtl?: Duration;
322
+ /**
323
+ * Claim renewal interval for `ownership: 'workflow-lease'` (default `5s`);
324
+ * like {@link EngineOptions.leaseRenewInterval}, scoped per workflow.
325
+ */
326
+ workflowClaimRenewInterval?: Duration;
311
327
  /**
312
328
  * History circuit-breaker thresholds. When `history.maxEvents` is set, a
313
329
  * workflow whose event-log record count would exceed it is forced to a
@@ -25,7 +25,7 @@
25
25
  * }
26
26
  * ```
27
27
  */
28
- export type WeftErrorCode = 'WorkflowAlreadyExistsError' | 'BulkDeleteRequiresTerminalWorkflowsError' | 'BulkOperationConfirmationError' | 'WorkflowTypeNotRegisteredForRecoveryError' | 'EngineCreateNameMismatchError' | 'EngineDisposedError' | 'EngineDisposalError' | 'WorkflowNotFoundError' | 'WorkflowNotRegisteredError' | 'WorkflowConcurrencyLimitExceededError' | 'WorkflowSuspendNotSupportedError' | 'ActivityResolutionError' | 'BranchTopologyChangedError' | 'PersistedDataIncompatibleError' | 'WorkflowTimeoutError' | 'HttpClientError' | 'WorkerProtocolIncompatibleError' | 'UpdateTimeoutError' | 'UpdateValidationError' | 'WorkflowTerminalError' | 'WorkflowBuilderError' | 'VersionMismatchError' | 'EffectReplayConflictError' | 'ReviewTimeoutError' | 'AtomicStateConflictError' | 'StandardSchemaValidationError' | 'ActivityReconciliationCapabilityError' | 'ActivityReconciliationConflictError' | 'ActivityReconciliationIndeterminateError' | 'DurableActivityScopeError' | 'DurableActivityUnsupportedError' | 'AsyncActivityTokenNotFoundError' | 'ActivityScheduleToCloseTimeoutError' | 'ActivityPerAttemptTimeoutError' | 'PayloadSizeExceededError' | 'StartOrSignalConflictError' | 'WorkflowTeardownPendingError' | 'IdempotencyKeyPurgedError' | 'WorkerManifestBuildError';
28
+ export type WeftErrorCode = 'WorkflowAlreadyExistsError' | 'BulkDeleteRequiresTerminalWorkflowsError' | 'BulkOperationConfirmationError' | 'WorkflowTypeNotRegisteredForRecoveryError' | 'EngineCreateNameMismatchError' | 'EngineDisposedError' | 'EngineDisposalError' | 'WorkflowNotFoundError' | 'WorkflowNotRegisteredError' | 'WorkflowConcurrencyLimitExceededError' | 'WorkflowSuspendNotSupportedError' | 'ActivityResolutionError' | 'BranchTopologyChangedError' | 'PersistedDataIncompatibleError' | 'WorkflowTimeoutError' | 'HttpClientError' | 'WorkerProtocolIncompatibleError' | 'UpdateTimeoutError' | 'UpdateValidationError' | 'WorkflowTerminalError' | 'WorkflowBuilderError' | 'VersionMismatchError' | 'EffectReplayConflictError' | 'ReviewTimeoutError' | 'AtomicStateConflictError' | 'StandardSchemaValidationError' | 'ActivityReconciliationCapabilityError' | 'ActivityReconciliationConflictError' | 'ActivityReconciliationIndeterminateError' | 'DurableActivityScopeError' | 'DurableActivityUnsupportedError' | 'AsyncActivityTokenNotFoundError' | 'ActivityScheduleToCloseTimeoutError' | 'ActivityPerAttemptTimeoutError' | 'PayloadSizeExceededError' | 'StartOrSignalConflictError' | 'WorkflowTeardownPendingError' | 'IdempotencyKeyPurgedError' | 'WorkerManifestBuildError' | 'OwnershipModeMismatchError';
29
29
  /**
30
30
  * Generic abstract base for all Weft library errors. The `TCode` parameter
31
31
  * makes each subclass's `code` its own literal type; the exported base surface
@@ -45,7 +45,8 @@ const publicWeftErrorCodeMap = {
45
45
  StartOrSignalConflictError: !0,
46
46
  WorkflowTeardownPendingError: !0,
47
47
  IdempotencyKeyPurgedError: !0,
48
- WorkerManifestBuildError: !0
48
+ WorkerManifestBuildError: !0,
49
+ OwnershipModeMismatchError: !0
49
50
  }, PUBLIC_WEFT_ERROR_CODES = new Set(Object.keys(publicWeftErrorCodeMap));
50
51
  export function isWeftError(value) {
51
52
  return value instanceof WeftError;
package/dist/http.js CHANGED
@@ -1,2 +1,2 @@
1
- async function*B(F,G){if(F.body===null)return;let J=F.body.getReader(),Q=new TextDecoder,Z="",$=0,j=!1;try{while(!0){let{done:y,value:z}=await J.read();if(y){j=!0;break}if($+=z.byteLength,$>G.maximumBytes)throw G.sizeLimitError();Z+=Q.decode(z,{stream:!0});let A=Z.split(`
2
- `);Z=A.pop()??"";for(let g of A)yield g}if(Z+=Q.decode(),Z.length>0)yield Z}finally{if(!j)try{await J.cancel()}catch{}J.releaseLock()}}function N(F){let G=[];for(let J=0;J<F.length;J+=512)G.push(String.fromCharCode(...F.subarray(J,J+512)));return btoa(G.join(""))}function R(F){let G=atob(F),J=new Uint8Array(G.length);for(let Q=0;Q<G.length;Q+=1)J[Q]=G.charCodeAt(Q);return J}function q(F){return typeof F==="object"&&F!==null&&!Array.isArray(F)}async function _(F,G){return await F.get(G)!==null}async function*X(F,G,J){for await(let[Q]of F.scan(G,J))yield Q}async function H(F,G){let J=0;for await(let Q of X(F,G))J++;return J}async function L(F,G){let J=[];for await(let Q of X(F,G))J.push({type:"delete",key:Q});if(J.length===0)return 0;return await F.batch(J),J.length}async function U(F,G,J){let Q=[];for await(let Z of X(F,G,J))Q.push({type:"delete",key:Z});if(Q.length===0)return 0;return await F.batch(Q),Q.length}function C(F,G,J){if(!F.capabilities()[G])throw Error(`Feature "${J}" requires storage capability "${G}", but this storage backend does not provide it.`)}var D=1e4;class T extends Error{code="StorageBatchOperationLimitExceededError";cap=D;count;target;constructor(F,G){super(`${F} count ${G} exceeds MAX_BATCH_OPERATIONS (${D}).`);this.name="StorageBatchOperationLimitExceededError",this.target=F,this.count=G}}function M(F,G){if(G>D)throw new T(F,G)}function K(F){return F.length>0?F.slice(0,-1)+String.fromCharCode(F.charCodeAt(F.length-1)+1):"ÿ"}function e(F,G={}){if(G.gt!==void 0&&F<=G.gt)return!1;if(G.gte!==void 0&&F<G.gte)return!1;if(G.lt!==void 0&&F>=G.lt)return!1;if(G.lte!==void 0&&F>G.lte)return!1;return!0}function FF(F,G){if(F===null||G===null)return F===G;if(F.byteLength!==G.byteLength)return!1;for(let J=0;J<F.byteLength;J++)if(F[J]!==G[J])return!1;return!0}async function S(F,G){if(F.has)return F.has(G);return _(F,G)}function O(F,G,J){if(F.keys)return F.keys(G,J);return X(F,G,J)}async function P(F,G){if(F.count)return F.count(G);return H(F,G)}async function b(F,G){if(F.deletePrefix)return F.deletePrefix(G);return L(F,G)}async function v(F,G,J){if(M("conditionalBatch conditions",G.length),M("conditionalBatch operations",J.length),C(F,"conditionalBatch","storageConditionalBatch"),!F.conditionalBatch)throw Error("This storage backend reports conditionalBatch capability but does not implement the conditionalBatch() method.");return F.conditionalBatch(G,J)}function c(F){if(F===void 0)return;if(typeof F!=="number"||!Number.isInteger(F)||F<0)throw Error("deleteRange limit must be a finite non-negative integer");return F===0?0:F}function V(F){let G={},J=!1;for(let Z of["gt","gte","lt","lte"]){let $=F[Z];if($===void 0)continue;if(typeof $!=="string")throw Error("deleteRange bounds must be strings");G[Z]=$,J=!0}if(!J)throw Error("deleteRange requires at least one of gt/gte/lt/lte; use deletePrefix to delete a whole prefix");let Q=c(F.limit);if(Q!==void 0)G.limit=Q;return G}async function h(F,G,J){let Q=V(J);if(F.deleteRange)return F.deleteRange(G,Q);return U(F,G,Q)}function I(F,G){let J={key:F,open:!1};if(G.gte!==void 0&&G.gte>J.key)J.key=G.gte,J.open=!1;if(G.gt!==void 0&&G.gt>=J.key)J.key=G.gt,J.open=!0;return J}function u(F,G){let J={key:K(F),open:!0};if(G.lt!==void 0&&G.lt<=J.key)J.key=G.lt,J.open=!0;if(G.lte!==void 0&&G.lte<J.key)J.key=G.lte,J.open=!1;return J}function ZF(F,G){let J=I(F,G),Q=u(F,G);if(J.key>Q.key||J.key===Q.key&&(J.open||Q.open))return null;return{lower:J,upper:Q}}function E(F){return F.replaceAll(/:+$/g,"")}function k(F,G){let J=E(F),Q=E(G);if(J.length===0)return Q;if(Q.length===0)return J;return`${J}:${Q}`}class W{#F;#J;constructor(F,G){this.#F=F,this.#J=E(G)}#G(F){if(this.#J.length===0)return F;return F.length===0?`${this.#J}:`:`${this.#J}:${F}`}#Z(F){return F.map((G)=>{if(G.type==="put")return{type:"put",key:this.#G(G.key),value:G.value};return{type:"delete",key:this.#G(G.key)}})}#$(F){if(this.#J.length===0)return F;return F.slice(this.#J.length+1)}#M(F){let G={};if(F.limit!==void 0)G.limit=F.limit;if(F.gt!==void 0)G.gt=this.#G(F.gt);if(F.gte!==void 0)G.gte=this.#G(F.gte);if(F.lt!==void 0)G.lt=this.#G(F.lt);if(F.lte!==void 0)G.lte=this.#G(F.lte);return G}#Q(F={}){let G=this.#M(F);if(F.reverse!==void 0)G.reverse=F.reverse;return G}#X(F){return this.#M(F)}capabilities(){return this.#F.capabilities()}scoped(F){return new W(this.#F,k(this.#J,F))}async get(F){return this.#F.get(this.#G(F))}async put(F,G){await this.#F.put(this.#G(F),G)}async delete(F){await this.#F.delete(this.#G(F))}async*scan(F,G){for await(let[J,Q]of this.#F.scan(this.#G(F),this.#Q(G)))yield[this.#$(J),Q]}async batch(F){M("batch operations",F.length),await this.#F.batch(this.#Z(F))}async conditionalBatch(F,G){return v(this.#F,F.map((J)=>({key:this.#G(J.key),expectedValue:J.expectedValue})),this.#Z(G))}async has(F){return S(this.#F,this.#G(F))}async deletePrefix(F){return b(this.#F,this.#G(F))}async deleteRange(F,G){let J=this.#X(V(G));return h(this.#F,this.#G(F),J)}async*keys(F,G){for await(let J of O(this.#F,this.#G(F),this.#Q(G)))yield this.#$(J)}async count(F){return P(this.#F,this.#G(F))}[Symbol.dispose](){this.#F[Symbol.dispose]()}}function m(F,G){return new W(F,G)}var w=67108864;function x(F){if(F.type==="put")return{type:"put",key:F.key,value:N(F.value)};return{type:"delete",key:F.key}}function f(F){return{key:F.key,expectedValue:F.expectedValue===null?null:N(F.expectedValue)}}function d(F){if(!q(F)||typeof F.key!=="string"||typeof F.value!=="string")throw Error("HTTPStorage scan response contained an invalid NDJSON entry.");return{key:F.key,value:F.value}}function l(F){if(!q(F)||typeof F.applied!=="boolean")throw Error('HTTPStorage conditional batch response must include a boolean "applied" field.');return F.applied}function Y(F,G,J){if(J!==void 0)F.searchParams.set(G,String(J))}function p(F){if(F.trim().length===0)return null;let G=d(JSON.parse(F));return[G.key,R(G.value)]}class s{#F;#J;#G;constructor(F){this.#F=F.baseUrl instanceof URL?F.baseUrl:new URL(F.baseUrl),this.#J={...F.headers},this.#G=F.remoteConditionalBatch??!1}capabilities(){return{persistence:"remote",readAfterWrite:"eventual",scanConsistency:"best-effort",atomicBatch:!0,conditionalBatch:this.#G,boundedRangeDelete:!1}}#Z(F){let G=this.#F.href.endsWith("/")?this.#F.href:`${this.#F.href}/`;return new URL(F.replace(/^\/+/,""),G)}#$(F){return this.#Z(`/v1/storage/${encodeURIComponent(F)}`)}#M(F,G){let J=this.#Z("/v1/storage");return J.searchParams.set("prefix",F),Y(J,"limit",G.limit),Y(J,"reverse",G.reverse),Y(J,"gt",G.gt),Y(J,"gte",G.gte),Y(J,"lt",G.lt),Y(J,"lte",G.lte),J}async#Q(F,G={},J=[]){let Q=new Headers(this.#J);for(let[$,j]of new Headers(G.headers).entries())Q.set($,j);let Z=await fetch(F,{...G,headers:Q});if(!Z.ok&&!J.includes(Z.status))throw Error(`HTTPStorage request failed: ${G.method??"GET"} ${F.pathname} returned ${String(Z.status)}.`);return Z}async get(F){let G=await this.#Q(this.#$(F),{method:"GET"},[404]);if(G.status===404)return null;return new Uint8Array(await G.arrayBuffer())}async put(F,G){await this.#Q(this.#$(F),{method:"PUT",headers:{"content-type":"application/octet-stream"},body:new Blob([G])})}async delete(F){await this.#Q(this.#$(F),{method:"DELETE"})}async*scan(F,G={}){let J=await this.#Q(this.#M(F,G),{method:"GET",headers:{accept:"application/x-ndjson"}});for await(let Q of B(J,{maximumBytes:w,sizeLimitError:()=>Error("HTTPStorage scan response exceeded the maximum allowed size.")})){let Z=p(Q);if(Z!==null)yield Z}}async batch(F){M("batch operations",F.length),await this.#Q(this.#Z("/v1/storage/-/batch"),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({operations:F.map(x)})})}async conditionalBatch(F,G){M("conditionalBatch conditions",F.length),M("conditionalBatch operations",G.length);let J=await this.#Q(this.#Z("/v1/storage/-/conditional-batch"),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({conditions:F.map(f),operations:G.map(x)})});return l(await J.json())}has(F){return _(this,F)}async*keys(F,G){yield*X(this,F,G)}count(F){return H(this,F)}deletePrefix(F){return L(this,F)}deleteRange(F,G){return U(this,F,V(G))}scoped(F){return m(this,F)}[Symbol.dispose](){}}export{s as HTTPStorage};
1
+ async function*T(F,Q){if(F.body===null)return;let Y=F.body.getReader(),_=new TextDecoder,$="",M=0,H=!1;try{while(!0){let{done:k,value:B}=await Y.read();if(k){H=!0;break}if(M+=B.byteLength,M>Q.maximumBytes)throw Q.sizeLimitError();$+=_.decode(B,{stream:!0});let R=$.split(`
2
+ `);$=R.pop()??"";for(let w of R)yield w}if($+=_.decode(),$.length>0)yield $}finally{if(!H)try{await Y.cancel()}catch{}Y.releaseLock()}}function q(F){let Q=[];for(let Y=0;Y<F.length;Y+=512)Q.push(String.fromCharCode(...F.subarray(Y,Y+512)));return btoa(Q.join(""))}function C(F){let Q=atob(F),Y=new Uint8Array(Q.length);for(let _=0;_<Q.length;_+=1)Y[_]=Q.charCodeAt(_);return Y}function D(F){return typeof F==="object"&&F!==null&&!Array.isArray(F)}async function L(F,Q){return await F.get(Q)!==null}async function*G(F,Q,Y){for await(let[_]of F.scan(Q,Y))yield _}async function U(F,Q){let Y=0;for await(let _ of G(F,Q))Y++;return Y}async function E(F,Q){let Y=[];for await(let _ of G(F,Q))Y.push({type:"delete",key:_});if(Y.length===0)return 0;return await F.batch(Y),Y.length}async function N(F,Q,Y){let _=[];for await(let $ of G(F,Q,Y))_.push({type:"delete",key:$});if(_.length===0)return 0;return await F.batch(_),_.length}function P(F,Q,Y){if(!F.capabilities()[Q])throw Error(`Feature "${Y}" requires storage capability "${Q}", but this storage backend does not provide it.`)}var K="default";function Z(F){return encodeURIComponent(F)}var O={workflowOwnerEpoch:(F)=>`wf-owner-epoch:${Z(F)}`,workflowOwnerHolder:(F)=>`wf-owner-holder:${Z(F)}`,ownershipModeMarker:()=>"ownership-mode-marker"};var W=1e4;class b extends Error{code="StorageBatchOperationLimitExceededError";cap=W;count;target;constructor(F,Q){super(`${F} count ${Q} exceeds MAX_BATCH_OPERATIONS (${W}).`);this.name="StorageBatchOperationLimitExceededError",this.target=F,this.count=Q}}function X(F,Q){if(Q>W)throw new b(F,Q)}function v(F){return F.length>0?F.slice(0,-1)+String.fromCharCode(F.charCodeAt(F.length-1)+1):"ÿ"}function LF(F,Q={}){if(Q.gt!==void 0&&F<=Q.gt)return!1;if(Q.gte!==void 0&&F<Q.gte)return!1;if(Q.lt!==void 0&&F>=Q.lt)return!1;if(Q.lte!==void 0&&F>Q.lte)return!1;return!0}function UF(F,Q){if(F===null||Q===null)return F===Q;if(F.byteLength!==Q.byteLength)return!1;for(let Y=0;Y<F.byteLength;Y++)if(F[Y]!==Q[Y])return!1;return!0}async function h(F,Q){if(F.has)return F.has(Q);return L(F,Q)}function m(F,Q,Y){if(F.keys)return F.keys(Q,Y);return G(F,Q,Y)}async function x(F,Q){if(F.count)return F.count(Q);return U(F,Q)}async function y(F,Q){if(F.deletePrefix)return F.deletePrefix(Q);return E(F,Q)}async function c(F,Q,Y){if(X("conditionalBatch conditions",Q.length),X("conditionalBatch operations",Y.length),P(F,"conditionalBatch","storageConditionalBatch"),!F.conditionalBatch)throw Error("This storage backend reports conditionalBatch capability but does not implement the conditionalBatch() method.");return F.conditionalBatch(Q,Y)}var J=(F)=>String(F).padStart(16,"0"),f="0",l="1",S=(F,Q,Y,_)=>`sig:${Z(F)}:${Z(Q)}:${_}:${Z(Y)}`,EF={workflow:(F)=>`wf:${Z(F)}`,checkpoint:(F)=>`wf:${Z(F)}:ckpt`,checkpointHistory:(F,Q)=>`wf:${Z(F)}:ckpt:${String(Q).padStart(10,"0")}`,timelinePrefix:(F)=>`wf:${Z(F)}:timeline:`,timeline:(F,Q)=>`wf:${Z(F)}:timeline:${String(Q).padStart(10,"0")}`,schedule:(F)=>`schedule:${Z(F)}`,scheduleTick:(F,Q)=>`schedule-due:${String(F).padStart(16,"0")}:${Z(Q)}`,scheduleRun:(F)=>`schedule-run:${Z(F)}`,scheduleRunLink:(F)=>`schedule-run-link:${Z(F)}`,scheduleRunBySchedulePrefix:(F)=>`schedule-run-by-schedule:${Z(F)}:`,scheduleRunBySchedule:(F,Q)=>`schedule-run-by-schedule:${Z(F)}:${Z(Q)}`,operation:(F,Q,Y)=>`op:${F}:${J(Q)}:${Y}`,operationInflight:(F)=>`op:inflight:${F}`,operationQueued:(F)=>`op:queued:${F}`,operationResolved:(F)=>`op:resolved:${F}`,bulkOperationAuditPrefix:()=>"audit:bulk:",bulkOperationAudit:(F,Q,Y)=>`audit:bulk:${J(F)}:${Z(Q)}:${Z(Y)}`,operationResolvedByTimePrefix:()=>"op:resolved-by-time:",operationResolvedByTime:(F,Q)=>`op:resolved-by-time:${J(F)}:${Z(Q)}`,asyncActivity:(F,Q)=>`async-act:v1:${Z(F)}:${Z(Q)}`,asyncActivityResolution:(F,Q)=>`async-act:v1:${Z(F)}:${Z(Q)}:resolution`,activityReconciliationPrefix:(F)=>`actrec:v1:${Z(F)}:`,activityReconciliation:(F,Q,Y)=>`actrec:v1:${Z(F)}:${Z(Q)}:${Y}`,eventPrefix:(F)=>`ev:${Z(F)}:`,event:(F,Q)=>`ev:${Z(F)}:${String(Q).padStart(10,"0")}`,eventHead:(F)=>`ev:${Z(F)}:head`,eventWatermark:(F)=>`ev:${Z(F)}:watermark`,fleetEventPrefix:()=>"fleet-event:",fleetEvent:(F)=>`fleet-event:${String(F).padStart(10,"0")}`,fleetEventTail:()=>"fleet-event-tail",fleetEventByWorkflowPrefix:(F)=>`fleet-event-by-workflow:${Z(F)}:`,fleetEventByWorkflow:(F,Q)=>`fleet-event-by-workflow:${Z(F)}:${String(Q).padStart(10,"0")}`,signal:(F,Q,Y)=>S(F,Q,Y,l),startSignal:(F,Q,Y)=>S(F,Q,Y,f),signalSequence:(F)=>`sigseq:v1:${Z(F)}`,signalAcceptedResponsePrefix:(F)=>`sigres:v1:${Z(F)}:`,signalAcceptedResponse:(F,Q,Y)=>`sigres:v1:${Z(F)}:${Z(Q)}:${Z(Y)}`,deadline:(F,Q)=>`wf-deadline:${J(F)}:${Z(Q)}`,terminalCleanup:(F,Q)=>`wf-cleanup:${J(F)}:${Z(Q)}`,teardownTimer:(F,Q)=>`wf-teardown:${J(F)}:${Z(Q)}`,delayedStart:(F,Q)=>`wf-delayed:${J(F)}:${Z(Q)}`,terminalWorkflowPrefix:()=>"wf-terminal:",terminalWorkflow:(F,Q)=>`wf-terminal:${J(F)}:${Z(Q)}`,attribute:(F)=>`attr:${Z(F)}`,attributeIndex:(F,Q,Y)=>`idx:${F}:${Q}:${Z(Y)}`,tagIndex:(F,Q)=>`tag:${Z(F)}:${Z(Q)}`,updatePrefix:(F)=>`upd:${Z(F)}:`,update:(F,Q)=>`upd:${Z(F)}:${Q}`,updateResponse:(F)=>`upr:${F}`,updateIdempotency:(F,Q)=>`upk:${Z(F)}:${Q}`,startIdempotency:(F)=>`start-idem:${Z(F)}`,startIdempotencySignalId:(F)=>`start-idem:${F}`,livenessPrefix:()=>"liveness:",liveness:(F)=>`liveness:${Z(F)}`,leasePrefix:()=>"lease:",leaseEpoch:()=>"lease:epoch",leaseHolder:()=>"lease:holder",...O,budget:(F,Q,Y)=>`budget:${F}:${Q}:${Y}`,review:(F,Q)=>`review:${Z(F)}:${Q}`,workflowHeaders:(F)=>`wf-headers:${Z(F)}`,childCancellationPrefix:(F)=>`child-cancel:${Z(F)}:`,childCancellation:(F,Q)=>`child-cancel:${Z(F)}:${Z(Q)}`,childWorkflowByParentPrefix:(F,Q)=>`child-by-parent:${Z(F)}:${Q===void 0?"":`${Z(Q)}:`}`,childWorkflowByParent:(F,Q,Y)=>`child-by-parent:${Z(F)}:${Q===void 0?"":`${Z(Q)}:`}${Z(Y)}`,terminalCleanupNeeded:(F)=>`wf-cleanup-needed:${Z(F)}`,workflowConcurrency:(F,Q)=>`wf-concurrency:${Z(F)}:${Z(Q)}`,workflowConcurrencyHolder:(F)=>`wf-concurrency-holder:${Z(F)}`,workflowHasServices:(F)=>`wf-has-services:${Z(F)}`,finalizerState:(F)=>`wf-finalizer-state:${Z(F)}`,teardownOwed:(F)=>`wf-teardown-needed:${Z(F)}`,teardownSucceeded:(F)=>`wf-teardown-succeeded:${Z(F)}`,teardownDeadLetter:(F)=>`wf-teardown-deadletter:${Z(F)}`,offload:(F,Q)=>`offload:${Z(F)}:${Q}`,archive:(F,Q)=>`archive:${Z(F)}:${Q}`,stateExecution:(F,Q)=>`state:execution:${Z(F)}:${Z(Q)}`,stateWorkflow:(F,Q)=>`state:workflow-scope:${K}:${Z(F)}:${Z(Q)}`,streamChunkPrefix:(F,Q)=>`blob:${Z(F)}:${Q}:chunk:`,streamChunk:(F,Q,Y)=>`blob:${Z(F)}:${Q}:chunk:${String(Y).padStart(10,"0")}`,streamTail:(F,Q)=>`blob:${Z(F)}:${Q}:tail`,streamMetadata:(F,Q)=>`blob:${Z(F)}:${Q}:meta`,budgetCharged:(F)=>`budget-charged:${F}`,toolEffect:(F,Q,Y)=>`tool-effect:${Z(F)}:${Q}:${Y}`,workflowVisibilityStatus:(F,Q)=>`wf-idx-status:${Z(F)}:${Z(Q)}`,workflowVisibilityType:(F,Q)=>`wf-idx-type:${Z(F)}:${Z(Q)}`,workflowVisibilityCreated:(F,Q)=>`wf-idx-created:${J(F)}:${Z(Q)}`,workflowVisibilityUpdated:(F,Q)=>`wf-idx-updated:${J(F)}:${Z(Q)}`,workflowVisibilityDeadline:(F,Q)=>`wf-idx-deadline:${J(F)}:${Z(Q)}`,workflowVisibilityManifest:(F)=>`wf-idx-manifest:${Z(F)}`,workflowVisibilityMetaVersion:()=>"wf-idx-meta:version",workflowVisibilityMetaBuiltAt:()=>"wf-idx-meta:built-at",workflowVisibilityMetaCursor:()=>"wf-idx-meta:cursor"};function d(F){if(F===void 0)return;if(typeof F!=="number"||!Number.isInteger(F)||F<0)throw Error("deleteRange limit must be a finite non-negative integer");return F===0?0:F}function j(F){let Q={},Y=!1;for(let $ of["gt","gte","lt","lte"]){let M=F[$];if(M===void 0)continue;if(typeof M!=="string")throw Error("deleteRange bounds must be strings");Q[$]=M,Y=!0}if(!Y)throw Error("deleteRange requires at least one of gt/gte/lt/lte; use deletePrefix to delete a whole prefix");let _=d(F.limit);if(_!==void 0)Q.limit=_;return Q}async function I(F,Q,Y){let _=j(Y);if(F.deleteRange)return F.deleteRange(Q,_);return N(F,Q,_)}function p(F,Q){let Y={key:F,open:!1};if(Q.gte!==void 0&&Q.gte>Y.key)Y.key=Q.gte,Y.open=!1;if(Q.gt!==void 0&&Q.gt>=Y.key)Y.key=Q.gt,Y.open=!0;return Y}function a(F,Q){let Y={key:v(F),open:!0};if(Q.lt!==void 0&&Q.lt<=Y.key)Y.key=Q.lt,Y.open=!0;if(Q.lte!==void 0&&Q.lte<Y.key)Y.key=Q.lte,Y.open=!1;return Y}function WF(F,Q){let Y=p(F,Q),_=a(F,Q);if(Y.key>_.key||Y.key===_.key&&(Y.open||_.open))return null;return{lower:Y,upper:_}}function z(F){return F.replaceAll(/:+$/g,"")}function s(F,Q){let Y=z(F),_=z(Q);if(Y.length===0)return _;if(_.length===0)return Y;return`${Y}:${_}`}class A{#F;#Y;constructor(F,Q){this.#F=F,this.#Y=z(Q)}#Q(F){if(this.#Y.length===0)return F;return F.length===0?`${this.#Y}:`:`${this.#Y}:${F}`}#_(F){return F.map((Q)=>{if(Q.type==="put")return{type:"put",key:this.#Q(Q.key),value:Q.value};return{type:"delete",key:this.#Q(Q.key)}})}#$(F){if(this.#Y.length===0)return F;return F.slice(this.#Y.length+1)}#J(F){let Q={};if(F.limit!==void 0)Q.limit=F.limit;if(F.gt!==void 0)Q.gt=this.#Q(F.gt);if(F.gte!==void 0)Q.gte=this.#Q(F.gte);if(F.lt!==void 0)Q.lt=this.#Q(F.lt);if(F.lte!==void 0)Q.lte=this.#Q(F.lte);return Q}#Z(F={}){let Q=this.#J(F);if(F.reverse!==void 0)Q.reverse=F.reverse;return Q}#M(F){return this.#J(F)}capabilities(){return this.#F.capabilities()}scoped(F){return new A(this.#F,s(this.#Y,F))}async get(F){return this.#F.get(this.#Q(F))}async put(F,Q){await this.#F.put(this.#Q(F),Q)}async delete(F){await this.#F.delete(this.#Q(F))}async*scan(F,Q){for await(let[Y,_]of this.#F.scan(this.#Q(F),this.#Z(Q)))yield[this.#$(Y),_]}async batch(F){X("batch operations",F.length),await this.#F.batch(this.#_(F))}async conditionalBatch(F,Q){return c(this.#F,F.map((Y)=>({key:this.#Q(Y.key),expectedValue:Y.expectedValue})),this.#_(Q))}async has(F){return h(this.#F,this.#Q(F))}async deletePrefix(F){return y(this.#F,this.#Q(F))}async deleteRange(F,Q){let Y=this.#M(j(Q));return I(this.#F,this.#Q(F),Y)}async*keys(F,Q){for await(let Y of m(this.#F,this.#Q(F),this.#Z(Q)))yield this.#$(Y)}async count(F){return x(this.#F,this.#Q(F))}[Symbol.dispose](){this.#F[Symbol.dispose]()}}function g(F,Q){return new A(F,Q)}var n=67108864;function u(F){if(F.type==="put")return{type:"put",key:F.key,value:q(F.value)};return{type:"delete",key:F.key}}function i(F){return{key:F.key,expectedValue:F.expectedValue===null?null:q(F.expectedValue)}}function t(F){if(!D(F)||typeof F.key!=="string"||typeof F.value!=="string")throw Error("HTTPStorage scan response contained an invalid NDJSON entry.");return{key:F.key,value:F.value}}function r(F){if(!D(F)||typeof F.applied!=="boolean")throw Error('HTTPStorage conditional batch response must include a boolean "applied" field.');return F.applied}function V(F,Q,Y){if(Y!==void 0)F.searchParams.set(Q,String(Y))}function o(F){if(F.trim().length===0)return null;let Q=t(JSON.parse(F));return[Q.key,C(Q.value)]}class e{#F;#Y;#Q;constructor(F){this.#F=F.baseUrl instanceof URL?F.baseUrl:new URL(F.baseUrl),this.#Y={...F.headers},this.#Q=F.remoteConditionalBatch??!1}capabilities(){return{persistence:"remote",readAfterWrite:"eventual",scanConsistency:"best-effort",atomicBatch:!0,conditionalBatch:this.#Q,boundedRangeDelete:!1}}#_(F){let Q=this.#F.href.endsWith("/")?this.#F.href:`${this.#F.href}/`;return new URL(F.replace(/^\/+/,""),Q)}#$(F){return this.#_(`/v1/storage/${encodeURIComponent(F)}`)}#J(F,Q){let Y=this.#_("/v1/storage");return Y.searchParams.set("prefix",F),V(Y,"limit",Q.limit),V(Y,"reverse",Q.reverse),V(Y,"gt",Q.gt),V(Y,"gte",Q.gte),V(Y,"lt",Q.lt),V(Y,"lte",Q.lte),Y}async#Z(F,Q={},Y=[]){let _=new Headers(this.#Y);for(let[M,H]of new Headers(Q.headers).entries())_.set(M,H);let $=await fetch(F,{...Q,headers:_});if(!$.ok&&!Y.includes($.status))throw Error(`HTTPStorage request failed: ${Q.method??"GET"} ${F.pathname} returned ${String($.status)}.`);return $}async get(F){let Q=await this.#Z(this.#$(F),{method:"GET"},[404]);if(Q.status===404)return null;return new Uint8Array(await Q.arrayBuffer())}async put(F,Q){await this.#Z(this.#$(F),{method:"PUT",headers:{"content-type":"application/octet-stream"},body:new Blob([Q])})}async delete(F){await this.#Z(this.#$(F),{method:"DELETE"})}async*scan(F,Q={}){let Y=await this.#Z(this.#J(F,Q),{method:"GET",headers:{accept:"application/x-ndjson"}});for await(let _ of T(Y,{maximumBytes:n,sizeLimitError:()=>Error("HTTPStorage scan response exceeded the maximum allowed size.")})){let $=o(_);if($!==null)yield $}}async batch(F){X("batch operations",F.length),await this.#Z(this.#_("/v1/storage/-/batch"),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({operations:F.map(u)})})}async conditionalBatch(F,Q){X("conditionalBatch conditions",F.length),X("conditionalBatch operations",Q.length);let Y=await this.#Z(this.#_("/v1/storage/-/conditional-batch"),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({conditions:F.map(i),operations:Q.map(u)})});return r(await Y.json())}has(F){return L(this,F)}async*keys(F,Q){yield*G(this,F,Q)}count(F){return U(this,F)}deletePrefix(F){return E(this,F)}deleteRange(F,Q){return N(this,F,j(Q))}scoped(F){return g(this,F)}[Symbol.dispose](){}}export{e as HTTPStorage};
package/dist/index.d.ts CHANGED
@@ -22,7 +22,8 @@ export { FAULT_CODE_TO_FAILURE_CATEGORY, failureCategoryForFaultCode, isFaultCod
22
22
  export type { FaultCode } from './core/fault-code.ts';
23
23
  export { DurableActivityScopeError, DurableActivityUnsupportedError, durableActivity, } from './core/context/durable-activity.ts';
24
24
  export { ActivityReconciliationCapabilityError, ActivityReconciliationConflictError, ActivityReconciliationIndeterminateError, ActivityResolutionError, AsyncActivityTokenNotFoundError, BulkDeleteRequiresTerminalWorkflowsError, BulkOperationConfirmationError, ENGINE_LEASE_LOST_WARNING_NAME, ENGINE_LEASE_SYNCHRONOUS_DISPOSE_WARNING_NAME, Engine, EngineCreateNameMismatchError, EngineDisposalError, EngineDisposedError, EngineLeaseAcquisitionTimeoutError, EngineLeaseCorruptedError, EngineLeaseNotHeldError, IdempotencyKeyPurgedError, PersistedDataIncompatibleError, ScheduleHandle, StartOrSignalConflictError, WorkflowAlreadyExistsError, WorkflowConcurrencyLimitExceededError, WorkflowHandle, WorkflowNotFoundError, WorkflowNotRegisteredError, WorkflowSuspendNotSupportedError, WorkflowTeardownPendingError, WorkflowTypeNotRegisteredForRecoveryError, } from './core/engine';
25
- export type { EngineCreateOptions, EngineLeaseHealth, EngineStateNamespace, LeaseLostReason, RecoverAllOptions, RecoveredWorkflowInfo, } from './core/engine';
25
+ export { OwnershipModeMismatchError, WORKFLOW_CLAIM_LOST_WARNING_NAME, WORKFLOW_WAKE_DISCARDED_WARNING_NAME, WeftWorkflowClaimLostWarning, WeftWorkflowWakeDiscardedWarning, WorkflowClaimUnavailableError, } from './core/engine';
26
+ export type { EngineCreateOptions, EngineLeaseHealth, EngineStateNamespace, LeaseLostReason, RecoverAllOptions, RecoveredWorkflowInfo, WorkflowWakeKind, } from './core/engine';
26
27
  export { DEFAULT_CHECKPOINT_SIZE_WARNING_THRESHOLD, DEFAULT_MAX_NESTING_DEPTH, DEFAULT_POLL_INTERVAL_MS, DEFAULT_RETRY_POLICY, DEFAULT_VISIBILITY_TIMEOUT_MS, HISTORY_CIRCUIT_BREAKER_REASON, WorkflowBuilderError, query, schedule, signal, update, workflow, } from './core/types';
27
28
  export type { ActivityArgsFor, ActivityCallOptions, ActivityCallable, ActivityContext, ActivityDefinition, ActivityEntryInput, ActivityFunction, ActivityMap, ActivityMapInput, ActivityObjectInput, ActivityResultFor, ActivityVerificationContext, ActivityVerificationPhase, ActivityVerificationResult, AnyActivityDefinition, AnyWorkflowDefinition, ArchiveAdapter, BuilderState, BuiltWorkflowDefinition, BulkCancelResult, BulkDeleteResult, BulkOperationAction, BulkOperationAuditEvent, BulkOperationCommitOptions, BulkOperationDryRunOptions, BulkOperationDryRunResult, BulkOperationError, BulkOperationFilterSummary, BulkOperationOptions, BulkOperationPrincipal, BulkOperationScopeSummary, BulkRetryFailedResult, BulkSignalAllCommitOptions, BulkSignalAllDryRunOptions, BulkSignalAllOptions, BulkSignalResult, BulkTagResult, Checkpoint, CheckpointState, CheckpointSummary, CompletedReviewEntry, CoordinatedUpdateResult, DefinitionSchema, Duration, EngineOptions, FailureCategory, ForkLineage, ForkOptions, HistoryPolicy, InferActivityEntries, InferActivityEntry, InferWorkflowEntries, InferWorkflowEntry, InitialBuilderState, LaunchMetadata, ListFilter, ListOptions, MarkBuilderState, NormalizeActivities, NormalizedActivityEntry, NormalizedRetentionPolicy, PaginatedResult, PayloadSizePolicy, PendingAsyncActivityInfo, PendingAsyncActivityListOptions, PendingAsyncActivityPage, PendingReviewEntry, PurgeResult, QueryDefinition, QueryMap, QueryShape, RegisteredWorkflowDefinition, RestartLineage, RetentionOverview, RetentionPolicy, RetryPolicy, ReviewDecision, ReviewListEntry, ReviewListFilter, ReviewStatus, ScheduleDefinition, ScheduleFilter, ScheduleOptions, ScheduleOverlapPolicy, ScheduleQueuedRun, ScheduleSpec, ScheduleState, ScheduleStatus, ScheduleSummary, ScheduleUpdateOptions, SearchAttributeDefinition, SearchAttributeHandle, SearchAttributeSchema, SearchAttributeValue, Serializer, SignalDefinition, SignalDeliveryOptions, SignalMap, SignalPayload, StartOptions, StartOrSignalOptions, StartOrSignalSignal, SubmitReviewOptions, TerminationReason, UpdateDefinition, UpdateMap, UpdatePayload, WorkerReplayOperationFailure, WorkerReplayOperationSignature, WorkflowAlreadyRegistered, WorkflowAtomicState, WorkflowAtomicStateOptions, WorkflowBuilder, WorkflowBuilderOptions, WorkflowConcurrencyOptions, WorkflowContext, WorkflowDefinition, WorkflowEvent, WorkflowFinalizerStatus, WorkflowFunction, WorkflowGenerator, WorkflowId, WorkflowKeyedRaceResult, WorkflowLogLevel, WorkflowLogRecord, WorkflowLogger, WorkflowRegistry, WorkflowReplay, WorkflowScheduleProvenance, WorkflowServicesResolution, WorkflowServicesResolverInfo, WorkflowServicesResolverLaunchOptions, WorkflowServicesResolverScheduleInfo, WorkflowSessionState, WorkflowSnapshot, WorkflowState, WorkflowStateNamespace, WorkflowStatus, WorkflowSummary, WorkflowTimelineEntry, WorkflowTimelineOperationDetail, WorkflowTimelineStatus, WorkflowTypeRetentionPolicy, } from './core/types';
28
29
  export { AlertManager } from './alerting/index';
package/dist/index.js CHANGED
@@ -46,6 +46,14 @@ export {
46
46
  WorkflowTeardownPendingError,
47
47
  WorkflowTypeNotRegisteredForRecoveryError
48
48
  } from "./core/engine.js";
49
+ export {
50
+ OwnershipModeMismatchError,
51
+ WORKFLOW_CLAIM_LOST_WARNING_NAME,
52
+ WORKFLOW_WAKE_DISCARDED_WARNING_NAME,
53
+ WeftWorkflowClaimLostWarning,
54
+ WeftWorkflowWakeDiscardedWarning,
55
+ WorkflowClaimUnavailableError
56
+ } from "./core/engine.js";
49
57
  export {
50
58
  DEFAULT_CHECKPOINT_SIZE_WARNING_THRESHOLD,
51
59
  DEFAULT_MAX_NESTING_DEPTH,
package/dist/indexeddb.js CHANGED
@@ -1 +1 @@
1
- async function O(G,J){return await G.get(J)!==null}async function*T(G,J,Q){for await(let[X]of G.scan(J,Q))yield X}async function R(G,J){let Q=0;for await(let X of T(G,J))Q++;return Q}async function h(G,J){let Q=[];for await(let X of T(G,J))Q.push({type:"delete",key:X});if(Q.length===0)return 0;return await G.batch(Q),Q.length}async function v(G,J,Q){let X=[];for await(let Y of T(G,J,Q))X.push({type:"delete",key:Y});if(X.length===0)return 0;return await G.batch(X),X.length}function E(G,J,Q){if(!G.capabilities()[J])throw Error(`Feature "${Q}" requires storage capability "${J}", but this storage backend does not provide it.`)}var P=1e4;class b extends Error{code="StorageBatchOperationLimitExceededError";cap=P;count;target;constructor(G,J){super(`${G} count ${J} exceeds MAX_BATCH_OPERATIONS (${P}).`);this.name="StorageBatchOperationLimitExceededError",this.target=G,this.count=J}}function W(G,J){if(J>P)throw new b(G,J)}function M(G){return G.length>0?G.slice(0,-1)+String.fromCharCode(G.charCodeAt(G.length-1)+1):"ÿ"}function m(G,J={}){if(J.gt!==void 0&&G<=J.gt)return!1;if(J.gte!==void 0&&G<J.gte)return!1;if(J.lt!==void 0&&G>=J.lt)return!1;if(J.lte!==void 0&&G>J.lte)return!1;return!0}function x(G,J){if(G===null||J===null)return G===J;if(G.byteLength!==J.byteLength)return!1;for(let Q=0;Q<G.byteLength;Q++)if(G[Q]!==J[Q])return!1;return!0}async function y(G,J){if(G.has)return G.has(J);return O(G,J)}function I(G,J,Q){if(G.keys)return G.keys(J,Q);return T(G,J,Q)}async function g(G,J){if(G.count)return G.count(J);return R(G,J)}async function k(G,J){if(G.deletePrefix)return G.deletePrefix(J);return h(G,J)}async function c(G,J,Q){if(W("conditionalBatch conditions",J.length),W("conditionalBatch operations",Q.length),E(G,"conditionalBatch","storageConditionalBatch"),!G.conditionalBatch)throw Error("This storage backend reports conditionalBatch capability but does not implement the conditionalBatch() method.");return G.conditionalBatch(J,Q)}function d(G){if(G===void 0)return;if(typeof G!=="number"||!Number.isInteger(G)||G<0)throw Error("deleteRange limit must be a finite non-negative integer");return G===0?0:G}function A(G){let J={},Q=!1;for(let Y of["gt","gte","lt","lte"]){let Z=G[Y];if(Z===void 0)continue;if(typeof Z!=="string")throw Error("deleteRange bounds must be strings");J[Y]=Z,Q=!0}if(!Q)throw Error("deleteRange requires at least one of gt/gte/lt/lte; use deletePrefix to delete a whole prefix");let X=d(G.limit);if(X!==void 0)J.limit=X;return J}async function u(G,J,Q){let X=A(Q);if(G.deleteRange)return G.deleteRange(J,X);return v(G,J,X)}function a(G,J){let Q={key:G,open:!1};if(J.gte!==void 0&&J.gte>Q.key)Q.key=J.gte,Q.open=!1;if(J.gt!==void 0&&J.gt>=Q.key)Q.key=J.gt,Q.open=!0;return Q}function s(G,J){let Q={key:M(G),open:!0};if(J.lt!==void 0&&J.lt<=Q.key)Q.key=J.lt,Q.open=!0;if(J.lte!==void 0&&J.lte<Q.key)Q.key=J.lte,Q.open=!1;return Q}function w(G,J){let Q=a(G,J),X=s(G,J);if(Q.key>X.key||Q.key===X.key&&(Q.open||X.open))return null;return{lower:Q,upper:X}}function q(G){return G.replaceAll(/:+$/g,"")}function n(G,J){let Q=q(G),X=q(J);if(Q.length===0)return X;if(X.length===0)return Q;return`${Q}:${X}`}class C{#J;#Q;constructor(G,J){this.#J=G,this.#Q=q(J)}#G(G){if(this.#Q.length===0)return G;return G.length===0?`${this.#Q}:`:`${this.#Q}:${G}`}#X(G){return G.map((J)=>{if(J.type==="put")return{type:"put",key:this.#G(J.key),value:J.value};return{type:"delete",key:this.#G(J.key)}})}#Y(G){if(this.#Q.length===0)return G;return G.slice(this.#Q.length+1)}#Z(G){let J={};if(G.limit!==void 0)J.limit=G.limit;if(G.gt!==void 0)J.gt=this.#G(G.gt);if(G.gte!==void 0)J.gte=this.#G(G.gte);if(G.lt!==void 0)J.lt=this.#G(G.lt);if(G.lte!==void 0)J.lte=this.#G(G.lte);return J}#$(G={}){let J=this.#Z(G);if(G.reverse!==void 0)J.reverse=G.reverse;return J}#U(G){return this.#Z(G)}capabilities(){return this.#J.capabilities()}scoped(G){return new C(this.#J,n(this.#Q,G))}async get(G){return this.#J.get(this.#G(G))}async put(G,J){await this.#J.put(this.#G(G),J)}async delete(G){await this.#J.delete(this.#G(G))}async*scan(G,J){for await(let[Q,X]of this.#J.scan(this.#G(G),this.#$(J)))yield[this.#Y(Q),X]}async batch(G){W("batch operations",G.length),await this.#J.batch(this.#X(G))}async conditionalBatch(G,J){return c(this.#J,G.map((Q)=>({key:this.#G(Q.key),expectedValue:Q.expectedValue})),this.#X(J))}async has(G){return y(this.#J,this.#G(G))}async deletePrefix(G){return k(this.#J,this.#G(G))}async deleteRange(G,J){let Q=this.#U(A(J));return u(this.#J,this.#G(G),Q)}async*keys(G,J){for await(let Q of I(this.#J,this.#G(G),this.#$(J)))yield this.#Y(Q)}async count(G){return g(this.#J,this.#G(G))}[Symbol.dispose](){this.#J[Symbol.dispose]()}}function f(G,J){return new C(G,J)}var U="kv";function i(G=globalThis){let{indexedDB:J,IDBKeyRange:Q}=G;if(J===void 0||Q===void 0)throw Error("IndexedDBStorage requires both indexedDB and IDBKeyRange runtime globals.");return{indexedDB:J,IDBKeyRange:Q}}function K(G){return new Promise((J,Q)=>{G.onsuccess=()=>J(G.result),G.onerror=()=>Q(G.error)})}function t(G,J){let Q=null,X=null,Y=null,Z=($,H)=>{let F=$??Error(H);if(X){let L=X;Q=null,X=null,L(F);return}Y=F};return G.onsuccess=()=>{if(!Q)return;let $=Q;Q=null,X=null,$(G.result)},G.onerror=()=>{Z(G.error,"IndexedDB cursor request failed.")},J.onerror=()=>{Z(J.error,"IndexedDB transaction failed.")},J.onabort=()=>{Z(J.error,"IndexedDB transaction aborted.")},()=>{return new Promise(($,H)=>{if(Y){let F=Y;Y=null,H(F);return}if(Q=$,X=H,G.readyState==="done"){let F=Q;Q=null,X=null,F?.(G.result)}})}}async function*l(G,J,Q,X,Y,Z){let{limit:$,reverse:H}=X,F=M(Q),L=J.bound(Q,F,!1,!0),z=H?"prev":"next",B=G.transaction(U,"readonly"),V=B.objectStore(U),D=Y(V,L,z),_=t(D,B),j=0,S=!1;try{let N=await _();while(N){if($!==void 0&&j>=$)break;let p=N.key;if(m(p,X))yield Z(N),j++;N.continue(),N=await _()}S=!0}finally{if(!S)try{B.abort()}catch{}}}class r{#J;#Q=null;#G;#X;constructor(G="weft",J=i()){this.#J=G,this.#X=J,this.#G=this.#Y()}capabilities(){return{persistence:"local",readAfterWrite:"linearizable",scanConsistency:"best-effort",atomicBatch:!0,conditionalBatch:!0,boundedRangeDelete:!0}}#Y(){let G=this.#X.indexedDB.open(this.#J,1);return G.onupgradeneeded=()=>{let J=G.result;if(!J.objectStoreNames.contains(U))J.createObjectStore(U)},K(G).then((J)=>{return this.#Q=J,J})}async get(G){let X=(await this.#G).transaction(U,"readonly").objectStore(U),Y=await K(X.get(G));return Y===void 0?null:new Uint8Array(Y)}async put(G,J){let Y=(await this.#G).transaction(U,"readwrite").objectStore(U);await K(Y.put(J,G))}async delete(G){let X=(await this.#G).transaction(U,"readwrite").objectStore(U);await K(X.delete(G))}async has(G){let X=(await this.#G).transaction(U,"readonly").objectStore(U);return await K(X.count(G))>0}async deletePrefix(G){let J=await this.#G,Q=M(G),X=this.#X.IDBKeyRange.bound(G,Q,!1,!0);return new Promise((Y,Z)=>{let $=J.transaction(U,"readwrite"),H=$.objectStore(U),F=0,L=H.count(X);L.onsuccess=()=>{F=L.result,H.delete(X)},$.oncomplete=()=>Y(F),$.onerror=()=>Z($.error)})}async deleteRange(G,J){let Q=A(J),X=w(G,Q);if(X===null)return 0;let Y=this.#X.IDBKeyRange.bound(X.lower.key,X.upper.key,X.lower.open,X.upper.open),Z=await this.#G,{limit:$}=Q;return new Promise((H,F)=>{let L=Z.transaction(U,"readwrite"),z=L.objectStore(U),B=0;if($===void 0){let V=z.count(Y);V.onsuccess=()=>{B=V.result,z.delete(Y)}}else{let V=z.openCursor(Y,"next");V.onsuccess=()=>{let D=V.result;if(D===null||B>=$)return;if(D.delete(),B++,B<$)D.continue()}}L.oncomplete=()=>H(B),L.onerror=()=>F(L.error)})}async*scan(G,J={}){let Q=await this.#G;yield*l(Q,this.#X.IDBKeyRange,G,J,(X,Y,Z)=>X.openCursor(Y,Z),(X)=>[X.key,new Uint8Array(X.value)])}async batch(G){if(W("batch operations",G.length),G.length===0)return;let J=await this.#G;return new Promise((Q,X)=>{let Y=J.transaction(U,"readwrite"),Z=Y.objectStore(U);for(let $ of G)if($.type==="put")Z.put($.value,$.key);else Z.delete($.key);Y.oncomplete=()=>Q(),Y.onerror=()=>X(Y.error)})}async conditionalBatch(G,J){W("conditionalBatch conditions",G.length),W("conditionalBatch operations",J.length);let Q=await this.#G;return new Promise((X,Y)=>{let Z=Q.transaction(U,"readwrite"),$=Z.objectStore(U),H=!1,F=!1,L=(V,D)=>{if(H)return;H=!0,Y(V??Error(D))};Z.oncomplete=()=>{if(H)return;H=!0,X(!0)},Z.onerror=()=>{L(Z.error,"IndexedDB conditionalBatch transaction failed.")},Z.onabort=()=>{if(H)return;if(H=!0,F){X(!1);return}Y(Z.error??Error("IndexedDB conditionalBatch transaction aborted."))};let z=()=>{for(let V of J)if(V.type==="put")$.put(V.value,V.key);else $.delete(V.key)},B=(V)=>{if(V>=G.length){z();return}let D=G[V],_=$.get(D.key);_.onsuccess=()=>{let j=_.result,S=j===void 0?null:new Uint8Array(j);if(!x(S,D.expectedValue)){F=!0,Z.abort();return}B(V+1)},_.onerror=()=>{L(_.error,"IndexedDB conditionalBatch condition check failed.")}};B(0)})}async*keys(G,J={}){let Q=await this.#G;yield*l(Q,this.#X.IDBKeyRange,G,J,(X,Y,Z)=>X.openKeyCursor(Y,Z),(X)=>X.key)}async count(G){let J=await this.#G,Q=M(G),Y=J.transaction(U,"readonly").objectStore(U);return K(Y.count(this.#X.IDBKeyRange.bound(G,Q,!1,!0)))}scoped(G){return f(this,G)}[Symbol.dispose](){if(this.#Q)this.#Q.close(),this.#Q=null}}export{r as IndexedDBStorage};
1
+ async function E(U,Y){return await U.get(Y)!==null}async function*P(U,Y,Q){for await(let[Z]of U.scan(Y,Q))yield Z}async function h(U,Y){let Q=0;for await(let Z of P(U,Y))Q++;return Q}async function v(U,Y){let Q=[];for await(let Z of P(U,Y))Q.push({type:"delete",key:Z});if(Q.length===0)return 0;return await U.batch(Q),Q.length}async function b(U,Y,Q){let Z=[];for await(let $ of P(U,Y,Q))Z.push({type:"delete",key:$});if(Z.length===0)return 0;return await U.batch(Z),Z.length}function m(U,Y,Q){if(!U.capabilities()[Y])throw Error(`Feature "${Q}" requires storage capability "${Y}", but this storage backend does not provide it.`)}var x="default";function X(U){return encodeURIComponent(U)}var y={workflowOwnerEpoch:(U)=>`wf-owner-epoch:${X(U)}`,workflowOwnerHolder:(U)=>`wf-owner-holder:${X(U)}`,ownershipModeMarker:()=>"ownership-mode-marker"};var O=1e4;class g extends Error{code="StorageBatchOperationLimitExceededError";cap=O;count;target;constructor(U,Y){super(`${U} count ${Y} exceeds MAX_BATCH_OPERATIONS (${O}).`);this.name="StorageBatchOperationLimitExceededError",this.target=U,this.count=Y}}function z(U,Y){if(Y>O)throw new g(U,Y)}function j(U){return U.length>0?U.slice(0,-1)+String.fromCharCode(U.charCodeAt(U.length-1)+1):"ÿ"}function k(U,Y={}){if(Y.gt!==void 0&&U<=Y.gt)return!1;if(Y.gte!==void 0&&U<Y.gte)return!1;if(Y.lt!==void 0&&U>=Y.lt)return!1;if(Y.lte!==void 0&&U>Y.lte)return!1;return!0}function c(U,Y){if(U===null||Y===null)return U===Y;if(U.byteLength!==Y.byteLength)return!1;for(let Q=0;Q<U.byteLength;Q++)if(U[Q]!==Y[Q])return!1;return!0}async function u(U,Y){if(U.has)return U.has(Y);return E(U,Y)}function w(U,Y,Q){if(U.keys)return U.keys(Y,Q);return P(U,Y,Q)}async function f(U,Y){if(U.count)return U.count(Y);return h(U,Y)}async function l(U,Y){if(U.deletePrefix)return U.deletePrefix(Y);return v(U,Y)}async function p(U,Y,Q){if(z("conditionalBatch conditions",Y.length),z("conditionalBatch operations",Q.length),m(U,"conditionalBatch","storageConditionalBatch"),!U.conditionalBatch)throw Error("This storage backend reports conditionalBatch capability but does not implement the conditionalBatch() method.");return U.conditionalBatch(Y,Q)}var D=(U)=>String(U).padStart(16,"0"),t="0",r="1",I=(U,Y,Q,Z)=>`sig:${X(U)}:${X(Y)}:${Z}:${X(Q)}`,zU={workflow:(U)=>`wf:${X(U)}`,checkpoint:(U)=>`wf:${X(U)}:ckpt`,checkpointHistory:(U,Y)=>`wf:${X(U)}:ckpt:${String(Y).padStart(10,"0")}`,timelinePrefix:(U)=>`wf:${X(U)}:timeline:`,timeline:(U,Y)=>`wf:${X(U)}:timeline:${String(Y).padStart(10,"0")}`,schedule:(U)=>`schedule:${X(U)}`,scheduleTick:(U,Y)=>`schedule-due:${String(U).padStart(16,"0")}:${X(Y)}`,scheduleRun:(U)=>`schedule-run:${X(U)}`,scheduleRunLink:(U)=>`schedule-run-link:${X(U)}`,scheduleRunBySchedulePrefix:(U)=>`schedule-run-by-schedule:${X(U)}:`,scheduleRunBySchedule:(U,Y)=>`schedule-run-by-schedule:${X(U)}:${X(Y)}`,operation:(U,Y,Q)=>`op:${U}:${D(Y)}:${Q}`,operationInflight:(U)=>`op:inflight:${U}`,operationQueued:(U)=>`op:queued:${U}`,operationResolved:(U)=>`op:resolved:${U}`,bulkOperationAuditPrefix:()=>"audit:bulk:",bulkOperationAudit:(U,Y,Q)=>`audit:bulk:${D(U)}:${X(Y)}:${X(Q)}`,operationResolvedByTimePrefix:()=>"op:resolved-by-time:",operationResolvedByTime:(U,Y)=>`op:resolved-by-time:${D(U)}:${X(Y)}`,asyncActivity:(U,Y)=>`async-act:v1:${X(U)}:${X(Y)}`,asyncActivityResolution:(U,Y)=>`async-act:v1:${X(U)}:${X(Y)}:resolution`,activityReconciliationPrefix:(U)=>`actrec:v1:${X(U)}:`,activityReconciliation:(U,Y,Q)=>`actrec:v1:${X(U)}:${X(Y)}:${Q}`,eventPrefix:(U)=>`ev:${X(U)}:`,event:(U,Y)=>`ev:${X(U)}:${String(Y).padStart(10,"0")}`,eventHead:(U)=>`ev:${X(U)}:head`,eventWatermark:(U)=>`ev:${X(U)}:watermark`,fleetEventPrefix:()=>"fleet-event:",fleetEvent:(U)=>`fleet-event:${String(U).padStart(10,"0")}`,fleetEventTail:()=>"fleet-event-tail",fleetEventByWorkflowPrefix:(U)=>`fleet-event-by-workflow:${X(U)}:`,fleetEventByWorkflow:(U,Y)=>`fleet-event-by-workflow:${X(U)}:${String(Y).padStart(10,"0")}`,signal:(U,Y,Q)=>I(U,Y,Q,r),startSignal:(U,Y,Q)=>I(U,Y,Q,t),signalSequence:(U)=>`sigseq:v1:${X(U)}`,signalAcceptedResponsePrefix:(U)=>`sigres:v1:${X(U)}:`,signalAcceptedResponse:(U,Y,Q)=>`sigres:v1:${X(U)}:${X(Y)}:${X(Q)}`,deadline:(U,Y)=>`wf-deadline:${D(U)}:${X(Y)}`,terminalCleanup:(U,Y)=>`wf-cleanup:${D(U)}:${X(Y)}`,teardownTimer:(U,Y)=>`wf-teardown:${D(U)}:${X(Y)}`,delayedStart:(U,Y)=>`wf-delayed:${D(U)}:${X(Y)}`,terminalWorkflowPrefix:()=>"wf-terminal:",terminalWorkflow:(U,Y)=>`wf-terminal:${D(U)}:${X(Y)}`,attribute:(U)=>`attr:${X(U)}`,attributeIndex:(U,Y,Q)=>`idx:${U}:${Y}:${X(Q)}`,tagIndex:(U,Y)=>`tag:${X(U)}:${X(Y)}`,updatePrefix:(U)=>`upd:${X(U)}:`,update:(U,Y)=>`upd:${X(U)}:${Y}`,updateResponse:(U)=>`upr:${U}`,updateIdempotency:(U,Y)=>`upk:${X(U)}:${Y}`,startIdempotency:(U)=>`start-idem:${X(U)}`,startIdempotencySignalId:(U)=>`start-idem:${U}`,livenessPrefix:()=>"liveness:",liveness:(U)=>`liveness:${X(U)}`,leasePrefix:()=>"lease:",leaseEpoch:()=>"lease:epoch",leaseHolder:()=>"lease:holder",...y,budget:(U,Y,Q)=>`budget:${U}:${Y}:${Q}`,review:(U,Y)=>`review:${X(U)}:${Y}`,workflowHeaders:(U)=>`wf-headers:${X(U)}`,childCancellationPrefix:(U)=>`child-cancel:${X(U)}:`,childCancellation:(U,Y)=>`child-cancel:${X(U)}:${X(Y)}`,childWorkflowByParentPrefix:(U,Y)=>`child-by-parent:${X(U)}:${Y===void 0?"":`${X(Y)}:`}`,childWorkflowByParent:(U,Y,Q)=>`child-by-parent:${X(U)}:${Y===void 0?"":`${X(Y)}:`}${X(Q)}`,terminalCleanupNeeded:(U)=>`wf-cleanup-needed:${X(U)}`,workflowConcurrency:(U,Y)=>`wf-concurrency:${X(U)}:${X(Y)}`,workflowConcurrencyHolder:(U)=>`wf-concurrency-holder:${X(U)}`,workflowHasServices:(U)=>`wf-has-services:${X(U)}`,finalizerState:(U)=>`wf-finalizer-state:${X(U)}`,teardownOwed:(U)=>`wf-teardown-needed:${X(U)}`,teardownSucceeded:(U)=>`wf-teardown-succeeded:${X(U)}`,teardownDeadLetter:(U)=>`wf-teardown-deadletter:${X(U)}`,offload:(U,Y)=>`offload:${X(U)}:${Y}`,archive:(U,Y)=>`archive:${X(U)}:${Y}`,stateExecution:(U,Y)=>`state:execution:${X(U)}:${X(Y)}`,stateWorkflow:(U,Y)=>`state:workflow-scope:${x}:${X(U)}:${X(Y)}`,streamChunkPrefix:(U,Y)=>`blob:${X(U)}:${Y}:chunk:`,streamChunk:(U,Y,Q)=>`blob:${X(U)}:${Y}:chunk:${String(Q).padStart(10,"0")}`,streamTail:(U,Y)=>`blob:${X(U)}:${Y}:tail`,streamMetadata:(U,Y)=>`blob:${X(U)}:${Y}:meta`,budgetCharged:(U)=>`budget-charged:${U}`,toolEffect:(U,Y,Q)=>`tool-effect:${X(U)}:${Y}:${Q}`,workflowVisibilityStatus:(U,Y)=>`wf-idx-status:${X(U)}:${X(Y)}`,workflowVisibilityType:(U,Y)=>`wf-idx-type:${X(U)}:${X(Y)}`,workflowVisibilityCreated:(U,Y)=>`wf-idx-created:${D(U)}:${X(Y)}`,workflowVisibilityUpdated:(U,Y)=>`wf-idx-updated:${D(U)}:${X(Y)}`,workflowVisibilityDeadline:(U,Y)=>`wf-idx-deadline:${D(U)}:${X(Y)}`,workflowVisibilityManifest:(U)=>`wf-idx-manifest:${X(U)}`,workflowVisibilityMetaVersion:()=>"wf-idx-meta:version",workflowVisibilityMetaBuiltAt:()=>"wf-idx-meta:built-at",workflowVisibilityMetaCursor:()=>"wf-idx-meta:cursor"};function o(U){if(U===void 0)return;if(typeof U!=="number"||!Number.isInteger(U)||U<0)throw Error("deleteRange limit must be a finite non-negative integer");return U===0?0:U}function S(U){let Y={},Q=!1;for(let $ of["gt","gte","lt","lte"]){let J=U[$];if(J===void 0)continue;if(typeof J!=="string")throw Error("deleteRange bounds must be strings");Y[$]=J,Q=!0}if(!Q)throw Error("deleteRange requires at least one of gt/gte/lt/lte; use deletePrefix to delete a whole prefix");let Z=o(U.limit);if(Z!==void 0)Y.limit=Z;return Y}async function d(U,Y,Q){let Z=S(Q);if(U.deleteRange)return U.deleteRange(Y,Z);return b(U,Y,Z)}function e(U,Y){let Q={key:U,open:!1};if(Y.gte!==void 0&&Y.gte>Q.key)Q.key=Y.gte,Q.open=!1;if(Y.gt!==void 0&&Y.gt>=Q.key)Q.key=Y.gt,Q.open=!0;return Q}function UU(U,Y){let Q={key:j(U),open:!0};if(Y.lt!==void 0&&Y.lt<=Q.key)Q.key=Y.lt,Q.open=!0;if(Y.lte!==void 0&&Y.lte<Q.key)Q.key=Y.lte,Q.open=!1;return Q}function a(U,Y){let Q=e(U,Y),Z=UU(U,Y);if(Q.key>Z.key||Q.key===Z.key&&(Q.open||Z.open))return null;return{lower:Q,upper:Z}}function C(U){return U.replaceAll(/:+$/g,"")}function YU(U,Y){let Q=C(U),Z=C(Y);if(Q.length===0)return Z;if(Z.length===0)return Q;return`${Q}:${Z}`}class R{#Y;#Q;constructor(U,Y){this.#Y=U,this.#Q=C(Y)}#U(U){if(this.#Q.length===0)return U;return U.length===0?`${this.#Q}:`:`${this.#Q}:${U}`}#X(U){return U.map((Y)=>{if(Y.type==="put")return{type:"put",key:this.#U(Y.key),value:Y.value};return{type:"delete",key:this.#U(Y.key)}})}#Z(U){if(this.#Q.length===0)return U;return U.slice(this.#Q.length+1)}#$(U){let Y={};if(U.limit!==void 0)Y.limit=U.limit;if(U.gt!==void 0)Y.gt=this.#U(U.gt);if(U.gte!==void 0)Y.gte=this.#U(U.gte);if(U.lt!==void 0)Y.lt=this.#U(U.lt);if(U.lte!==void 0)Y.lte=this.#U(U.lte);return Y}#J(U={}){let Y=this.#$(U);if(U.reverse!==void 0)Y.reverse=U.reverse;return Y}#G(U){return this.#$(U)}capabilities(){return this.#Y.capabilities()}scoped(U){return new R(this.#Y,YU(this.#Q,U))}async get(U){return this.#Y.get(this.#U(U))}async put(U,Y){await this.#Y.put(this.#U(U),Y)}async delete(U){await this.#Y.delete(this.#U(U))}async*scan(U,Y){for await(let[Q,Z]of this.#Y.scan(this.#U(U),this.#J(Y)))yield[this.#Z(Q),Z]}async batch(U){z("batch operations",U.length),await this.#Y.batch(this.#X(U))}async conditionalBatch(U,Y){return p(this.#Y,U.map((Q)=>({key:this.#U(Q.key),expectedValue:Q.expectedValue})),this.#X(Y))}async has(U){return u(this.#Y,this.#U(U))}async deletePrefix(U){return l(this.#Y,this.#U(U))}async deleteRange(U,Y){let Q=this.#G(S(Y));return d(this.#Y,this.#U(U),Q)}async*keys(U,Y){for await(let Q of w(this.#Y,this.#U(U),this.#J(Y)))yield this.#Z(Q)}async count(U){return f(this.#Y,this.#U(U))}[Symbol.dispose](){this.#Y[Symbol.dispose]()}}function s(U,Y){return new R(U,Y)}var F="kv";function QU(U=globalThis){let{indexedDB:Y,IDBKeyRange:Q}=U;if(Y===void 0||Q===void 0)throw Error("IndexedDBStorage requires both indexedDB and IDBKeyRange runtime globals.");return{indexedDB:Y,IDBKeyRange:Q}}function N(U){return new Promise((Y,Q)=>{U.onsuccess=()=>Y(U.result),U.onerror=()=>Q(U.error)})}function XU(U,Y){let Q=null,Z=null,$=null,J=(G,H)=>{let V=G??Error(H);if(Z){let B=Z;Q=null,Z=null,B(V);return}$=V};return U.onsuccess=()=>{if(!Q)return;let G=Q;Q=null,Z=null,G(U.result)},U.onerror=()=>{J(U.error,"IndexedDB cursor request failed.")},Y.onerror=()=>{J(Y.error,"IndexedDB transaction failed.")},Y.onabort=()=>{J(Y.error,"IndexedDB transaction aborted.")},()=>{return new Promise((G,H)=>{if($){let V=$;$=null,H(V);return}if(Q=G,Z=H,U.readyState==="done"){let V=Q;Q=null,Z=null,V?.(U.result)}})}}async function*n(U,Y,Q,Z,$,J){let{limit:G,reverse:H}=Z,V=j(Q),B=Y.bound(Q,V,!1,!0),M=H?"prev":"next",W=U.transaction(F,"readonly"),L=W.objectStore(F),_=$(L,B,M),K=XU(_,W),T=0,q=!1;try{let A=await K();while(A){if(G!==void 0&&T>=G)break;let i=A.key;if(k(i,Z))yield J(A),T++;A.continue(),A=await K()}q=!0}finally{if(!q)try{W.abort()}catch{}}}class ZU{#Y;#Q=null;#U;#X;constructor(U="weft",Y=QU()){this.#Y=U,this.#X=Y,this.#U=this.#Z()}capabilities(){return{persistence:"local",readAfterWrite:"linearizable",scanConsistency:"best-effort",atomicBatch:!0,conditionalBatch:!0,boundedRangeDelete:!0}}#Z(){let U=this.#X.indexedDB.open(this.#Y,1);return U.onupgradeneeded=()=>{let Y=U.result;if(!Y.objectStoreNames.contains(F))Y.createObjectStore(F)},N(U).then((Y)=>{return this.#Q=Y,Y})}async get(U){let Z=(await this.#U).transaction(F,"readonly").objectStore(F),$=await N(Z.get(U));return $===void 0?null:new Uint8Array($)}async put(U,Y){let $=(await this.#U).transaction(F,"readwrite").objectStore(F);await N($.put(Y,U))}async delete(U){let Z=(await this.#U).transaction(F,"readwrite").objectStore(F);await N(Z.delete(U))}async has(U){let Z=(await this.#U).transaction(F,"readonly").objectStore(F);return await N(Z.count(U))>0}async deletePrefix(U){let Y=await this.#U,Q=j(U),Z=this.#X.IDBKeyRange.bound(U,Q,!1,!0);return new Promise(($,J)=>{let G=Y.transaction(F,"readwrite"),H=G.objectStore(F),V=0,B=H.count(Z);B.onsuccess=()=>{V=B.result,H.delete(Z)},G.oncomplete=()=>$(V),G.onerror=()=>J(G.error)})}async deleteRange(U,Y){let Q=S(Y),Z=a(U,Q);if(Z===null)return 0;let $=this.#X.IDBKeyRange.bound(Z.lower.key,Z.upper.key,Z.lower.open,Z.upper.open),J=await this.#U,{limit:G}=Q;return new Promise((H,V)=>{let B=J.transaction(F,"readwrite"),M=B.objectStore(F),W=0;if(G===void 0){let L=M.count($);L.onsuccess=()=>{W=L.result,M.delete($)}}else{let L=M.openCursor($,"next");L.onsuccess=()=>{let _=L.result;if(_===null||W>=G)return;if(_.delete(),W++,W<G)_.continue()}}B.oncomplete=()=>H(W),B.onerror=()=>V(B.error)})}async*scan(U,Y={}){let Q=await this.#U;yield*n(Q,this.#X.IDBKeyRange,U,Y,(Z,$,J)=>Z.openCursor($,J),(Z)=>[Z.key,new Uint8Array(Z.value)])}async batch(U){if(z("batch operations",U.length),U.length===0)return;let Y=await this.#U;return new Promise((Q,Z)=>{let $=Y.transaction(F,"readwrite"),J=$.objectStore(F);for(let G of U)if(G.type==="put")J.put(G.value,G.key);else J.delete(G.key);$.oncomplete=()=>Q(),$.onerror=()=>Z($.error)})}async conditionalBatch(U,Y){z("conditionalBatch conditions",U.length),z("conditionalBatch operations",Y.length);let Q=await this.#U;return new Promise((Z,$)=>{let J=Q.transaction(F,"readwrite"),G=J.objectStore(F),H=!1,V=!1,B=(L,_)=>{if(H)return;H=!0,$(L??Error(_))};J.oncomplete=()=>{if(H)return;H=!0,Z(!0)},J.onerror=()=>{B(J.error,"IndexedDB conditionalBatch transaction failed.")},J.onabort=()=>{if(H)return;if(H=!0,V){Z(!1);return}$(J.error??Error("IndexedDB conditionalBatch transaction aborted."))};let M=()=>{for(let L of Y)if(L.type==="put")G.put(L.value,L.key);else G.delete(L.key)},W=(L)=>{if(L>=U.length){M();return}let _=U[L],K=G.get(_.key);K.onsuccess=()=>{let T=K.result,q=T===void 0?null:new Uint8Array(T);if(!c(q,_.expectedValue)){V=!0,J.abort();return}W(L+1)},K.onerror=()=>{B(K.error,"IndexedDB conditionalBatch condition check failed.")}};W(0)})}async*keys(U,Y={}){let Q=await this.#U;yield*n(Q,this.#X.IDBKeyRange,U,Y,(Z,$,J)=>Z.openKeyCursor($,J),(Z)=>Z.key)}async count(U){let Y=await this.#U,Q=j(U),$=Y.transaction(F,"readonly").objectStore(F);return N($.count(this.#X.IDBKeyRange.bound(U,Q,!1,!0)))}scoped(U){return s(this,U)}[Symbol.dispose](){if(this.#Q)this.#Q.close(),this.#Q=null}}export{ZU as IndexedDBStorage};