@ankhorage/flow 0.4.1 → 0.5.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 (66) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +3 -1
  3. package/dist/execution.d.ts +5 -0
  4. package/dist/execution.d.ts.map +1 -0
  5. package/dist/execution.js +5 -0
  6. package/dist/execution.js.map +1 -0
  7. package/dist/features/execution/adapters/FileFlowPersistenceAdapter.d.ts +4 -0
  8. package/dist/features/execution/adapters/FileFlowPersistenceAdapter.d.ts.map +1 -0
  9. package/dist/features/execution/adapters/FileFlowPersistenceAdapter.js +219 -0
  10. package/dist/features/execution/adapters/FileFlowPersistenceAdapter.js.map +1 -0
  11. package/dist/features/execution/application/cancelDurableRunAsync.d.ts +8 -0
  12. package/dist/features/execution/application/cancelDurableRunAsync.d.ts.map +1 -0
  13. package/dist/features/execution/application/cancelDurableRunAsync.js +22 -0
  14. package/dist/features/execution/application/cancelDurableRunAsync.js.map +1 -0
  15. package/dist/features/execution/application/createDurableRunAsync.d.ts +12 -0
  16. package/dist/features/execution/application/createDurableRunAsync.d.ts.map +1 -0
  17. package/dist/features/execution/application/createDurableRunAsync.js +8 -0
  18. package/dist/features/execution/application/createDurableRunAsync.js.map +1 -0
  19. package/dist/features/execution/application/runDurableWorkerAsync.d.ts +52 -0
  20. package/dist/features/execution/application/runDurableWorkerAsync.d.ts.map +1 -0
  21. package/dist/features/execution/application/runDurableWorkerAsync.js +195 -0
  22. package/dist/features/execution/application/runDurableWorkerAsync.js.map +1 -0
  23. package/dist/features/execution/domain/calculateRetryDueAt.d.ts +4 -0
  24. package/dist/features/execution/domain/calculateRetryDueAt.d.ts.map +1 -0
  25. package/dist/features/execution/domain/calculateRetryDueAt.js +14 -0
  26. package/dist/features/execution/domain/calculateRetryDueAt.js.map +1 -0
  27. package/dist/features/execution/domain/createDurableRun.d.ts +7 -0
  28. package/dist/features/execution/domain/createDurableRun.d.ts.map +1 -0
  29. package/dist/features/execution/domain/createDurableRun.js +10 -0
  30. package/dist/features/execution/domain/createDurableRun.js.map +1 -0
  31. package/dist/features/execution/domain/createRetryWork.d.ts +4 -0
  32. package/dist/features/execution/domain/createRetryWork.d.ts.map +1 -0
  33. package/dist/features/execution/domain/createRetryWork.js +17 -0
  34. package/dist/features/execution/domain/createRetryWork.js.map +1 -0
  35. package/dist/features/execution/domain/isCronDueAt.d.ts +7 -0
  36. package/dist/features/execution/domain/isCronDueAt.d.ts.map +1 -0
  37. package/dist/features/execution/domain/isCronDueAt.js +114 -0
  38. package/dist/features/execution/domain/isCronDueAt.js.map +1 -0
  39. package/dist/features/execution/ports/FlowPersistencePort.d.ts +34 -0
  40. package/dist/features/execution/ports/FlowPersistencePort.d.ts.map +1 -0
  41. package/dist/features/execution/ports/FlowPersistencePort.js +2 -0
  42. package/dist/features/execution/ports/FlowPersistencePort.js.map +1 -0
  43. package/dist/features/security/application/createFlowSecurityPort.js +2 -2
  44. package/dist/features/security/application/createFlowSecurityPort.js.map +1 -1
  45. package/dist/index.d.ts +7 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +5 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/types/durableExecution.d.ts +55 -0
  50. package/dist/types/durableExecution.d.ts.map +1 -0
  51. package/dist/types/durableExecution.js +2 -0
  52. package/dist/types/durableExecution.js.map +1 -0
  53. package/package.json +5 -1
  54. package/src/execution.ts +9 -0
  55. package/src/features/execution/adapters/FileFlowPersistenceAdapter.ts +333 -0
  56. package/src/features/execution/application/cancelDurableRunAsync.ts +28 -0
  57. package/src/features/execution/application/createDurableRunAsync.ts +14 -0
  58. package/src/features/execution/application/runDurableWorkerAsync.ts +337 -0
  59. package/src/features/execution/domain/calculateRetryDueAt.ts +21 -0
  60. package/src/features/execution/domain/createDurableRun.ts +14 -0
  61. package/src/features/execution/domain/createRetryWork.ts +22 -0
  62. package/src/features/execution/domain/isCronDueAt.ts +131 -0
  63. package/src/features/execution/ports/FlowPersistencePort.ts +38 -0
  64. package/src/features/security/application/createFlowSecurityPort.ts +2 -2
  65. package/src/index.ts +14 -0
  66. package/src/types/durableExecution.ts +61 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @ankhorage/flow
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 63d8dd6: Add provider-neutral durable execution with fenced workers, retries, checkpoints, cron matching,
8
+ outbox idempotency, cancellation, and published-security approval waits.
9
+
3
10
  ## 0.4.1
4
11
 
5
12
  ### Patch Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # @ankhorage/flow
5
5
 
6
- ![license: MIT](././paradox/badges/license.svg) ![npm: v0.4.1](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: configured](././paradox/badges/tests.svg) ![paradox: canonical](././paradox/badges/docs.svg)
6
+ ![license: MIT](././paradox/badges/license.svg) ![npm: v0.5.0](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: configured](././paradox/badges/tests.svg) ![paradox: warnings](././paradox/badges/docs.svg)
7
7
 
8
8
  Standalone workflow engine foundation for Ankhorage.
9
9
 
@@ -16,3 +16,5 @@ Standalone workflow engine foundation for Ankhorage.
16
16
  - [Module relationships](././paradox/diagrams/module-relationships.mmd)
17
17
  - [Export graph](././paradox/diagrams/export-graph.mmd)
