@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,60 @@
1
+ import "./workflowDelivery.js";
2
+ import { activityPrimaryKey as e, classifyRunReply as t, runEntityAddress as n } from "./workflowJournal.js";
3
+ import { Effect as r, Option as i, Schema as a } from "effect";
4
+ import { SqlClient as o } from "effect/sql";
5
+ import { MessageStorage as s, Sharding as c } from "effect/cluster";
6
+ //#region src/workflowRestartObservation.ts
7
+ var l = a.fromJsonString(a.Record(a.String, a.String)), u = (u) => r.gen(function* () {
8
+ if (!/^[1-9]\d*$/.test(u.delivery.requestId) || u.executionId.length === 0 || u.activities.some((e) => e.name.length === 0 || !Number.isSafeInteger(e.attempt) || e.attempt < 1)) return yield* r.die(/* @__PURE__ */ Error("Restart observation requires an exact delivery and valid activity attempts"));
9
+ let d = (yield* o.SqlClient).withoutTransforms(), f = yield* s.MessageStorage, p = yield* c.Sharding, m = n(u.workflow, u.executionId, p), h = (e, t) => r.gen(function* () {
10
+ let [n] = yield* d`SELECT m.entity_type, m.entity_id, m.tag, m.headers, r.id AS reply_id, r.payload
11
+ FROM cluster_messages m
12
+ LEFT JOIN cluster_replies r ON r.id = m.last_reply_id AND r.request_id = m.id AND r.kind = 0
13
+ WHERE m.id = ${e} AND m.kind = 0`;
14
+ if (n !== void 0) return n.entity_type !== `Workflow/${u.workflow._tag}` || n.entity_id !== u.executionId || n.tag !== t ? yield* r.die(/* @__PURE__ */ Error("Restart observation belongs to another execution or procedure")) : n;
15
+ }), g = yield* h(u.delivery.requestId, "run");
16
+ if (g === void 0) return {
17
+ kind: "unavailable",
18
+ reason: "no-journal"
19
+ };
20
+ if (((yield* r.sync(() => a.decodeUnknownSync(l)(g.headers)))["voltro-workflow-restart"] ?? null) !== u.delivery.restartToken) return {
21
+ kind: "unavailable",
22
+ reason: "delivery-changed"
23
+ };
24
+ if (g.reply_id === null || g.payload === null) return {
25
+ kind: "unavailable",
26
+ reason: "no-reply"
27
+ };
28
+ let _ = t(yield* r.sync(() => JSON.parse(g.payload)));
29
+ if (_ !== "failed") return {
30
+ kind: "unavailable",
31
+ reason: _
32
+ };
33
+ let v = /* @__PURE__ */ new Map([[u.delivery.requestId, String(g.reply_id)]]);
34
+ for (let t of u.activities) {
35
+ let n = yield* f.requestIdForPrimaryKey({
36
+ address: m,
37
+ tag: "activity",
38
+ id: e(t.name, t.attempt)
39
+ });
40
+ if (i.isNone(n)) return {
41
+ kind: "unavailable",
42
+ reason: "activity-not-found"
43
+ };
44
+ let r = String(n.value), a = yield* h(r, "activity");
45
+ if (a?.reply_id == null) return {
46
+ kind: "unavailable",
47
+ reason: "activity-not-found"
48
+ };
49
+ v.set(r, String(a.reply_id));
50
+ }
51
+ return {
52
+ kind: "observed",
53
+ replies: [...v].map(([e, t]) => ({
54
+ requestId: e,
55
+ replyId: t
56
+ }))
57
+ };
58
+ }).pipe(r.provideService(o.SafeIntegers, !0));
59
+ //#endregion
60
+ export { u as observeWorkflowRestartReplies };
@@ -0,0 +1,109 @@
1
+ import { readWorkflowRestartCommandInTransaction as e, readWorkflowRestartReservationInTransaction as t, reserveWorkflowRestartInTransaction as n } from "./workflowRestartReservation.js";
2
+ import { Schema as r } from "effect";
3
+ import { eq as i } from "@voltro/database";
4
+ //#region src/workflowRestartPreparation.ts
5
+ var a = async (t) => {
6
+ if (t.workflow._tag !== t.command.workflowName || t.control.workflowName !== t.command.workflowName) throw Error("Restart preparation requires the matching workflow");
7
+ let n = await t.transaction(async (n) => {
8
+ let a = await e(n, t.command);
9
+ if (a !== void 0) return {
10
+ kind: "accepted",
11
+ request: a
12
+ };
13
+ let [o] = await n.query({
14
+ table: "_voltro_workflow_runs",
15
+ predicate: i("id", t.command.runId),
16
+ lock: "update",
17
+ order: [],
18
+ take: 1,
19
+ skip: void 0,
20
+ projection: ["runnerGeneration"]
21
+ });
22
+ return {
23
+ kind: "captured",
24
+ generation: r.decodeUnknownSync(r.NonEmptyString)(o?.runnerGeneration)
25
+ };
26
+ });
27
+ return n.kind === "accepted" ? {
28
+ kind: "reserved",
29
+ request: n.request,
30
+ reused: !0
31
+ } : s({
32
+ ...t,
33
+ command: {
34
+ ...t.command,
35
+ generation: n.generation
36
+ }
37
+ });
38
+ }, o = r.Struct({
39
+ requestId: r.NonEmptyString,
40
+ restartToken: r.NullOr(r.NonEmptyString)
41
+ }), s = async (e) => {
42
+ let { command: a } = e;
43
+ if (e.workflow._tag !== a.workflowName || e.control.workflowName !== a.workflowName) throw Error("Restart preparation requires the matching workflow");
44
+ let s = await e.transaction(async (n) => {
45
+ let s = await t(n, a);
46
+ if (s !== void 0) return {
47
+ kind: "accepted",
48
+ request: s
49
+ };
50
+ let [c] = await n.query({
51
+ table: "_voltro_workflow_runs",
52
+ predicate: i("id", a.runId),
53
+ lock: "update",
54
+ order: [],
55
+ take: 1,
56
+ skip: void 0,
57
+ projection: void 0
58
+ });
59
+ if (c === void 0 || c.tag !== a.workflowName || c.runnerGeneration !== a.generation || c.status !== "failed" || c.discardedAt != null || typeof c.executionId != "string" || c.executionId.length === 0) throw Error("Restart preparation requires its failed resident generation");
60
+ if ((r.decodeUnknownSync(r.NullOr(r.Struct({ tenantId: r.NullOr(r.NonEmptyString) })))(c.subject ?? null)?.tenantId ?? null) !== a.tenantId) throw Error("Restart preparation belongs to another tenant");
61
+ return {
62
+ kind: "observe",
63
+ executionId: c.executionId,
64
+ delivery: r.decodeUnknownSync(o)(c.engineDelivery),
65
+ activities: await e.readActivities(n)
66
+ };
67
+ });
68
+ if (s.kind === "accepted") return {
69
+ kind: "reserved",
70
+ request: s.request,
71
+ reused: !0
72
+ };
73
+ let c = await e.observe({
74
+ workflow: e.workflow,
75
+ executionId: s.executionId,
76
+ delivery: s.delivery,
77
+ activities: s.activities
78
+ });
79
+ return e.transaction(async (r) => {
80
+ let o = await t(r, a);
81
+ if (o !== void 0) return {
82
+ kind: "reserved",
83
+ request: o,
84
+ reused: !0
85
+ };
86
+ if (c.kind === "unavailable") return c;
87
+ let [l] = await r.query({
88
+ table: "_voltro_workflow_runs",
89
+ predicate: i("id", a.runId),
90
+ lock: "update",
91
+ order: [],
92
+ take: 1,
93
+ skip: void 0,
94
+ projection: ["executionId"]
95
+ });
96
+ if (l?.executionId !== s.executionId) throw Error("Restart execution changed during observation");
97
+ return n(r, {
98
+ ...a,
99
+ token: e.createToken(),
100
+ delivery: s.delivery,
101
+ replies: c.replies,
102
+ control: e.control,
103
+ paused: await e.readPaused(r),
104
+ now: e.now
105
+ });
106
+ });
107
+ };
108
+ //#endregion
109
+ export { s as prepareWorkflowRestart, a as prepareWorkflowRestartCommand };
@@ -0,0 +1,426 @@
1
+ import { resolveAdmissionKeys as e } from "./admission.js";
2
+ import { decodePendingPayload as t, evaluateAdmission as n, recordAdmission as r } from "./admissionStore.js";
3
+ import { resetExpectedWorkflowReplies as i } from "./workflowJournalReset.js";
4
+ import { Effect as a, Schema as o } from "effect";
5
+ import { WorkflowRestartObservationSchema as s, WorkflowRestartSelectionSchema as c, WorkflowRestartTargetSchema as l } from "@voltro/protocol";
6
+ import { and as u, derivedRowId as d, eq as f, id as p, integer as m, json as h, or as g, table as _, text as v, timestamp as y } from "@voltro/database";
7
+ //#region src/workflowRestartReservation.ts
8
+ var b = _("_voltro_workflow_restart_requests", {
9
+ id: p({ prefix: "wfrs" }),
10
+ runId: v(),
11
+ workflowName: v(),
12
+ executionId: v(),
13
+ generation: v(),
14
+ token: v(),
15
+ ledgerId: v(),
16
+ tenantId: v().nullable(),
17
+ delivery: h(),
18
+ replies: h(),
19
+ selection: h().nullable(),
20
+ eviction: h().nullable(),
21
+ requestedAt: y(),
22
+ dispatchAttempts: m().default(0),
23
+ enteredGeneration: v().nullable()
24
+ }).unique(["runId", "generation"]).unique(["runId", "token"]).nonReactive(), x = o.NonEmptyString, S = o.Struct({
25
+ requestId: x,
26
+ restartToken: o.NullOr(x)
27
+ }), C = o.Struct({
28
+ requestId: x,
29
+ replyId: x
30
+ }), w = c, T = _("_voltro_workflow_restart_withdrawals", {
31
+ id: p({ prefix: "wfrw" }),
32
+ runId: v(),
33
+ workflowName: v(),
34
+ tenantId: v().nullable(),
35
+ selection: h(),
36
+ withdrawnAt: y()
37
+ }).nonReactive(), E = o.Struct({
38
+ id: o.NonEmptyString,
39
+ runId: o.NonEmptyString,
40
+ workflowName: o.NonEmptyString,
41
+ tenantId: o.NullOr(o.NonEmptyString),
42
+ selection: w,
43
+ withdrawnAt: o.Date
44
+ }), D = async (e, t) => {
45
+ let n = o.decodeUnknownSync(l)(t), [r] = await e.query({
46
+ table: T.tableName,
47
+ predicate: f("id", n.requestId),
48
+ lock: "update",
49
+ order: [],
50
+ take: 1,
51
+ skip: void 0,
52
+ projection: void 0
53
+ });
54
+ if (r === void 0) return;
55
+ let i = o.decodeUnknownSync(E)(r);
56
+ if (i.runId !== n.runId) throw Error("Restart identity belongs to a different command");
57
+ if (!Number.isFinite(i.withdrawnAt.getTime())) throw Error("Invalid restart withdrawal timestamp");
58
+ return i;
59
+ }, O = async (e, t) => {
60
+ if (await D(e, t) !== void 0) throw Error("Restart request was permanently withdrawn");
61
+ }, k = o.Struct({
62
+ id: x,
63
+ runId: x,
64
+ workflowName: x,
65
+ executionId: x,
66
+ generation: x,
67
+ token: x,
68
+ ledgerId: x,
69
+ tenantId: o.NullOr(x),
70
+ delivery: S,
71
+ replies: o.Array(C),
72
+ selection: w,
73
+ eviction: o.NullOr(o.Struct({
74
+ executionId: x,
75
+ runId: o.String
76
+ })),
77
+ requestedAt: o.Date,
78
+ dispatchAttempts: o.Number.check(o.isInt(), o.isGreaterThanOrEqualTo(0)),
79
+ enteredGeneration: o.NullOr(x)
80
+ }), A = (e, t) => e.kind === t.kind && (e.kind === "failed-steps" || t.kind === "from-step" && e.stepName === t.stepName), j = async (e, t) => {
81
+ let n = o.decodeUnknownSync(l)(t), [r] = await e.query({
82
+ table: b.tableName,
83
+ predicate: f("id", n.requestId),
84
+ lock: "update",
85
+ order: [],
86
+ take: 1,
87
+ skip: void 0,
88
+ projection: void 0
89
+ });
90
+ if (r === void 0) return;
91
+ let i = o.decodeUnknownSync(k)(r);
92
+ if (i.runId !== n.runId) throw Error("Restart identity belongs to a different command");
93
+ return i;
94
+ }, M = async (e, t) => {
95
+ let n = await D(e, t);
96
+ if (n !== void 0) return o.decodeUnknownSync(s)({
97
+ ...t,
98
+ kind: "withdrawn",
99
+ workflowName: n.workflowName,
100
+ selection: n.selection,
101
+ withdrawnAt: n.withdrawnAt.getTime()
102
+ });
103
+ let r = await j(e, t);
104
+ if (r === void 0) return o.decodeUnknownSync(s)({
105
+ ...t,
106
+ kind: "not-found"
107
+ });
108
+ let i = {
109
+ requestId: r.id,
110
+ runId: r.runId,
111
+ workflowName: r.workflowName,
112
+ executionId: r.executionId,
113
+ originalGeneration: r.generation,
114
+ selection: r.selection,
115
+ requestedAt: r.requestedAt.getTime(),
116
+ dispatchAttempts: r.dispatchAttempts
117
+ };
118
+ return o.decodeUnknownSync(s)(r.enteredGeneration === null ? {
119
+ ...i,
120
+ kind: "pending"
121
+ } : {
122
+ ...i,
123
+ kind: "entered",
124
+ enteredGeneration: r.enteredGeneration
125
+ });
126
+ }, N = async (e, t) => {
127
+ for (let e of [
128
+ t.id,
129
+ t.runId,
130
+ t.workflowName
131
+ ]) o.decodeUnknownSync(x)(e);
132
+ if (t.id.length > 64) throw Error("Invalid restart request identity");
133
+ o.decodeUnknownSync(o.NullOr(x))(t.tenantId);
134
+ let n = o.decodeUnknownSync(w)(t.selection);
135
+ await O(e, {
136
+ requestId: t.id,
137
+ runId: t.runId
138
+ });
139
+ let r = await j(e, {
140
+ requestId: t.id,
141
+ runId: t.runId
142
+ });
143
+ if (r !== void 0) {
144
+ if (r.runId !== t.runId || r.workflowName !== t.workflowName || r.tenantId !== t.tenantId || !A(r.selection, n)) throw Error("Restart identity belongs to a different command");
145
+ return r;
146
+ }
147
+ }, P = async (e, t) => {
148
+ let n = o.decodeUnknownSync(E)({
149
+ ...t,
150
+ withdrawnAt: new Date(t.now)
151
+ });
152
+ if (o.decodeUnknownSync(l)({
153
+ requestId: n.id,
154
+ runId: n.runId
155
+ }), !Number.isFinite(n.withdrawnAt.getTime())) throw Error("Invalid restart withdrawal timestamp");
156
+ let r = await D(e, {
157
+ requestId: n.id,
158
+ runId: n.runId
159
+ });
160
+ if (r !== void 0) {
161
+ if (r.workflowName !== n.workflowName || r.tenantId !== n.tenantId || !A(r.selection, n.selection)) throw Error("Restart identity belongs to a different command");
162
+ return {
163
+ kind: "withdrawn",
164
+ withdrawal: r,
165
+ reused: !0
166
+ };
167
+ }
168
+ let i = await N(e, n);
169
+ if (i !== void 0) return {
170
+ kind: "reserved",
171
+ request: i
172
+ };
173
+ let [a] = await e.query({
174
+ table: "_voltro_workflow_runs",
175
+ predicate: f("id", n.runId),
176
+ lock: "update",
177
+ order: [],
178
+ take: 1,
179
+ skip: void 0,
180
+ projection: void 0
181
+ });
182
+ if (a === void 0 || a.tag !== n.workflowName) throw Error("Restart withdrawal requires its resident workflow");
183
+ if ((o.decodeUnknownSync(o.NullOr(o.Struct({ tenantId: o.NullOr(x) })))(a.subject ?? null)?.tenantId ?? null) !== n.tenantId) throw Error("Restart withdrawal belongs to another resident tenant");
184
+ return await e.insert(T.tableName, n), {
185
+ kind: "withdrawn",
186
+ withdrawal: n,
187
+ reused: !1
188
+ };
189
+ }, F = async (e, t) => {
190
+ o.decodeUnknownSync(x)(t.generation);
191
+ let n = await N(e, t);
192
+ if (n !== void 0 && n.generation !== t.generation) throw Error("Restart identity belongs to a different command");
193
+ return n;
194
+ }, I = async (i, a) => {
195
+ for (let e of [
196
+ a.id,
197
+ a.runId,
198
+ a.generation,
199
+ a.token
200
+ ]) o.decodeUnknownSync(x)(e);
201
+ if (a.id.length > 64 || !Number.isFinite(new Date(a.now).getTime())) throw Error("Invalid restart request identity or timestamp");
202
+ await O(i, {
203
+ requestId: a.id,
204
+ runId: a.runId
205
+ });
206
+ let s = o.decodeUnknownSync(S)(a.delivery), c = o.decodeUnknownSync(w)(a.selection);
207
+ if (s.restartToken === a.token) throw Error("Restart requires a new delivery token");
208
+ let l = o.decodeUnknownSync(o.Array(C))(a.replies), d = /* @__PURE__ */ new Map();
209
+ for (let e of l) {
210
+ if (!/^[1-9]\d*$/.test(e.requestId) || !/^[1-9]\d*$/.test(e.replyId) || d.has(e.requestId)) throw Error("Restart requires distinct, exact journal reply identities");
211
+ d.set(e.requestId, e.replyId);
212
+ }
213
+ if (!d.has(s.requestId)) throw Error("Restart selection must include its run reply");
214
+ let [p] = await i.query({
215
+ table: b.tableName,
216
+ predicate: f("id", a.id),
217
+ lock: "update",
218
+ order: [],
219
+ take: 1,
220
+ skip: void 0,
221
+ projection: void 0
222
+ });
223
+ if (p !== void 0) {
224
+ let e = o.decodeUnknownSync(k)(p);
225
+ if (e.runId !== a.runId || e.generation !== a.generation || e.token !== a.token || e.workflowName !== a.control.workflowName || e.tenantId !== a.tenantId || e.delivery.requestId !== s.requestId || e.delivery.restartToken !== s.restartToken || !A(e.selection, c) || e.replies.length !== d.size || e.replies.some((e) => d.get(e.requestId) !== e.replyId)) throw Error("Restart identity belongs to a different request");
226
+ return {
227
+ kind: "reserved",
228
+ request: e,
229
+ reused: !0
230
+ };
231
+ }
232
+ let [m] = await i.query({
233
+ table: "_voltro_workflow_runs",
234
+ predicate: f("id", a.runId),
235
+ lock: "update",
236
+ order: [],
237
+ take: 1,
238
+ skip: void 0,
239
+ projection: void 0
240
+ });
241
+ if (m === void 0 || m.tag !== a.control.workflowName || m.runnerGeneration !== a.generation || m.status !== "failed" || m.discardedAt != null || typeof m.executionId != "string" || m.executionId.length === 0) throw Error("Restart requires its undiscarded failed resident generation");
242
+ if ((o.decodeUnknownSync(o.NullOr(o.Struct({ tenantId: o.NullOr(x) })))(m.subject ?? null)?.tenantId ?? null) !== a.tenantId) throw Error("Restart belongs to another resident tenant");
243
+ let h = o.decodeUnknownSync(S)(m.engineDelivery);
244
+ if (h.requestId !== s.requestId || h.restartToken !== s.restartToken) throw Error("Restart journal delivery does not belong to its resident generation");
245
+ let [_] = await i.query({
246
+ table: b.tableName,
247
+ predicate: u(f("runId", a.runId), g(f("generation", a.generation), f("token", a.token))),
248
+ lock: "update",
249
+ order: [],
250
+ take: 1,
251
+ skip: void 0,
252
+ projection: ["id", "generation"]
253
+ });
254
+ if (_ !== void 0) {
255
+ if (_.generation !== a.generation) throw Error("Restart delivery token was already used by this run");
256
+ return { kind: "already-requested" };
257
+ }
258
+ let v = t(i, a.control.workflowName, m.payload), y = e(a.control, v), T = await n({
259
+ store: i,
260
+ control: a.control,
261
+ keys: y,
262
+ paused: a.paused,
263
+ now: a.now,
264
+ reconsidering: !0
265
+ });
266
+ if (T.kind !== "admit") return {
267
+ kind: "not-admitted",
268
+ decision: T
269
+ };
270
+ let E = await r({
271
+ store: i,
272
+ control: a.control,
273
+ keys: y,
274
+ outcome: "admitted",
275
+ mode: "restart",
276
+ reason: T.reason,
277
+ collapsed: 1,
278
+ waitedMs: 0,
279
+ executionId: null,
280
+ runId: null,
281
+ tenantId: a.tenantId,
282
+ now: a.now
283
+ });
284
+ if (E === void 0) throw Error("Restart has no admission reservation");
285
+ let D = o.decodeUnknownSync(k)({
286
+ id: a.id,
287
+ runId: a.runId,
288
+ workflowName: a.control.workflowName,
289
+ executionId: m.executionId,
290
+ generation: a.generation,
291
+ token: a.token,
292
+ ledgerId: E,
293
+ tenantId: a.tenantId,
294
+ delivery: s,
295
+ replies: l,
296
+ selection: c,
297
+ eviction: T.evict ?? null,
298
+ requestedAt: new Date(a.now),
299
+ dispatchAttempts: 0,
300
+ enteredGeneration: null
301
+ });
302
+ return await i.insert(b.tableName, D), {
303
+ kind: "reserved",
304
+ request: D,
305
+ reused: !1
306
+ };
307
+ }, L = async (e, t) => {
308
+ let [n] = await e.query({
309
+ table: b.tableName,
310
+ predicate: f("id", t),
311
+ lock: "update",
312
+ order: [],
313
+ take: 1,
314
+ skip: void 0,
315
+ projection: void 0
316
+ });
317
+ if (n === void 0) throw Error("Restart dispatch has no resident request");
318
+ let r = o.decodeUnknownSync(k)(n);
319
+ if (r.enteredGeneration !== null) throw Error("Restart request has already entered its resident generation");
320
+ let [i] = await e.query({
321
+ table: "_voltro_workflow_admissions",
322
+ predicate: f("id", r.ledgerId),
323
+ lock: "update",
324
+ order: [],
325
+ take: 1,
326
+ skip: void 0,
327
+ projection: void 0
328
+ });
329
+ if (i === void 0 || i.outcome !== "admitted" || i.releasedAt !== null || i.workflowName !== r.workflowName || i.tenantId !== r.tenantId || i.executionId !== null || i.runId !== null) throw Error("Restart dispatch has no matching held resident reservation");
330
+ let [a] = await e.query({
331
+ table: "_voltro_workflow_runs",
332
+ predicate: f("id", r.runId),
333
+ lock: "update",
334
+ order: [],
335
+ take: 1,
336
+ skip: void 0,
337
+ projection: void 0
338
+ });
339
+ if (a === void 0 || a.tag !== r.workflowName || a.executionId !== r.executionId || a.runnerGeneration !== r.generation || a.status !== "failed" || a.discardedAt != null) throw Error("Restart dispatch no longer owns its failed resident generation");
340
+ let s = o.decodeUnknownSync(S)(a.engineDelivery);
341
+ if (s.requestId !== r.delivery.requestId || s.restartToken !== r.delivery.restartToken) throw Error("Restart dispatch no longer owns its journal delivery");
342
+ return {
343
+ request: r,
344
+ run: a
345
+ };
346
+ }, R = async (e, t) => {
347
+ let { request: n } = await L(e, t), r = n.dispatchAttempts + 1;
348
+ if (!Number.isSafeInteger(r)) throw Error("Restart dispatch attempt counter exhausted");
349
+ if (await e.update(b.tableName, t, { dispatchAttempts: r }) === null) throw Error("Restart request disappeared before dispatch");
350
+ return {
351
+ ...n,
352
+ dispatchAttempts: r
353
+ };
354
+ }, z = (e) => a.gen(function* () {
355
+ let { request: t } = yield* a.promise(() => e.transaction((t) => L(t, e.requestId)));
356
+ if (!(yield* a.promise(() => e.acquireCapacity(t)))) return { kind: "waiting-capacity" };
357
+ let n = yield* a.promise(() => e.transaction((e) => R(e, t.id)));
358
+ return yield* i(n.replies, {
359
+ requestId: n.delivery.requestId,
360
+ previousToken: n.delivery.restartToken,
361
+ token: n.token,
362
+ workflowName: n.workflowName,
363
+ executionId: n.executionId
364
+ });
365
+ }), B = async (e, t) => {
366
+ for (let e of [
367
+ t.runId,
368
+ t.workflowName,
369
+ t.executionId,
370
+ t.generation
371
+ ]) o.decodeUnknownSync(x)(e);
372
+ if (!Number.isFinite(new Date(t.now).getTime())) throw Error("Restart entry requires a valid timestamp");
373
+ let n = o.decodeUnknownSync(S)(t.delivery);
374
+ if (n.restartToken === null) throw Error("Restart entry has no delivery token");
375
+ let [r] = await e.query({
376
+ table: b.tableName,
377
+ predicate: u(f("runId", t.runId), f("token", n.restartToken)),
378
+ lock: "update",
379
+ order: [],
380
+ take: 1,
381
+ skip: void 0,
382
+ projection: ["id"]
383
+ });
384
+ if (r === void 0) throw Error("Restart delivery has no matching resident request");
385
+ let { request: i, run: a } = await L(e, o.decodeUnknownSync(x)(r.id));
386
+ if (i.runId !== t.runId || i.workflowName !== t.workflowName || i.executionId !== t.executionId || i.generation === t.generation || i.delivery.requestId !== n.requestId || i.token !== n.restartToken) throw Error("Restart entry does not match its reserved delivery");
387
+ if (i.dispatchAttempts === 0) throw Error("Restart entry has no committed dispatch attempt");
388
+ if (await e.update("_voltro_workflow_admissions", i.ledgerId, {
389
+ executionId: i.executionId,
390
+ runId: i.runId
391
+ }) === null) throw Error("Restart reservation disappeared during entry");
392
+ if (await e.update(b.tableName, i.id, { enteredGeneration: t.generation }) === null) throw Error("Restart request disappeared during entry");
393
+ let s = await e.update("_voltro_workflow_runs", i.runId, {
394
+ runnerGeneration: t.generation,
395
+ engineDelivery: n,
396
+ runnerEnteredAt: new Date(t.now),
397
+ reclaimCount: 0,
398
+ status: "running",
399
+ errorTag: null,
400
+ errorMessage: null,
401
+ output: null,
402
+ completedAt: null,
403
+ durationMs: null
404
+ });
405
+ if (s === null) throw Error("Restart run disappeared during entry");
406
+ return {
407
+ row: s,
408
+ previous: a,
409
+ event: await e.insert("_voltro_workflow_run_events", {
410
+ id: d("wfev", JSON.stringify(["restart", i.id])),
411
+ runId: i.runId,
412
+ eventType: "run-redriven",
413
+ occurredAt: new Date(t.now),
414
+ stepName: null,
415
+ attempt: null,
416
+ payload: {
417
+ requestId: i.id,
418
+ executionId: i.executionId,
419
+ previousGeneration: i.generation,
420
+ generation: t.generation
421
+ }
422
+ })
423
+ };
424
+ };
425
+ //#endregion
426
+ export { R as beginWorkflowRestartDispatchInTransaction, B as consumeWorkflowRestartInTransaction, N as readWorkflowRestartCommandInTransaction, M as readWorkflowRestartObservationInTransaction, F as readWorkflowRestartReservationInTransaction, I as reserveWorkflowRestartInTransaction, z as resetReservedWorkflowJournal, P as withdrawWorkflowRestartInTransaction, b as workflowRestartRequestsTable, T as workflowRestartWithdrawalsTable };
@@ -0,0 +1,29 @@
1
+ import { beginWorkflowControlDispatchInTransaction as e, bindWorkflowResumeJournalInTransaction as t } from "./controlRequest.js";
2
+ import { observeWorkflowSuspension as n, resetExpectedWorkflowSuspension as r } from "./workflowJournalReset.js";
3
+ import { Context as i, Effect as a } from "effect";
4
+ import { SqlClient as o } from "effect/sql";
5
+ import { Sharding as s } from "effect/cluster";
6
+ //#region src/workflowResumeDispatch.ts
7
+ var c = (i) => a.gen(function* () {
8
+ let o = yield* a.context(), c = yield* a.promise(() => i.transaction((e) => t(e, i.requestId, (e) => a.runPromiseWith(o)(n(e)))));
9
+ if (c.kind !== "bound") return c;
10
+ let l = yield* a.promise(() => i.transaction((t) => e(t, i.requestId)));
11
+ if (l.kind !== "dispatch") return l;
12
+ let { request: u } = l;
13
+ if (u.operation !== "resume" || u.resumeJournal === null) return yield* a.die(/* @__PURE__ */ Error("Resume dispatch requires its committed suspension binding"));
14
+ let d = yield* r({
15
+ workflowName: u.workflowName,
16
+ executionId: u.executionId,
17
+ ...u.resumeJournal
18
+ });
19
+ return yield* (yield* s.Sharding).pollStorage, {
20
+ kind: "attempted",
21
+ request: u,
22
+ journal: d
23
+ };
24
+ }), l = class extends i.Service()("@voltro/workflow/WorkflowResumeDispatcher") {}, u = a.gen(function* () {
25
+ let e = yield* o.SqlClient, t = yield* s.Sharding;
26
+ return l.of({ dispatch: (n) => c(n).pipe(a.provideService(o.SqlClient, e), a.provideService(s.Sharding, t)) });
27
+ });
28
+ //#endregion
29
+ export { l as WorkflowResumeDispatcher, c as dispatchWorkflowResume, u as makeWorkflowResumeDispatcher };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/workflow",
3
- "version": "0.85.0",
3
+ "version": "0.86.0",
4
4
  "description": "Durable workflows for Voltro — the workflow() descriptor + step() / awaitSignal primitives over @effect/cluster, with a browser-safe define subpath.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -48,9 +48,9 @@
48
48
  "node": ">=24.0.0"
49
49
  },
50
50
  "dependencies": {
51
- "@voltro/database": "0.85.0",
52
- "@voltro/logger": "0.85.0",
53
- "@voltro/protocol": "0.85.0"
51
+ "@voltro/database": "0.86.0",
52
+ "@voltro/logger": "0.86.0",
53
+ "@voltro/protocol": "0.86.0"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "effect": "4.0.1",