@voltro/workflow 0.85.0 → 0.86.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 (85) hide show
  1. package/CHANGELOG.md +666 -0
  2. package/THIRD-PARTY-NOTICES.md +5 -33
  3. package/dist/admission.js +140 -0
  4. package/dist/admissionDrainer.js +705 -0
  5. package/dist/admissionGate.js +115 -0
  6. package/dist/admissionStore.js +347 -0
  7. package/dist/admissionSubmission.js +297 -0
  8. package/dist/admissionTables.js +137 -0
  9. package/dist/admissionTransaction.js +21 -0
  10. package/dist/awaitEvent.js +82 -0
  11. package/dist/budgetHold.js +136 -0
  12. package/dist/cancelOnPlan.js +75 -0
  13. package/dist/cancelOnSweep.js +188 -0
  14. package/dist/cancelPendingStart.js +50 -0
  15. package/dist/cluster.js +8 -2
  16. package/dist/clusterCron.js +11 -0
  17. package/dist/clusterDiagnostics.js +20 -0
  18. package/dist/clusterDiagnosticsSql.js +145 -0
  19. package/dist/clusterLayer.js +110 -0
  20. package/dist/clusterMigrationGate.js +61 -0
  21. package/dist/clusterRunnerLayer.js +21 -0
  22. package/dist/clusterTestSuite.js +101 -101
  23. package/dist/controlMonitor.js +13 -0
  24. package/dist/controlRequest.js +367 -0
  25. package/dist/executionRefusal.js +162 -0
  26. package/dist/failureHandoff.js +183 -0
  27. package/dist/failureMessage.js +12 -0
  28. package/dist/failureRecovery.js +50 -0
  29. package/dist/failureReportStore.js +60 -0
  30. package/dist/flowControl.js +142 -0
  31. package/dist/forwardStartReceipts.js +41 -0
  32. package/dist/inMemoryLayer.js +7 -0
  33. package/dist/inMemoryRecorder.js +56 -0
  34. package/dist/index.d.ts +82 -8
  35. package/dist/index.js +49 -5
  36. package/dist/inspectWorkflow.js +101 -0
  37. package/dist/{journalEncryption-DTyhUzaS.js → journalEncryption.js} +30 -37
  38. package/dist/messages.js +92 -0
  39. package/dist/nondeterminism.js +51 -0
  40. package/dist/parentClosure.js +141 -0
  41. package/dist/parentClosureRecovery.js +54 -0
  42. package/dist/pendingSubmission.js +85 -0
  43. package/dist/prepareSubmissionDispatch.js +17 -0
  44. package/dist/preparedExecution.js +14 -0
  45. package/dist/primitives.js +225 -2
  46. package/dist/queuedCancellationRequest.js +364 -0
  47. package/dist/queuedControl.js +21 -0
  48. package/dist/recorder.js +11 -0
  49. package/dist/residentExecutionEvidence.js +64 -0
  50. package/dist/retryPolicy.js +57 -0
  51. package/dist/runEventsTable.js +17 -0
  52. package/dist/runGeneration.js +345 -0
  53. package/dist/runRecording.js +615 -0
  54. package/dist/runStepsTable.js +28 -0
  55. package/dist/runnerStorageGuard.js +29 -0
  56. package/dist/runsTable.js +42 -0
  57. package/dist/scheduleDeclaration.js +44 -0
  58. package/dist/signalHint.js +23 -0
  59. package/dist/signals.js +70 -0
  60. package/dist/signalsSuspending.js +57 -0
  61. package/dist/sqliteWorkflowOwnership.js +26 -0
  62. package/dist/stalenessSweep.js +191 -0
  63. package/dist/startContextsTable.js +20 -0
  64. package/dist/startReceipt.js +143 -0
  65. package/dist/submissionRecovery.js +122 -0
  66. package/dist/submissionRejection.js +10 -0
  67. package/dist/submissionRetry.js +71 -0
  68. package/dist/suspensionUnavailable.js +9 -0
  69. package/dist/updates.js +87 -0
  70. package/dist/watermarkTable.js +9 -0
  71. package/dist/webCrypto.js +17 -0
  72. package/dist/workflowDelivery.js +24 -0
  73. package/dist/workflowJournal.js +124 -0
  74. package/dist/workflowJournalReset.js +111 -0
  75. package/dist/workflowReadSelections.js +12 -0
  76. package/dist/workflowRestartDispatch.js +29 -0
  77. package/dist/workflowRestartObservation.js +60 -0
  78. package/dist/workflowRestartPreparation.js +109 -0
  79. package/dist/workflowRestartReservation.js +426 -0
  80. package/dist/workflowResumeDispatch.js +29 -0
  81. package/package.json +4 -4
  82. package/dist/cluster-F_bhqVP7.js +0 -679
  83. package/dist/primitives-DX04WTWh.js +0 -507
  84. package/dist/src-C2NEyUM8.js +0 -3925
  85. package/dist/workflowReadSelections-CZAUjP7f.js +0 -2096