18
18
  - [compileFlowGraph sequence](././paradox/diagrams/sequences/compile-flow-graph.mmd)
19
+ - [createRetryWork sequence](././paradox/diagrams/sequences/create-retry-work.mmd)
20
+ - [isCronDueAt sequence](././paradox/diagrams/sequences/is-cron-due-at.mmd)
@@ -0,0 +1,5 @@
1
+ /*** Node-specific durable-execution adapters. */
2
+ export { FileFlowPersistenceAdapter } from './features/execution/adapters/FileFlowPersistenceAdapter.js';
3
+ export { cancelDurableRunAsync } from './features/execution/application/cancelDurableRunAsync.js';
4
+ export { type FlowDurableEffectPort, type FlowWorkerOutcome, type FlowWorkerResult, runDurableWorkerAsync, } from './features/execution/application/runDurableWorkerAsync.js';
5
+ //# sourceMappingURL=execution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execution.d.ts","sourceRoot":"","sources":["../src/execution.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,OAAO,EAAE,0BAA0B,EAAE,MAAM,6DAA6D,CAAC;AACzG,OAAO,EAAE,qBAAqB,EAAE,MAAM,2DAA2D,CAAC;AAClG,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,qBAAqB,GACtB,MAAM,2DAA2D,CAAC"}
@@ -0,0 +1,5 @@
1
+ /*** Node-specific durable-execution adapters. */
2
+ export { FileFlowPersistenceAdapter } from './features/execution/adapters/FileFlowPersistenceAdapter.js';
3
+ export { cancelDurableRunAsync } from './features/execution/application/cancelDurableRunAsync.js';
4
+ export { runDurableWorkerAsync, } from './features/execution/application/runDurableWorkerAsync.js';
5
+ //# sourceMappingURL=execution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execution.js","sourceRoot":"","sources":["../src/execution.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,OAAO,EAAE,0BAA0B,EAAE,MAAM,6DAA6D,CAAC;AACzG,OAAO,EAAE,qBAAqB,EAAE,MAAM,2DAA2D,CAAC;AAClG,OAAO,EAIL,qBAAqB,GACtB,MAAM,2DAA2D,CAAC","sourcesContent":["/*** Node-specific durable-execution adapters. */\nexport { FileFlowPersistenceAdapter } from './features/execution/adapters/FileFlowPersistenceAdapter.js';\nexport { cancelDurableRunAsync } from './features/execution/application/cancelDurableRunAsync.js';\nexport {\n type FlowDurableEffectPort,\n type FlowWorkerOutcome,\n type FlowWorkerResult,\n runDurableWorkerAsync,\n} from './features/execution/application/runDurableWorkerAsync.js';\n"]}
@@ -0,0 +1,4 @@
1
+ import type { FlowPersistencePort } from '../ports/FlowPersistencePort.js';
2
+ /*** Create a self-hostable, single-process JSON persistence adapter for durable Flow state. */
3
+ export declare const FileFlowPersistenceAdapter: (path: string) => FlowPersistencePort;
4
+ //# sourceMappingURL=FileFlowPersistenceAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileFlowPersistenceAdapter.d.ts","sourceRoot":"","sources":["../../../../src/features/execution/adapters/FileFlowPersistenceAdapter.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAE3E,+FAA+F;AAC/F,eAAO,MAAM,0BAA0B,GAAI,MAAM,MAAM,KAAG,mBA0BzD,CAAC"}
@@ -0,0 +1,219 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
3
+ import { dirname, resolve } from 'node:path';
4
+ /*** Create a self-hostable, single-process JSON persistence adapter for durable Flow state. */
5
+ export const FileFlowPersistenceAdapter = (path) => {
6
+ const persistencePath = resolve(path);
7
+ const serializedCall = getSerializedCall(persistencePath);
8
+ return {
9
+ cancelRunWorkAsync: (runId) => serializedCall.runAsync(() => cancelRunWorkAsync(persistencePath, runId)),
10
+ claimDueWorkAsync: (input) => serializedCall.runAsync(() => claimDueWorkAsync(persistencePath, input)),
11
+ completeWorkAsync: (input) => serializedCall.runAsync(() => completeWorkAsync(persistencePath, input)),
12
+ createOutboxAsync: (record) => serializedCall.runAsync(() => createOutboxAsync(persistencePath, record)),
13
+ createRunAsync: (run) => serializedCall.runAsync(() => createRunAsync(persistencePath, run)),
14
+ enqueueWorkAsync: (work) => serializedCall.runAsync(() => enqueueWorkAsync(persistencePath, work)),
15
+ markOutboxDeliveredAsync: (id) => serializedCall.runAsync(() => markOutboxDeliveredAsync(persistencePath, id)),
16
+ readOutboxAsync: (id) => serializedCall.runAsync(() => readOutboxAsync(persistencePath, id)),
17
+ readRunAsync: (id) => serializedCall.runAsync(() => readRunAsync(persistencePath, id)),
18
+ recoverExpiredClaimsAsync: (now) => serializedCall.runAsync(() => recoverExpiredClaimsAsync(persistencePath, now)),
19
+ releaseWorkAsync: (input) => serializedCall.runAsync(() => releaseWorkAsync(persistencePath, input)),
20
+ transitionRunAsync: (input) => serializedCall.runAsync(() => transitionRunAsync(persistencePath, input)),
21
+ };
22
+ };
23
+ /** Cancel every nonterminal delivery for a run after its durable cancellation transition. */
24
+ const cancelRunWorkAsync = async (path, runId) => updateStoreAsync(path, (store) => {
25
+ const cancelled = store.work
26
+ .filter((work) => work.runId === runId && (work.status === 'ready' || work.status === 'claimed'))
27
+ .map((work) => ({ ...work, lease: undefined, status: 'cancelled' }));
28
+ return {
29
+ result: cancelled.map((work) => work.id),
30
+ store: { ...store, work: replaceWork(store.work, cancelled) },
31
+ };
32
+ });
33
+ /** Persist a stable outbox identity before any external effect is attempted. */
34
+ const createOutboxAsync = async (path, record) => updateStoreAsync(path, (store) => {
35
+ const existing = store.outbox.find((candidate) => candidate.id === record.id);
36
+ if (existing !== undefined && existing.idempotencyKey !== record.idempotencyKey)
37
+ throw new Error(`FLOW_OUTBOX_CONFLICT:${record.id}`);
38
+ return {
39
+ result: undefined,
40
+ store: existing === undefined ? { ...store, outbox: [...store.outbox, copy(record)] } : store,
41
+ };
42
+ });
43
+ const serializedCallsByPath = new Map();
44
+ /*** Share serialization for every adapter instance targeting one normalized persistence path. */
45
+ const getSerializedCall = (path) => {
46
+ const existing = serializedCallsByPath.get(path);
47
+ if (existing !== undefined)
48
+ return existing;
49
+ const serializedCall = createSerializedCall();
50
+ serializedCallsByPath.set(path, serializedCall);
51
+ return serializedCall;
52
+ };
53
+ /*** Serialize one file path so concurrent callbacks cannot claim or overwrite the same state. */
54
+ const createSerializedCall = () => {
55
+ let previous = Promise.resolve();
56
+ return {
57
+ runAsync: async (operation) => {
58
+ const current = previous.then(operation, operation);
59
+ previous = current.then(() => undefined, () => undefined);
60
+ return current;
61
+ },
62
+ };
63
+ };
64
+ /*** Claim due work in stable order and record a lease before it is returned to a worker. */
65
+ const claimDueWorkAsync = async (path, input) => updateStoreAsync(path, (store) => {
66
+ const claimed = store.work
67
+ .filter((work) => work.status === 'ready' && work.dueAt <= input.now.toISOString())
68
+ .sort((left, right) => left.dueAt.localeCompare(right.dueAt) || left.id.localeCompare(right.id))
69
+ .slice(0, input.limit)
70
+ .map((work, index) => claimWork(work, input, store.nextLeaseFencingToken + index));
71
+ return {
72
+ result: claimed,
73
+ store: {
74
+ ...store,
75
+ nextLeaseFencingToken: store.nextLeaseFencingToken + claimed.length,
76
+ work: replaceWork(store.work, claimed),
77
+ },
78
+ };
79
+ });
80
+ /*** Complete only the unexpired fenced lease returned by the successful claim. */
81
+ const completeWorkAsync = async (path, { id, leaseFencingToken, now, workerId }) => updateStoreAsync(path, (store) => {
82
+ const work = store.work.find((candidate) => candidate.id === id);
83
+ if (work?.status !== 'claimed' ||
84
+ work.lease?.workerId !== workerId ||
85
+ work.lease.fencingToken !== leaseFencingToken ||
86
+ work.lease.expiresAt <= now.toISOString())
87
+ return { result: false, store };
88
+ return {
89
+ result: true,
90
+ store: { ...store, work: replaceWork(store.work, [{ ...work, status: 'completed' }]) },
91
+ };
92
+ });
93
+ /*** Persist a new run once, rejecting a duplicate durable identity. */
94
+ const createRunAsync = async (path, run) => updateStoreAsync(path, (store) => {
95
+ if (store.runs.some((candidate) => candidate.id === run.id))
96
+ throw new Error(`FLOW_RUN_EXISTS:${run.id}`);
97
+ return { result: undefined, store: { ...store, runs: [...store.runs, copy(run)] } };
98
+ });
99
+ /*** Persist one immutable work identity once, rejecting duplicate delivery records. */
100
+ const enqueueWorkAsync = async (path, work) => updateStoreAsync(path, (store) => {
101
+ if (store.work.some((candidate) => candidate.id === work.id))
102
+ throw new Error(`FLOW_WORK_EXISTS:${work.id}`);
103
+ return { result: undefined, store: { ...store, work: [...store.work, copy(work)] } };
104
+ });
105
+ /** Mark an effect delivery complete; duplicate marks are harmless and do not rerun the effect. */
106
+ const markOutboxDeliveredAsync = async (path, id) => updateStoreAsync(path, (store) => {
107
+ const record = store.outbox.find((candidate) => candidate.id === id);
108
+ if (record === undefined)
109
+ return { result: false, store };
110
+ return {
111
+ result: true,
112
+ store: {
113
+ ...store,
114
+ outbox: replaceOutbox(store.outbox, [{ ...record, status: 'delivered' }]),
115
+ },
116
+ };
117
+ });
118
+ /** Read a copied outbox snapshot without exposing adapter-owned mutable state. */
119
+ const readOutboxAsync = async (path, id) => copy((await readStoreAsync(path)).outbox.find((record) => record.id === id));
120
+ /*** Read a copied run snapshot so callers cannot mutate persisted state in-place. */
121
+ const readRunAsync = async (path, id) => copy((await readStoreAsync(path)).runs.find((run) => run.id === id));
122
+ /*** Make expired claims available for recovery after a worker crash or stop. */
123
+ const recoverExpiredClaimsAsync = async (path, now) => updateStoreAsync(path, (store) => {
124
+ const recovered = store.work
125
+ .filter((work) => work.status === 'claimed' &&
126
+ work.lease?.expiresAt !== undefined &&
127
+ work.lease.expiresAt <= now.toISOString())
128
+ .map((work) => ({ ...work, lease: undefined, status: 'ready' }));
129
+ return {
130
+ result: recovered.map((work) => work.id),
131
+ store: { ...store, work: replaceWork(store.work, recovered) },
132
+ };
133
+ });
134
+ /** Return a fenced claim to the queue without permitting a stale worker to reschedule it. */
135
+ const releaseWorkAsync = async (path, input) => updateStoreAsync(path, (store) => {
136
+ const work = store.work.find((candidate) => candidate.id === input.id);
137
+ if (work?.status !== 'claimed' ||
138
+ work.lease?.workerId !== input.workerId ||
139
+ work.lease.fencingToken !== input.leaseFencingToken)
140
+ return { result: false, store };
141
+ return {
142
+ result: true,
143
+ store: {
144
+ ...store,
145
+ work: replaceWork(store.work, [
146
+ { ...work, dueAt: input.dueAt, lease: undefined, status: 'ready' },
147
+ ]),
148
+ },
149
+ };
150
+ });
151
+ /*** Store a revision-checked run transition while preserving its immutable flow reference. */
152
+ const transitionRunAsync = async (path, { expectedRevision, run }) => updateStoreAsync(path, (store) => {
153
+ const current = store.runs.find((candidate) => candidate.id === run.id);
154
+ if (!sameFlow(current ?? run, run) || current?.revision !== expectedRevision)
155
+ return { result: false, store };
156
+ if (run.revision !== expectedRevision + 1)
157
+ throw new Error('FLOW_RUN_REVISION_INVALID');
158
+ return { result: true, store: { ...store, runs: replaceRuns(store.runs, [copy(run)]) } };
159
+ });
160
+ /*** Build a claimed work record with an explicit bounded lease. */
161
+ const claimWork = (work, input, fencingToken) => ({
162
+ ...work,
163
+ lease: {
164
+ expiresAt: new Date(input.now.getTime() + input.leaseDurationMs).toISOString(),
165
+ fencingToken,
166
+ workerId: input.workerId,
167
+ },
168
+ status: 'claimed',
169
+ });
170
+ /*** Replace changed work entries by identity without exposing a mutable adapter collection. */
171
+ const replaceWork = (work, replacements) => work.map((candidate) => replacements.find((replacement) => replacement.id === candidate.id) ?? candidate);
172
+ /*** Replace changed run entries by identity without exposing a mutable adapter collection. */
173
+ const replaceRuns = (runs, replacements) => runs.map((candidate) => replacements.find((replacement) => replacement.id === candidate.id) ?? candidate);
174
+ /** Replace durable outbox records by identity. */
175
+ const replaceOutbox = (records, replacements) => records.map((candidate) => replacements.find((replacement) => replacement.id === candidate.id) ?? candidate);
176
+ /*** Read persisted state or initialize an empty store when it has not yet been created. */
177
+ const readStoreAsync = async (path) => {
178
+ try {
179
+ return readFileStore(JSON.parse(await readFile(path, 'utf8')));
180
+ }
181
+ catch (reason) {
182
+ if (isMissingFile(reason))
183
+ return { nextLeaseFencingToken: 1, outbox: [], runs: [], work: [] };
184
+ throw reason;
185
+ }
186
+ };
187
+ /*** Validate the adapter-owned persistence envelope before using its fencing counter. */
188
+ const readFileStore = (value) => {
189
+ if (typeof value !== 'object' ||
190
+ value === null ||
191
+ !('nextLeaseFencingToken' in value) ||
192
+ typeof value.nextLeaseFencingToken !== 'number' ||
193
+ !Number.isSafeInteger(value.nextLeaseFencingToken) ||
194
+ value.nextLeaseFencingToken < 1 ||
195
+ !('runs' in value) ||
196
+ !Array.isArray(value.runs) ||
197
+ !('work' in value) ||
198
+ !Array.isArray(value.work) ||
199
+ !('outbox' in value) ||
200
+ !Array.isArray(value.outbox))
201
+ throw new Error('FLOW_PERSISTENCE_INVALID');
202
+ return value;
203
+ };
204
+ /*** Apply one single-process transaction and atomically replace the persisted JSON snapshot. */
205
+ const updateStoreAsync = async (path, update) => {
206
+ const updated = update(await readStoreAsync(path));
207
+ await mkdir(dirname(path), { recursive: true });
208
+ const temporaryPath = `${path}.${randomUUID()}.tmp`;
209
+ await writeFile(temporaryPath, JSON.stringify(updated.store));
210
+ await rename(temporaryPath, path);
211
+ return copy(updated.result);
212
+ };
213
+ /*** Preserve adapter encapsulation by returning data that callers cannot mutate in-place. */
214
+ const copy = (value) => structuredClone(value);
215
+ /*** Distinguish an absent first-run store from real filesystem or JSON errors. */
216
+ const isMissingFile = (reason) => typeof reason === 'object' && reason !== null && 'code' in reason && reason.code === 'ENOENT';
217
+ /*** Reject a transition that attempts to repoint a run at another immutable flow revision. */
218
+ const sameFlow = (left, right) => left.flow.id === right.flow.id && left.flow.version === right.flow.version;
219
+ //# sourceMappingURL=FileFlowPersistenceAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileFlowPersistenceAdapter.js","sourceRoot":"","sources":["../../../../src/features/execution/adapters/FileFlowPersistenceAdapter.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { mkdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { dirname, resolve } from 'node:path';\n\nimport type {\n FlowDurableRun,\n FlowDurableWork,\n FlowOutboxRecord,\n} from '../../../types/durableExecution.js';\nimport type { FlowPersistencePort } from '../ports/FlowPersistencePort.js';\n\n/*** Create a self-hostable, single-process JSON persistence adapter for durable Flow state. */\nexport const FileFlowPersistenceAdapter = (path: string): FlowPersistencePort => {\n const persistencePath = resolve(path);\n const serializedCall = getSerializedCall(persistencePath);\n return {\n cancelRunWorkAsync: (runId) =>\n serializedCall.runAsync(() => cancelRunWorkAsync(persistencePath, runId)),\n claimDueWorkAsync: (input) =>\n serializedCall.runAsync(() => claimDueWorkAsync(persistencePath, input)),\n completeWorkAsync: (input) =>\n serializedCall.runAsync(() => completeWorkAsync(persistencePath, input)),\n createOutboxAsync: (record) =>\n serializedCall.runAsync(() => createOutboxAsync(persistencePath, record)),\n createRunAsync: (run) => serializedCall.runAsync(() => createRunAsync(persistencePath, run)),\n enqueueWorkAsync: (work) =>\n serializedCall.runAsync(() => enqueueWorkAsync(persistencePath, work)),\n markOutboxDeliveredAsync: (id) =>\n serializedCall.runAsync(() => markOutboxDeliveredAsync(persistencePath, id)),\n readOutboxAsync: (id) => serializedCall.runAsync(() => readOutboxAsync(persistencePath, id)),\n readRunAsync: (id) => serializedCall.runAsync(() => readRunAsync(persistencePath, id)),\n recoverExpiredClaimsAsync: (now) =>\n serializedCall.runAsync(() => recoverExpiredClaimsAsync(persistencePath, now)),\n releaseWorkAsync: (input) =>\n serializedCall.runAsync(() => releaseWorkAsync(persistencePath, input)),\n transitionRunAsync: (input) =>\n serializedCall.runAsync(() => transitionRunAsync(persistencePath, input)),\n };\n};\n\n/** Cancel every nonterminal delivery for a run after its durable cancellation transition. */\nconst cancelRunWorkAsync = async (path: string, runId: string): Promise<readonly string[]> =>\n updateStoreAsync(path, (store) => {\n const cancelled = store.work\n .filter(\n (work) => work.runId === runId && (work.status === 'ready' || work.status === 'claimed'),\n )\n .map((work) => ({ ...work, lease: undefined, status: 'cancelled' as const }));\n return {\n result: cancelled.map((work) => work.id),\n store: { ...store, work: replaceWork(store.work, cancelled) },\n };\n });\n\ninterface FileFlowStore {\n readonly nextLeaseFencingToken: number;\n readonly outbox: readonly FlowOutboxRecord[];\n readonly runs: readonly FlowDurableRun[];\n readonly work: readonly FlowDurableWork[];\n}\n\n/** Persist a stable outbox identity before any external effect is attempted. */\nconst createOutboxAsync = async (path: string, record: FlowOutboxRecord): Promise<void> =>\n updateStoreAsync(path, (store) => {\n const existing = store.outbox.find((candidate) => candidate.id === record.id);\n if (existing !== undefined && existing.idempotencyKey !== record.idempotencyKey)\n throw new Error(`FLOW_OUTBOX_CONFLICT:${record.id}`);\n return {\n result: undefined,\n store: existing === undefined ? { ...store, outbox: [...store.outbox, copy(record)] } : store,\n };\n });\n\ninterface SerializedCall {\n readonly runAsync: <Result>(operation: () => Promise<Result>) => Promise<Result>;\n}\n\nconst serializedCallsByPath = new Map<string, SerializedCall>();\n\n/*** Share serialization for every adapter instance targeting one normalized persistence path. */\nconst getSerializedCall = (path: string): SerializedCall => {\n const existing = serializedCallsByPath.get(path);\n if (existing !== undefined) return existing;\n const serializedCall = createSerializedCall();\n serializedCallsByPath.set(path, serializedCall);\n return serializedCall;\n};\n\n/*** Serialize one file path so concurrent callbacks cannot claim or overwrite the same state. */\nconst createSerializedCall = (): SerializedCall => {\n let previous = Promise.resolve();\n return {\n runAsync: async (operation) => {\n const current = previous.then(operation, operation);\n previous = current.then(\n () => undefined,\n () => undefined,\n );\n return current;\n },\n };\n};\n\n/*** Claim due work in stable order and record a lease before it is returned to a worker. */\nconst claimDueWorkAsync = async (\n path: string,\n input: Parameters<FlowPersistencePort['claimDueWorkAsync']>[0],\n): Promise<readonly FlowDurableWork[]> =>\n updateStoreAsync(path, (store) => {\n const claimed = store.work\n .filter((work) => work.status === 'ready' && work.dueAt <= input.now.toISOString())\n .sort(\n (left, right) => left.dueAt.localeCompare(right.dueAt) || left.id.localeCompare(right.id),\n )\n .slice(0, input.limit)\n .map((work, index) => claimWork(work, input, store.nextLeaseFencingToken + index));\n return {\n result: claimed,\n store: {\n ...store,\n nextLeaseFencingToken: store.nextLeaseFencingToken + claimed.length,\n work: replaceWork(store.work, claimed),\n },\n };\n });\n\n/*** Complete only the unexpired fenced lease returned by the successful claim. */\nconst completeWorkAsync = async (\n path: string,\n { id, leaseFencingToken, now, workerId }: Parameters<FlowPersistencePort['completeWorkAsync']>[0],\n): Promise<boolean> =>\n updateStoreAsync(path, (store) => {\n const work = store.work.find((candidate) => candidate.id === id);\n if (\n work?.status !== 'claimed' ||\n work.lease?.workerId !== workerId ||\n work.lease.fencingToken !== leaseFencingToken ||\n work.lease.expiresAt <= now.toISOString()\n )\n return { result: false, store };\n return {\n result: true,\n store: { ...store, work: replaceWork(store.work, [{ ...work, status: 'completed' }]) },\n };\n });\n\n/*** Persist a new run once, rejecting a duplicate durable identity. */\nconst createRunAsync = async (path: string, run: FlowDurableRun): Promise<void> =>\n updateStoreAsync(path, (store) => {\n if (store.runs.some((candidate) => candidate.id === run.id))\n throw new Error(`FLOW_RUN_EXISTS:${run.id}`);\n return { result: undefined, store: { ...store, runs: [...store.runs, copy(run)] } };\n });\n\n/*** Persist one immutable work identity once, rejecting duplicate delivery records. */\nconst enqueueWorkAsync = async (path: string, work: FlowDurableWork): Promise<void> =>\n updateStoreAsync(path, (store) => {\n if (store.work.some((candidate) => candidate.id === work.id))\n throw new Error(`FLOW_WORK_EXISTS:${work.id}`);\n return { result: undefined, store: { ...store, work: [...store.work, copy(work)] } };\n });\n\n/** Mark an effect delivery complete; duplicate marks are harmless and do not rerun the effect. */\nconst markOutboxDeliveredAsync = async (path: string, id: string): Promise<boolean> =>\n updateStoreAsync(path, (store) => {\n const record = store.outbox.find((candidate) => candidate.id === id);\n if (record === undefined) return { result: false, store };\n return {\n result: true,\n store: {\n ...store,\n outbox: replaceOutbox(store.outbox, [{ ...record, status: 'delivered' }]),\n },\n };\n });\n\n/** Read a copied outbox snapshot without exposing adapter-owned mutable state. */\nconst readOutboxAsync = async (path: string, id: string): Promise<FlowOutboxRecord | undefined> =>\n copy((await readStoreAsync(path)).outbox.find((record) => record.id === id));\n\n/*** Read a copied run snapshot so callers cannot mutate persisted state in-place. */\nconst readRunAsync = async (path: string, id: string): Promise<FlowDurableRun | undefined> =>\n copy((await readStoreAsync(path)).runs.find((run) => run.id === id));\n\n/*** Make expired claims available for recovery after a worker crash or stop. */\nconst recoverExpiredClaimsAsync = async (path: string, now: Date): Promise<readonly string[]> =>\n updateStoreAsync(path, (store) => {\n const recovered = store.work\n .filter(\n (work) =>\n work.status === 'claimed' &&\n work.lease?.expiresAt !== undefined &&\n work.lease.expiresAt <= now.toISOString(),\n )\n .map((work) => ({ ...work, lease: undefined, status: 'ready' as const }));\n return {\n result: recovered.map((work) => work.id),\n store: { ...store, work: replaceWork(store.work, recovered) },\n };\n });\n\n/** Return a fenced claim to the queue without permitting a stale worker to reschedule it. */\nconst releaseWorkAsync = async (\n path: string,\n input: Parameters<FlowPersistencePort['releaseWorkAsync']>[0],\n): Promise<boolean> =>\n updateStoreAsync(path, (store) => {\n const work = store.work.find((candidate) => candidate.id === input.id);\n if (\n work?.status !== 'claimed' ||\n work.lease?.workerId !== input.workerId ||\n work.lease.fencingToken !== input.leaseFencingToken\n )\n return { result: false, store };\n return {\n result: true,\n store: {\n ...store,\n work: replaceWork(store.work, [\n { ...work, dueAt: input.dueAt, lease: undefined, status: 'ready' },\n ]),\n },\n };\n });\n\n/*** Store a revision-checked run transition while preserving its immutable flow reference. */\nconst transitionRunAsync = async (\n path: string,\n { expectedRevision, run }: Parameters<FlowPersistencePort['transitionRunAsync']>[0],\n): Promise<boolean> =>\n updateStoreAsync(path, (store) => {\n const current = store.runs.find((candidate) => candidate.id === run.id);\n if (!sameFlow(current ?? run, run) || current?.revision !== expectedRevision)\n return { result: false, store };\n if (run.revision !== expectedRevision + 1) throw new Error('FLOW_RUN_REVISION_INVALID');\n return { result: true, store: { ...store, runs: replaceRuns(store.runs, [copy(run)]) } };\n });\n\n/*** Build a claimed work record with an explicit bounded lease. */\nconst claimWork = (\n work: FlowDurableWork,\n input: Parameters<FlowPersistencePort['claimDueWorkAsync']>[0],\n fencingToken: number,\n): FlowDurableWork => ({\n ...work,\n lease: {\n expiresAt: new Date(input.now.getTime() + input.leaseDurationMs).toISOString(),\n fencingToken,\n workerId: input.workerId,\n },\n status: 'claimed',\n});\n\n/*** Replace changed work entries by identity without exposing a mutable adapter collection. */\nconst replaceWork = (\n work: readonly FlowDurableWork[],\n replacements: readonly FlowDurableWork[],\n): readonly FlowDurableWork[] =>\n work.map(\n (candidate) => replacements.find((replacement) => replacement.id === candidate.id) ?? candidate,\n );\n\n/*** Replace changed run entries by identity without exposing a mutable adapter collection. */\nconst replaceRuns = (\n runs: readonly FlowDurableRun[],\n replacements: readonly FlowDurableRun[],\n): readonly FlowDurableRun[] =>\n runs.map(\n (candidate) => replacements.find((replacement) => replacement.id === candidate.id) ?? candidate,\n );\n\n/** Replace durable outbox records by identity. */\nconst replaceOutbox = (\n records: readonly FlowOutboxRecord[],\n replacements: readonly FlowOutboxRecord[],\n): readonly FlowOutboxRecord[] =>\n records.map(\n (candidate) => replacements.find((replacement) => replacement.id === candidate.id) ?? candidate,\n );\n\n/*** Read persisted state or initialize an empty store when it has not yet been created. */\nconst readStoreAsync = async (path: string): Promise<FileFlowStore> => {\n try {\n return readFileStore(JSON.parse(await readFile(path, 'utf8')));\n } catch (reason) {\n if (isMissingFile(reason)) return { nextLeaseFencingToken: 1, outbox: [], runs: [], work: [] };\n throw reason;\n }\n};\n\n/*** Validate the adapter-owned persistence envelope before using its fencing counter. */\nconst readFileStore = (value: unknown): FileFlowStore => {\n if (\n typeof value !== 'object' ||\n value === null ||\n !('nextLeaseFencingToken' in value) ||\n typeof value.nextLeaseFencingToken !== 'number' ||\n !Number.isSafeInteger(value.nextLeaseFencingToken) ||\n value.nextLeaseFencingToken < 1 ||\n !('runs' in value) ||\n !Array.isArray(value.runs) ||\n !('work' in value) ||\n !Array.isArray(value.work) ||\n !('outbox' in value) ||\n !Array.isArray(value.outbox)\n )\n throw new Error('FLOW_PERSISTENCE_INVALID');\n return value as FileFlowStore;\n};\n\n/*** Apply one single-process transaction and atomically replace the persisted JSON snapshot. */\nconst updateStoreAsync = async <Result>(\n path: string,\n update: (store: FileFlowStore) => { readonly result: Result; readonly store: FileFlowStore },\n): Promise<Result> => {\n const updated = update(await readStoreAsync(path));\n await mkdir(dirname(path), { recursive: true });\n const temporaryPath = `${path}.${randomUUID()}.tmp`;\n await writeFile(temporaryPath, JSON.stringify(updated.store));\n await rename(temporaryPath, path);\n return copy(updated.result);\n};\n\n/*** Preserve adapter encapsulation by returning data that callers cannot mutate in-place. */\nconst copy = <Value>(value: Value): Value => structuredClone(value);\n\n/*** Distinguish an absent first-run store from real filesystem or JSON errors. */\nconst isMissingFile = (reason: unknown): reason is NodeJS.ErrnoException =>\n typeof reason === 'object' && reason !== null && 'code' in reason && reason.code === 'ENOENT';\n\n/*** Reject a transition that attempts to repoint a run at another immutable flow revision. */\nconst sameFlow = (left: FlowDurableRun, right: FlowDurableRun): boolean =>\n left.flow.id === right.flow.id && left.flow.version === right.flow.version;\n"]}
@@ -0,0 +1,8 @@
1
+ import type { FlowClock } from '../../../types/execution.js';
2
+ import type { FlowPersistencePort } from '../ports/FlowPersistencePort.js';
3
+ /** Cancel a durable run and atomically prevent future ready or claimed deliveries from starting. */
4
+ export declare const cancelDurableRunAsync: (runId: string, dependencies: {
5
+ readonly clock: FlowClock;
6
+ readonly persistence: FlowPersistencePort;
7
+ }) => Promise<boolean>;
8
+ //# sourceMappingURL=cancelDurableRunAsync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cancelDurableRunAsync.d.ts","sourceRoot":"","sources":["../../../../src/features/execution/application/cancelDurableRunAsync.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAE3E,oGAAoG;AACpG,eAAO,MAAM,qBAAqB,GAChC,OAAO,MAAM,EACb,cAAc;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAA;CAAE,KACrF,OAAO,CAAC,OAAO,CAejB,CAAC"}
@@ -0,0 +1,22 @@
1
+ /** Cancel a durable run and atomically prevent future ready or claimed deliveries from starting. */
2
+ export const cancelDurableRunAsync = async (runId, dependencies) => {
3
+ const run = await dependencies.persistence.readRunAsync(runId);
4
+ if (run === undefined || run.status === 'cancelled' || isTerminal(run))
5
+ return false;
6
+ const transitioned = await dependencies.persistence.transitionRunAsync({
7
+ expectedRevision: run.revision,
8
+ run: {
9
+ ...run,
10
+ revision: run.revision + 1,
11
+ status: 'cancelled',
12
+ updatedAt: dependencies.clock.now().toISOString(),
13
+ },
14
+ });
15
+ if (!transitioned)
16
+ return false;
17
+ await dependencies.persistence.cancelRunWorkAsync(runId);
18
+ return true;
19
+ };
20
+ /** Leave completed and terminally failed runs immutable. */
21
+ const isTerminal = (run) => run.status === 'failed' || run.status === 'succeeded';
22
+ //# sourceMappingURL=cancelDurableRunAsync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cancelDurableRunAsync.js","sourceRoot":"","sources":["../../../../src/features/execution/application/cancelDurableRunAsync.ts"],"names":[],"mappings":"AAIA,oGAAoG;AACpG,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,EACxC,KAAa,EACb,YAAsF,EACpE,EAAE;IACpB,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,WAAW,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAC/D,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,WAAW,IAAI,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACrF,MAAM,YAAY,GAAG,MAAM,YAAY,CAAC,WAAW,CAAC,kBAAkB,CAAC;QACrE,gBAAgB,EAAE,GAAG,CAAC,QAAQ;QAC9B,GAAG,EAAE;YACH,GAAG,GAAG;YACN,QAAQ,EAAE,GAAG,CAAC,QAAQ,GAAG,CAAC;YAC1B,MAAM,EAAE,WAAW;YACnB,SAAS,EAAE,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;SAClD;KACF,CAAC,CAAC;IACH,IAAI,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAChC,MAAM,YAAY,CAAC,WAAW,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACzD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,4DAA4D;AAC5D,MAAM,UAAU,GAAG,CAAC,GAAmB,EAAW,EAAE,CAClD,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,WAAW,CAAC","sourcesContent":["import type { FlowDurableRun } from '../../../types/durableExecution.js';\nimport type { FlowClock } from '../../../types/execution.js';\nimport type { FlowPersistencePort } from '../ports/FlowPersistencePort.js';\n\n/** Cancel a durable run and atomically prevent future ready or claimed deliveries from starting. */\nexport const cancelDurableRunAsync = async (\n runId: string,\n dependencies: { readonly clock: FlowClock; readonly persistence: FlowPersistencePort },\n): Promise<boolean> => {\n const run = await dependencies.persistence.readRunAsync(runId);\n if (run === undefined || run.status === 'cancelled' || isTerminal(run)) return false;\n const transitioned = await dependencies.persistence.transitionRunAsync({\n expectedRevision: run.revision,\n run: {\n ...run,\n revision: run.revision + 1,\n status: 'cancelled',\n updatedAt: dependencies.clock.now().toISOString(),\n },\n });\n if (!transitioned) return false;\n await dependencies.persistence.cancelRunWorkAsync(runId);\n return true;\n};\n\n/** Leave completed and terminally failed runs immutable. */\nconst isTerminal = (run: FlowDurableRun): boolean =>\n run.status === 'failed' || run.status === 'succeeded';\n"]}
@@ -0,0 +1,12 @@
1
+ import type { FlowDefinitionRef, FlowDurableRun } from '../../../types/durableExecution.js';
2
+ import type { FlowClock } from '../../../types/execution.js';
3
+ import type { FlowPersistencePort } from '../ports/FlowPersistencePort.js';
4
+ /*** Persist a new durable run without executing an operation or provider effect. */
5
+ export declare const createDurableRunAsync: (input: {
6
+ readonly flow: FlowDefinitionRef;
7
+ readonly id: string;
8
+ }, dependencies: {
9
+ readonly clock: FlowClock;
10
+ readonly persistence: FlowPersistencePort;
11
+ }) => Promise<FlowDurableRun>;
12
+ //# sourceMappingURL=createDurableRunAsync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createDurableRunAsync.d.ts","sourceRoot":"","sources":["../../../../src/features/execution/application/createDurableRunAsync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAC5F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAE7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAE3E,oFAAoF;AACpF,eAAO,MAAM,qBAAqB,GAChC,OAAO;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EAChE,cAAc;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAA;CAAE,KACrF,OAAO,CAAC,cAAc,CAIxB,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { createDurableRun } from '../domain/createDurableRun.js';
2
+ /*** Persist a new durable run without executing an operation or provider effect. */
3
+ export const createDurableRunAsync = async (input, dependencies) => {
4
+ const run = createDurableRun(input, dependencies.clock.now());
5
+ await dependencies.persistence.createRunAsync(run);
6
+ return run;
7
+ };
8
+ //# sourceMappingURL=createDurableRunAsync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createDurableRunAsync.js","sourceRoot":"","sources":["../../../../src/features/execution/application/createDurableRunAsync.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGjE,oFAAoF;AACpF,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,EACxC,KAAgE,EAChE,YAAsF,EAC7D,EAAE;IAC3B,MAAM,GAAG,GAAG,gBAAgB,CAAC,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;IAC9D,MAAM,YAAY,CAAC,WAAW,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;IACnD,OAAO,GAAG,CAAC;AACb,CAAC,CAAC","sourcesContent":["import type { FlowDefinitionRef, FlowDurableRun } from '../../../types/durableExecution.js';\nimport type { FlowClock } from '../../../types/execution.js';\nimport { createDurableRun } from '../domain/createDurableRun.js';\nimport type { FlowPersistencePort } from '../ports/FlowPersistencePort.js';\n\n/*** Persist a new durable run without executing an operation or provider effect. */\nexport const createDurableRunAsync = async (\n input: { readonly flow: FlowDefinitionRef; readonly id: string },\n dependencies: { readonly clock: FlowClock; readonly persistence: FlowPersistencePort },\n): Promise<FlowDurableRun> => {\n const run = createDurableRun(input, dependencies.clock.now());\n await dependencies.persistence.createRunAsync(run);\n return run;\n};\n"]}
@@ -0,0 +1,52 @@
1
+ import type { FlowDurableRun, FlowDurableWork, FlowRetryPolicy } from '../../../types/durableExecution.js';
2
+ import type { FlowClock } from '../../../types/execution.js';
3
+ import type { FlowSecurityPort } from '../../../types/security.js';
4
+ import type { FlowPersistencePort } from '../ports/FlowPersistencePort.js';
5
+ /** Claim bounded durable work and execute it with fenced recovery, retry, and approval handling. */
6
+ export declare const runDurableWorkerAsync: (input: {
7
+ readonly approvalPollIntervalMs: number;
8
+ readonly concurrency: number;
9
+ readonly leaseDurationMs: number;
10
+ readonly limit: number;
11
+ readonly retry: FlowRetryPolicy;
12
+ readonly workerId: string;
13
+ }, dependencies: {
14
+ readonly clock: FlowClock;
15
+ readonly effects: FlowDurableEffectPort;
16
+ readonly persistence: FlowPersistencePort;
17
+ readonly security?: FlowSecurityPort;
18
+ }) => Promise<FlowWorkerResult>;
19
+ /** Application-owned effect boundary; adapters must apply the supplied idempotency key at their side-effect boundary. */
20
+ export interface FlowDurableEffectPort {
21
+ readonly compensateAsync?: (input: {
22
+ readonly run: FlowDurableRun;
23
+ readonly work: FlowDurableWork;
24
+ }) => Promise<void>;
25
+ readonly executeAsync: (input: {
26
+ readonly idempotencyKey: string;
27
+ readonly run: FlowDurableRun;
28
+ readonly work: FlowDurableWork;
29
+ }) => Promise<void>;
30
+ /**
31
+ * Query the provider's idempotency boundary before a protected replay. A provider must return
32
+ * `delivered` only when the stable key proves that the original effect completed.
33
+ */
34
+ readonly reconcileAsync?: (input: {
35
+ readonly idempotencyKey: string;
36
+ readonly run: FlowDurableRun;
37
+ readonly work: FlowDurableWork;
38
+ }) => Promise<'delivered' | 'unknown'>;
39
+ }
40
+ /** Observable outcome for every delivery claimed by one bounded worker tick. */
41
+ export interface FlowWorkerOutcome {
42
+ readonly error?: string;
43
+ readonly status: 'already_completed' | 'cancelled' | 'failed' | 'lost_lease' | 'retry_scheduled' | 'succeeded' | 'waiting_approval';
44
+ readonly workId: string;
45
+ }
46
+ /** Summary of one worker tick; delivery remains at-least-once until fenced completion. */
47
+ export interface FlowWorkerResult {
48
+ readonly claimedWorkIds: readonly string[];
49
+ readonly outcomes: readonly FlowWorkerOutcome[];
50
+ readonly recoveredWorkIds: readonly string[];
51
+ }
52
+ //# sourceMappingURL=runDurableWorkerAsync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runDurableWorkerAsync.d.ts","sourceRoot":"","sources":["../../../../src/features/execution/application/runDurableWorkerAsync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,eAAe,EAEhB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAGnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAE3E,oGAAoG;AACpG,eAAO,MAAM,qBAAqB,GAChC,OAAO;IACL,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,EACD,cAAc;IACZ,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,qBAAqB,CAAC;IACxC,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAC;IAC1C,QAAQ,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;CACtC,KACA,OAAO,CAAC,gBAAgB,CAc1B,CAAC;AA6PF,yHAAyH;AACzH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE;QACjC,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;QAC7B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;KAChC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,QAAQ,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE;QAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;QAChC,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;QAC7B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;KAChC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE;QAChC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;QAChC,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;QAC7B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;KAChC,KAAK,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC;CACxC;AAED,gFAAgF;AAChF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EACX,mBAAmB,GACnB,WAAW,GACX,QAAQ,GACR,YAAY,GACZ,iBAAiB,GACjB,WAAW,GACX,kBAAkB,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,0FAA0F;AAC1F,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAChD,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9C"}