@@ -0,0 +1,345 @@
1
+ import { isWorkflowEncrypted as e, sealErrorText as t, sealJournalValue as n } from "./journalEncryption.js";
2
+ import { recordWorkflowFailureInTransaction as r } from "./failureReportStore.js";
3
+ import { handoffWorkflowFailureInTransaction as i, isFailureDeliveryExecution as a } from "./failureHandoff.js";
4
+ import { dispatchedExecutionSubmissions as o } from "./admissionSubmission.js";
5
+ import { ADMISSIONS_TABLE as s, releaseLease as c } from "./admissionStore.js";
6
+ import { finishTimeoutCause as l, hasPendingWorkflowCancellation as u, readPendingWorkflowCancellations as d, settleWorkflowControlsInTransaction as f, settleWorkflowResumesInTransaction as p } from "./controlRequest.js";
7
+ import { stageParentClosureAtChildEntry as m } from "./parentClosure.js";
8
+ import { consumeWorkflowRestartInTransaction as h } from "./workflowRestartReservation.js";
9
+ import { stageQueuedCancellationsAtRunEntry as g } from "./queuedCancellationRequest.js";
10
+ import { Schema as _ } from "effect";
11
+ import { derivedRowId as v, eq as y } from "@voltro/database";
12
+ import { createHash as b } from "node:crypto";
13
+ //#region src/runGeneration.ts
14
+ var x = "_voltro_workflow_runs", S = _.NullOr(_.Struct({
15
+ requestId: _.NonEmptyString,
16
+ restartToken: _.NullOr(_.NonEmptyString)
17
+ })), C = async (e, t, n, r) => {
18
+ if (typeof t.id != "string") throw Error("Child entry has no allocated run identity");
19
+ let i = {
20
+ runId: t.id,
21
+ workflowName: n.workflowName,
22
+ executionId: n.executionId,
23
+ generation: n.generation
24
+ }, a = await g(e, i), o = await m(e, t);
25
+ if (!a && !o) return;
26
+ let s = await k(e, {
27
+ owner: i,
28
+ now: n.now,
29
+ durationMs: 0,
30
+ outcome: { status: "cancelled" }
31
+ }, "running");
32
+ if (s.kind !== "committed") throw Error("Child entry cancellation lost its resident owner");
33
+ return {
34
+ kind: "cancelled",
35
+ row: s.row,
36
+ previous: r,
37
+ terminalEvent: s.terminalEvent
38
+ };
39
+ }, w = async (e, t) => {
40
+ let n = await h(e, t);
41
+ return {
42
+ ...await C(e, n.row, t, n.previous) ?? {
43
+ kind: "entered",
44
+ row: n.row,
45
+ previous: n.previous
46
+ },
47
+ restartEvent: n.event
48
+ };
49
+ }, T = async (e, t) => {
50
+ if ([
51
+ t.workflowName,
52
+ t.executionId,
53
+ t.generation
54
+ ].some((e) => e.length === 0) || typeof t.initialRow.id != "string" || t.initialRow.id.length === 0 || !Number.isFinite(t.now)) throw Error("Workflow entry requires allocated identities and a finite timestamp");
55
+ let n = (await e.query({
56
+ table: x,
57
+ predicate: y("executionId", t.executionId),
58
+ lock: "update",
59
+ order: [],
60
+ take: 1,
61
+ skip: void 0,
62
+ projection: void 0
63
+ }))[0], r = _.decodeUnknownSync(S)(t.delivery ?? null), i = (r) => C(e, r, t, n);
64
+ if (n === void 0) {
65
+ if (r !== null && r.restartToken !== null) throw Error("Workflow restart delivery has no resident authorization");
66
+ if ((await e.query({
67
+ table: "_voltro_workflow_parent_closures",
68
+ predicate: y("executionId", t.executionId),
69
+ lock: "update",
70
+ order: [],
71
+ take: 1,
72
+ skip: void 0,
73
+ projection: ["id"]
74
+ })).length !== 0) throw Error("Workflow execution is already terminal; restore its engine journal instead of re-entering the body");
75
+ let n = await e.insert(x, {
76
+ ...t.initialRow,
77
+ tag: t.workflowName,
78
+ executionId: t.executionId,
79
+ runnerGeneration: t.generation,
80
+ engineDelivery: r,
81
+ status: "running",
82
+ runnerEnteredAt: new Date(t.now),
83
+ reclaimCount: 0
84
+ });
85
+ return await i(n) ?? {
86
+ kind: "created",
87
+ row: n,
88
+ previous: void 0
89
+ };
90
+ }
91
+ if (n.tag !== t.workflowName || typeof n.id != "string") throw Error("Workflow entry execution belongs to a different resident run");
92
+ let a = _.decodeUnknownSync(S)(n.engineDelivery ?? null);
93
+ if (a?.requestId !== r?.requestId || a?.restartToken !== r?.restartToken) {
94
+ if (n.status === "failed" && r !== null && r.restartToken !== null && a?.requestId === r.requestId) return w(e, {
95
+ runId: n.id,
96
+ workflowName: t.workflowName,
97
+ executionId: t.executionId,
98
+ generation: t.generation,
99
+ delivery: r,
100
+ now: t.now
101
+ });
102
+ throw Error("Workflow delivery does not match its resident owner");
103
+ }
104
+ if (n.status === "cancelled") return {
105
+ kind: "already-cancelled",
106
+ row: n,
107
+ previous: n
108
+ };
109
+ if (n.status === "succeeded" || n.status === "failed") throw Error("Workflow execution is already terminal; restore its engine journal instead of re-entering the body");
110
+ if (n.status === "suspended" && n.runnerEnteredAt === null && typeof n.runnerGeneration == "string") {
111
+ let r = {
112
+ runId: n.id,
113
+ workflowName: t.workflowName,
114
+ executionId: t.executionId,
115
+ generation: n.runnerGeneration
116
+ };
117
+ if (await u(e, r)) {
118
+ let i = await k(e, {
119
+ owner: r,
120
+ now: t.now,
121
+ durationMs: 0,
122
+ outcome: { status: "cancelled" }
123
+ }, "suspended");
124
+ if (i.kind !== "committed") throw Error("Suspended cancellation lost its resident owner");
125
+ return {
126
+ kind: "cancelled",
127
+ row: i.row,
128
+ previous: n,
129
+ terminalEvent: i.terminalEvent
130
+ };
131
+ }
132
+ }
133
+ if (n.runnerGeneration === t.generation) return {
134
+ kind: "replayed",
135
+ row: n,
136
+ previous: void 0
137
+ };
138
+ let o = n.runnerEnteredAt !== null && n.runnerEnteredAt !== void 0, s = n.reclaimCount;
139
+ if (typeof s != "number" || !Number.isSafeInteger(s) || s < 0) throw Error("Workflow entry has an invalid resident reclaim counter");
140
+ let c = o ? s + 1 : 0;
141
+ if (!Number.isSafeInteger(c)) throw Error("Workflow reclaim counter exhausted");
142
+ let l = await e.update(x, n.id, {
143
+ runnerGeneration: t.generation,
144
+ runnerEnteredAt: new Date(t.now),
145
+ reclaimCount: c,
146
+ status: "running",
147
+ errorTag: null,
148
+ errorMessage: null,
149
+ output: null,
150
+ completedAt: null
151
+ });
152
+ if (l === null) throw Error("Workflow run disappeared while entering its new generation");
153
+ let d = await i(l);
154
+ return d === void 0 ? (n.status === "suspended" && n.runnerEnteredAt === null && typeof n.runnerGeneration == "string" && await p(e, {
155
+ runId: n.id,
156
+ workflowName: t.workflowName,
157
+ executionId: t.executionId,
158
+ generation: n.runnerGeneration
159
+ }, t.generation, t.now), {
160
+ kind: "entered",
161
+ row: l,
162
+ previous: n
163
+ }) : d;
164
+ }, E = async (e, t) => {
165
+ if ([
166
+ t.runId,
167
+ t.workflowName,
168
+ t.executionId,
169
+ t.generation
170
+ ].some((e) => e.length === 0)) throw Error("Workflow runner ownership requires non-empty identities");
171
+ let n = (await e.query({
172
+ table: x,
173
+ predicate: y("id", t.runId),
174
+ lock: "update",
175
+ order: [],
176
+ take: 1,
177
+ skip: void 0,
178
+ projection: void 0
179
+ }))[0];
180
+ if (n === void 0 || n.tag !== t.workflowName || n.executionId !== t.executionId) throw Error("Workflow runner ownership does not match its resident run");
181
+ return n;
182
+ }, D = async (e, t, n) => {
183
+ let r = await E(e, t);
184
+ if (r.runnerGeneration !== t.generation || r.status !== "suspended" || r.runnerEnteredAt !== null || !await u(e, t)) return !1;
185
+ if ((await k(e, {
186
+ owner: t,
187
+ now: n,
188
+ durationMs: 0,
189
+ outcome: { status: "cancelled" }
190
+ }, "suspended")).kind !== "committed") throw Error("Suspended cancellation lost its resident owner");
191
+ return !0;
192
+ }, O = async (e, t, n) => {
193
+ let r = await E(e, t);
194
+ if (r.runnerGeneration !== t.generation) return { kind: "superseded" };
195
+ if (r.status !== "running") return { kind: "not-running" };
196
+ let i = await e.update(x, t.runId, {
197
+ status: "suspended",
198
+ runnerEnteredAt: null,
199
+ errorTag: n.errorTag,
200
+ errorMessage: n.errorMessage
201
+ });
202
+ if (i === null) throw Error("Workflow run disappeared during suspension recording");
203
+ return {
204
+ kind: "committed",
205
+ row: i
206
+ };
207
+ }, k = async (u, p, m) => {
208
+ let h = await E(u, p.owner);
209
+ if (h.runnerGeneration !== p.owner.generation) return { kind: "superseded" };
210
+ if (h.status !== m) return { kind: "not-running" };
211
+ if (!Number.isFinite(p.now) || !Number.isFinite(p.durationMs) || p.durationMs < 0) throw Error("Workflow completion requires finite time and non-negative duration");
212
+ let g = p.outcome, S = null;
213
+ if (g.status === "failed" && (S = `wffr_${b("sha256").update(JSON.stringify([
214
+ "run",
215
+ p.owner.workflowName,
216
+ p.owner.runId,
217
+ p.owner.generation
218
+ ])).digest("hex").slice(0, 40)}`, await r(u, {
219
+ id: S,
220
+ sourceKind: "run",
221
+ sourceId: p.owner.runId,
222
+ workflowName: p.owner.workflowName,
223
+ tenantId: g.tenantId,
224
+ handler: await a(u, p.owner.executionId, p.owner.workflowName, g.tenantId) ? null : g.handler,
225
+ callerContext: g.callerContext,
226
+ report: {
227
+ workflow: p.owner.workflowName,
228
+ payload: h.payload,
229
+ errorTag: g.errorTag,
230
+ errorMessage: g.errorMessage,
231
+ runId: p.owner.runId,
232
+ executionId: p.owner.executionId,
233
+ reason: "the run failed",
234
+ failedAt: p.now
235
+ }
236
+ }), await i(u, S, p.now)), g.status === "cancelled") {
237
+ let e = (await d((e) => u.query({
238
+ ...e,
239
+ lock: "update"
240
+ }), p.owner)).map((e) => ({
241
+ request: e,
242
+ cause: l(e)
243
+ })).find((e) => e.cause !== void 0);
244
+ if (e !== void 0 && e.cause !== void 0) {
245
+ let t = _.decodeUnknownOption(_.Struct({ tenantId: _.NullOr(_.String) }))(h.subject), n = e.request.owner?.tenantId ?? (t._tag === "Some" ? t.value.tenantId : null);
246
+ S = `wffr_${b("sha256").update(JSON.stringify([
247
+ "finish-timeout",
248
+ p.owner.workflowName,
249
+ p.owner.runId,
250
+ p.owner.generation
251
+ ])).digest("hex").slice(0, 40)}`, await r(u, {
252
+ id: S,
253
+ sourceKind: "run",
254
+ sourceId: p.owner.runId,
255
+ workflowName: p.owner.workflowName,
256
+ tenantId: n,
257
+ handler: await a(u, p.owner.executionId, p.owner.workflowName, n) ? null : e.cause.detail.onFailure,
258
+ callerContext: {
259
+ subject: h.subject ?? null,
260
+ traceId: h.traceId ?? null,
261
+ source: h.source ?? null,
262
+ parentExecutionId: h.parentExecutionId ?? null,
263
+ parentGeneration: h.parentGeneration ?? null,
264
+ parentClosePolicy: h.parentClosePolicy ?? null
265
+ },
266
+ report: {
267
+ workflow: p.owner.workflowName,
268
+ payload: h.payload,
269
+ errorTag: null,
270
+ errorMessage: null,
271
+ runId: p.owner.runId,
272
+ executionId: p.owner.executionId,
273
+ reason: e.cause.reason,
274
+ failedAt: p.now
275
+ }
276
+ }), await i(u, S, p.now);
277
+ }
278
+ }
279
+ let C = await u.update(x, p.owner.runId, {
280
+ status: g.status,
281
+ completedAt: new Date(p.now),
282
+ durationMs: p.durationMs,
283
+ cancelled: g.status === "cancelled",
284
+ runnerEnteredAt: null,
285
+ output: g.status === "succeeded" ? e(p.owner.workflowName) ? n(g.output, `workflow '${p.owner.workflowName}' result`) : g.output : null,
286
+ errorTag: g.status === "failed" ? g.errorTag : null,
287
+ errorMessage: g.status === "failed" ? t(p.owner.workflowName, g.errorMessage) : null
288
+ });
289
+ if (C === null) throw Error("Workflow run disappeared during terminal recording");
290
+ await u.insert("_voltro_workflow_parent_closures", {
291
+ id: v("wfpc", JSON.stringify([p.owner.runId, p.owner.generation])),
292
+ runId: p.owner.runId,
293
+ workflowName: p.owner.workflowName,
294
+ executionId: p.owner.executionId,
295
+ generation: p.owner.generation,
296
+ parentStatus: g.status,
297
+ closedAt: new Date(p.now),
298
+ afterChildId: null,
299
+ scanHasOpenChildren: !1,
300
+ completedAt: null
301
+ });
302
+ let w = await u.insert("_voltro_workflow_run_events", {
303
+ id: v("wfev", JSON.stringify([
304
+ "terminal",
305
+ p.owner.runId,
306
+ p.owner.generation
307
+ ])),
308
+ runId: p.owner.runId,
309
+ eventType: `run-${g.status}`,
310
+ occurredAt: new Date(p.now),
311
+ stepName: null,
312
+ attempt: null,
313
+ payload: {
314
+ executionId: p.owner.executionId,
315
+ generation: p.owner.generation,
316
+ durationMs: p.durationMs,
317
+ ...g.status === "failed" ? {
318
+ errorTag: g.errorTag,
319
+ errorMessage: t(p.owner.workflowName, g.errorMessage)
320
+ } : {}
321
+ }
322
+ });
323
+ await f(u, p), await c(u, p.owner.executionId, p.now);
324
+ for await (let e of o(u, p.owner.executionId, p.owner.workflowName)) {
325
+ let [t] = await u.query({
326
+ table: s,
327
+ predicate: y("id", e.ledgerId),
328
+ lock: "update",
329
+ order: [],
330
+ take: 1,
331
+ skip: void 0,
332
+ projection: void 0
333
+ });
334
+ if (t === void 0 || t.workflowName !== e.workflowName || t.tenantId !== e.tenantId || t.outcome !== "admitted" || t.executionId !== null && t.executionId !== p.owner.executionId) throw Error("Workflow terminal reservation does not match its resident submission");
335
+ if (t.releasedAt === null && await u.update("_voltro_workflow_admissions", e.ledgerId, { releasedAt: new Date(p.now) }) === null) throw Error("Workflow terminal reservation disappeared during release");
336
+ }
337
+ return {
338
+ kind: "committed",
339
+ row: C,
340
+ failureId: S,
341
+ terminalEvent: w
342
+ };
343
+ }, A = (e, t) => k(e, t, "running");
344
+ //#endregion
345
+ export { D as cancelSuspendedWorkflowRunInTransaction, A as completeWorkflowRunGenerationInTransaction, w as enterWorkflowRestartGenerationInTransaction, T as enterWorkflowRunGenerationInTransaction, O as suspendWorkflowRunGenerationInTransaction };