@voltro/workflow 0.85.0 → 0.87.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.
- package/CHANGELOG.md +5987 -0
- package/THIRD-PARTY-NOTICES.md +1122 -31
- package/dist/admission.js +140 -0
- package/dist/admissionDrainer.js +705 -0
- package/dist/admissionGate.js +115 -0
- package/dist/admissionStore.js +347 -0
- package/dist/admissionSubmission.js +297 -0
- package/dist/admissionTables.js +137 -0
- package/dist/admissionTransaction.js +21 -0
- package/dist/awaitEvent.js +82 -0
- package/dist/budgetHold.js +136 -0
- package/dist/cancelOnPlan.js +75 -0
- package/dist/cancelOnSweep.js +188 -0
- package/dist/cancelPendingStart.js +50 -0
- package/dist/cluster.d.ts +111 -27
- package/dist/cluster.js +12 -2
- package/dist/clusterCron.js +19 -0
- package/dist/clusterDiagnostics.js +20 -0
- package/dist/clusterDiagnosticsSql.js +145 -0
- package/dist/clusterLayer.js +145 -0
- package/dist/clusterMigrationGate.js +61 -0
- package/dist/clusterRunnerLayer.js +31 -0
- package/dist/clusterShardGroups.js +33 -0
- package/dist/clusterTestSuite.d.ts +23 -0
- package/dist/clusterTestSuite.js +209 -179
- package/dist/controlMonitor.js +13 -0
- package/dist/controlRequest.js +367 -0
- package/dist/executionRefusal.js +162 -0
- package/dist/failureHandoff.js +183 -0
- package/dist/failureMessage.js +12 -0
- package/dist/failureRecovery.js +50 -0
- package/dist/failureReportStore.js +60 -0
- package/dist/flowControl.js +142 -0
- package/dist/forwardStartReceipts.js +41 -0
- package/dist/inMemoryLayer.js +7 -0
- package/dist/inMemoryRecorder.js +56 -0
- package/dist/index.d.ts +129 -30
- package/dist/index.js +50 -5
- package/dist/inspectWorkflow.js +101 -0
- package/dist/{journalEncryption-DTyhUzaS.js → journalEncryption.js} +30 -37
- package/dist/journalRoutingGuard.js +50 -0
- package/dist/journalRoutingLayout.js +47 -0
- package/dist/journalRoutingLock.js +39 -0
- package/dist/journalRoutingMaintenance.js +24 -0
- package/dist/journalRoutingMessageStorage.js +32 -0
- package/dist/journalRoutingNamespace.js +23 -0
- package/dist/journalRoutingRuntime.js +33 -0
- package/dist/journalRoutingSharding.js +28 -0
- package/dist/journalRoutingState.js +93 -0
- package/dist/journalRoutingTable.js +11 -0
- package/dist/journalRoutingTransaction.js +16 -0
- package/dist/messages.js +92 -0
- package/dist/nondeterminism.js +51 -0
- package/dist/parentClosure.js +141 -0
- package/dist/parentClosureRecovery.js +54 -0
- package/dist/pendingSubmission.js +85 -0
- package/dist/prepareSubmissionDispatch.js +17 -0
- package/dist/preparedExecution.js +14 -0
- package/dist/primitives.js +225 -2
- package/dist/queuedCancellationRequest.js +364 -0
- package/dist/queuedControl.js +21 -0
- package/dist/recorder.js +11 -0
- package/dist/residentExecutionEvidence.js +64 -0
- package/dist/retryPolicy.js +57 -0
- package/dist/runEventsTable.js +17 -0
- package/dist/runGeneration.js +345 -0
- package/dist/runRecording.js +615 -0
- package/dist/runStepsTable.js +28 -0
- package/dist/runnerStorageGuard.js +29 -0
- package/dist/runsTable.js +42 -0
- package/dist/scheduleDeclaration.js +44 -0
- package/dist/signalHint.js +23 -0
- package/dist/signals.js +70 -0
- package/dist/signalsSuspending.js +57 -0
- package/dist/sqliteWorkflowOwnership.js +26 -0
- package/dist/stalenessSweep.js +191 -0
- package/dist/startContextsTable.js +20 -0
- package/dist/startReceipt.js +143 -0
- package/dist/submissionRecovery.js +122 -0
- package/dist/submissionRejection.js +10 -0
- package/dist/submissionRetry.js +71 -0
- package/dist/suspensionUnavailable.js +9 -0
- package/dist/updates.js +87 -0
- package/dist/watermarkTable.js +9 -0
- package/dist/webCrypto.js +17 -0
- package/dist/workflowDelivery.js +24 -0
- package/dist/workflowExecutorRouting.js +33 -0
- package/dist/workflowJournal.js +124 -0
- package/dist/workflowJournalReset.js +111 -0
- package/dist/workflowReadSelections.js +12 -0
- package/dist/workflowRestartDispatch.js +35 -0
- package/dist/workflowRestartObservation.js +60 -0
- package/dist/workflowRestartPreparation.js +109 -0
- package/dist/workflowRestartReservation.js +427 -0
- package/dist/workflowResumeDispatch.js +30 -0
- package/dist/workflowRuntimeReadiness.js +12 -0
- package/dist/workflowShardRelocation.js +58 -0
- package/dist/workflowShardRelocationSql.js +72 -0
- package/package.json +4 -4
- package/dist/cluster-F_bhqVP7.js +0 -679
- package/dist/primitives-DX04WTWh.js +0 -507
- package/dist/src-C2NEyUM8.js +0 -3925
- package/dist/workflowReadSelections-CZAUjP7f.js +0 -2096
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import { isWorkflowEncrypted as e, sealErrorText as t, sealJournalValue as n } from "./journalEncryption.js";
|
|
2
|
+
import { Schema as r } from "effect";
|
|
3
|
+
import { and as i, derivedRowId as a, eq as o, gt as s, or as c } from "@voltro/database";
|
|
4
|
+
//#region src/controlRequest.ts
|
|
5
|
+
var l = "_voltro_workflow_control_requests", u = r.String.pipe(r.check(r.isMinLength(1))), d = r.Struct({
|
|
6
|
+
kind: r.Literals(["confirmed", "already-terminal"]),
|
|
7
|
+
status: r.Literals([
|
|
8
|
+
"succeeded",
|
|
9
|
+
"failed",
|
|
10
|
+
"cancelled"
|
|
11
|
+
]),
|
|
12
|
+
completedAt: r.Number.pipe(r.check(r.isFinite())),
|
|
13
|
+
output: r.Unknown,
|
|
14
|
+
errorTag: r.NullOr(r.String),
|
|
15
|
+
errorMessage: r.NullOr(r.String)
|
|
16
|
+
}), f = r.Struct({
|
|
17
|
+
kind: r.Literal("confirmed"),
|
|
18
|
+
status: r.Literal("running"),
|
|
19
|
+
enteredAt: r.Number.pipe(r.check(r.isFinite())),
|
|
20
|
+
generation: u
|
|
21
|
+
}), p = r.Union([d, f]), m = r.Struct({
|
|
22
|
+
reason: r.NonEmptyString,
|
|
23
|
+
detail: r.optional(r.Record(r.String, r.Unknown))
|
|
24
|
+
}), h = r.Struct({ tenantId: r.NullOr(r.String) }), g = r.Struct({
|
|
25
|
+
requestId: r.String.pipe(r.check(r.isPattern(/^[1-9]\d*$/))),
|
|
26
|
+
replyId: r.String.pipe(r.check(r.isPattern(/^[1-9]\d*$/))),
|
|
27
|
+
restartToken: r.NullOr(r.NonEmptyString)
|
|
28
|
+
}), _ = r.Struct({
|
|
29
|
+
id: u,
|
|
30
|
+
runId: u,
|
|
31
|
+
workflowName: u,
|
|
32
|
+
executionId: u,
|
|
33
|
+
generation: u,
|
|
34
|
+
operation: r.Literals([
|
|
35
|
+
"cancel",
|
|
36
|
+
"terminate",
|
|
37
|
+
"resume"
|
|
38
|
+
]),
|
|
39
|
+
requestedAt: r.Number.pipe(r.check(r.isFinite())),
|
|
40
|
+
owner: r.NullOr(h),
|
|
41
|
+
cause: r.optional(r.NullOr(m)),
|
|
42
|
+
dispatchAttempts: r.Number.pipe(r.check(r.isInt()), r.check(r.isGreaterThanOrEqualTo(0))),
|
|
43
|
+
resumeJournal: r.NullOr(g),
|
|
44
|
+
result: r.NullOr(p)
|
|
45
|
+
}), v = r.Struct({
|
|
46
|
+
..._.fields,
|
|
47
|
+
requestedAt: r.Date
|
|
48
|
+
}), y = (e) => {
|
|
49
|
+
let t = r.decodeUnknownSync(v)(e);
|
|
50
|
+
return r.decodeUnknownSync(_)({
|
|
51
|
+
...t,
|
|
52
|
+
requestedAt: t.requestedAt.getTime()
|
|
53
|
+
});
|
|
54
|
+
}, b = async (e, t, n, r) => {
|
|
55
|
+
if (await e.update("_voltro_workflow_control_requests", t.id, { result: n }) === null) throw Error("Workflow control request disappeared during settlement");
|
|
56
|
+
await e.insert("_voltro_workflow_run_events", {
|
|
57
|
+
id: a("wfev", JSON.stringify(["control-settled", t.id])),
|
|
58
|
+
runId: t.runId,
|
|
59
|
+
eventType: "control-settled",
|
|
60
|
+
occurredAt: new Date(r),
|
|
61
|
+
stepName: null,
|
|
62
|
+
attempt: null,
|
|
63
|
+
payload: {
|
|
64
|
+
requestId: t.id,
|
|
65
|
+
operation: t.operation,
|
|
66
|
+
workflowName: t.workflowName,
|
|
67
|
+
executionId: t.executionId,
|
|
68
|
+
generation: t.generation,
|
|
69
|
+
cause: t.cause ?? null,
|
|
70
|
+
kind: n.kind,
|
|
71
|
+
status: n.status,
|
|
72
|
+
..."generation" in n ? { enteredGeneration: n.generation } : {}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}, x = async (e, t) => {
|
|
76
|
+
let n = [], r;
|
|
77
|
+
for (;;) {
|
|
78
|
+
let a = await e({
|
|
79
|
+
table: l,
|
|
80
|
+
predicate: i(o("runId", t.runId), o("workflowName", t.workflowName), o("executionId", t.executionId), o("generation", t.generation), c(o("operation", "cancel"), o("operation", "terminate")), ...r === void 0 ? [] : [s("id", r)]),
|
|
81
|
+
order: [{
|
|
82
|
+
column: "id",
|
|
83
|
+
direction: "asc"
|
|
84
|
+
}],
|
|
85
|
+
take: 100,
|
|
86
|
+
skip: void 0,
|
|
87
|
+
projection: void 0
|
|
88
|
+
});
|
|
89
|
+
for (let e of a) {
|
|
90
|
+
let t = y(e);
|
|
91
|
+
t.result === null && n.push(t);
|
|
92
|
+
}
|
|
93
|
+
if (a.length < 100) return n;
|
|
94
|
+
r = y(a[a.length - 1]).id;
|
|
95
|
+
}
|
|
96
|
+
}, S = async (e, t) => (await x(e, t)).length > 0, C = (e, t) => S((t) => e.query({
|
|
97
|
+
...t,
|
|
98
|
+
lock: "update"
|
|
99
|
+
}), t), w = {
|
|
100
|
+
source: "flow-control",
|
|
101
|
+
timeout: "finish"
|
|
102
|
+
}, T = r.Struct({
|
|
103
|
+
reason: r.NonEmptyString,
|
|
104
|
+
detail: r.Struct({
|
|
105
|
+
source: r.Literal("flow-control"),
|
|
106
|
+
timeout: r.Literal("finish"),
|
|
107
|
+
onFailure: r.NullOr(r.NonEmptyString)
|
|
108
|
+
})
|
|
109
|
+
}), E = (e) => {
|
|
110
|
+
let t = r.decodeUnknownOption(T)(e.cause);
|
|
111
|
+
return t._tag === "Some" ? t.value : void 0;
|
|
112
|
+
}, D = async (e, t) => {
|
|
113
|
+
let [n] = await e.query({
|
|
114
|
+
table: l,
|
|
115
|
+
predicate: o("id", t),
|
|
116
|
+
lock: "update",
|
|
117
|
+
order: [],
|
|
118
|
+
take: 1,
|
|
119
|
+
skip: void 0,
|
|
120
|
+
projection: void 0
|
|
121
|
+
});
|
|
122
|
+
if (n !== void 0) return y(n);
|
|
123
|
+
}, O = async (e, t) => {
|
|
124
|
+
let n;
|
|
125
|
+
for (;;) {
|
|
126
|
+
let r = await e.query({
|
|
127
|
+
table: l,
|
|
128
|
+
predicate: i(o("parentClosureId", t), ...n === void 0 ? [] : [s("id", n)]),
|
|
129
|
+
lock: "update",
|
|
130
|
+
order: [{
|
|
131
|
+
column: "id",
|
|
132
|
+
direction: "asc"
|
|
133
|
+
}],
|
|
134
|
+
take: 100,
|
|
135
|
+
skip: void 0,
|
|
136
|
+
projection: void 0
|
|
137
|
+
});
|
|
138
|
+
for (let e of r) {
|
|
139
|
+
let r = y(e);
|
|
140
|
+
if (r.cause?.reason !== "parent-close" || r.cause.detail?.closureId !== t || r.operation === "resume") throw Error("Parent closure control has contradictory ownership");
|
|
141
|
+
if (r.result === null) return !0;
|
|
142
|
+
n = r.id;
|
|
143
|
+
}
|
|
144
|
+
if (r.length < 100) return !1;
|
|
145
|
+
}
|
|
146
|
+
}, k = async (e, t) => {
|
|
147
|
+
let n = r.decodeUnknownSync(_)({
|
|
148
|
+
...t,
|
|
149
|
+
owner: null,
|
|
150
|
+
cause: t.cause ?? null,
|
|
151
|
+
dispatchAttempts: 0,
|
|
152
|
+
resumeJournal: null,
|
|
153
|
+
result: null
|
|
154
|
+
}), i = await D(e, n.id);
|
|
155
|
+
if (i !== void 0) {
|
|
156
|
+
if (i.runId !== n.runId || i.workflowName !== n.workflowName || i.executionId !== n.executionId || i.generation !== n.generation || i.operation !== n.operation) throw Error("Workflow control identity belongs to a different target or operation");
|
|
157
|
+
return i;
|
|
158
|
+
}
|
|
159
|
+
let a = new Date(n.requestedAt);
|
|
160
|
+
if (!Number.isFinite(a.getTime())) throw Error("Workflow control timestamp is outside the supported date range");
|
|
161
|
+
let [s] = await e.query({
|
|
162
|
+
table: "_voltro_workflow_runs",
|
|
163
|
+
predicate: o("id", n.runId),
|
|
164
|
+
lock: "update",
|
|
165
|
+
order: [],
|
|
166
|
+
take: 1,
|
|
167
|
+
skip: void 0,
|
|
168
|
+
projection: void 0
|
|
169
|
+
});
|
|
170
|
+
if (s !== void 0 && (s.tag !== n.workflowName || s.executionId !== n.executionId)) throw Error("Workflow control target does not match its resident run");
|
|
171
|
+
let c = r.decodeUnknownOption(h)(s?.runnerGeneration === n.generation ? s.subject : void 0), u = {
|
|
172
|
+
...n,
|
|
173
|
+
owner: c._tag === "Some" ? c.value : null
|
|
174
|
+
}, d = s !== void 0 && s.runnerGeneration === n.generation && (s.status === "succeeded" || s.status === "failed" || s.status === "cancelled") ? {
|
|
175
|
+
...u,
|
|
176
|
+
result: r.decodeUnknownSync(p)({
|
|
177
|
+
kind: "already-terminal",
|
|
178
|
+
status: s.status,
|
|
179
|
+
completedAt: r.decodeUnknownSync(r.Date)(s.completedAt).getTime(),
|
|
180
|
+
output: s.output,
|
|
181
|
+
errorTag: s.errorTag,
|
|
182
|
+
errorMessage: s.errorMessage
|
|
183
|
+
})
|
|
184
|
+
} : u, f = d.cause?.reason === "parent-close" ? r.decodeUnknownSync(r.NonEmptyString)(d.cause.detail?.closureId) : null;
|
|
185
|
+
return await e.insert(l, {
|
|
186
|
+
...d,
|
|
187
|
+
requestedAt: a,
|
|
188
|
+
parentClosureId: f
|
|
189
|
+
}), d.result !== null && await b(e, d, d.result, n.requestedAt), d;
|
|
190
|
+
}, A = async (e, t, n) => {
|
|
191
|
+
let i = await D(e, t);
|
|
192
|
+
if (i === void 0) throw Error("Workflow control request is missing");
|
|
193
|
+
if (i.operation !== "resume") throw Error("Only resume controls can bind a suspension journal");
|
|
194
|
+
if (i.result !== null) return {
|
|
195
|
+
kind: "settled",
|
|
196
|
+
request: i
|
|
197
|
+
};
|
|
198
|
+
let [a] = await e.query({
|
|
199
|
+
table: "_voltro_workflow_runs",
|
|
200
|
+
predicate: o("id", i.runId),
|
|
201
|
+
lock: "update",
|
|
202
|
+
order: [],
|
|
203
|
+
take: 1,
|
|
204
|
+
skip: void 0,
|
|
205
|
+
projection: void 0
|
|
206
|
+
});
|
|
207
|
+
if (a === void 0) return {
|
|
208
|
+
kind: "unknown",
|
|
209
|
+
request: i
|
|
210
|
+
};
|
|
211
|
+
if (a.tag !== i.workflowName || a.executionId !== i.executionId) throw Error("Workflow control target does not match its resident run");
|
|
212
|
+
if (a.runnerGeneration !== i.generation) return {
|
|
213
|
+
kind: "superseded",
|
|
214
|
+
request: i
|
|
215
|
+
};
|
|
216
|
+
if (a.status !== "suspended" || a.runnerEnteredAt !== null) return {
|
|
217
|
+
kind: "not-suspended",
|
|
218
|
+
request: i
|
|
219
|
+
};
|
|
220
|
+
let s = r.decodeUnknownSync(r.NullOr(r.Struct({
|
|
221
|
+
requestId: g.fields.requestId,
|
|
222
|
+
restartToken: g.fields.restartToken
|
|
223
|
+
})))(a.engineDelivery);
|
|
224
|
+
if (s === null) return {
|
|
225
|
+
kind: "unknown",
|
|
226
|
+
request: i
|
|
227
|
+
};
|
|
228
|
+
if (i.resumeJournal !== null) {
|
|
229
|
+
if (i.resumeJournal.requestId !== s.requestId || i.resumeJournal.restartToken !== s.restartToken) throw Error("Resume journal binding does not match its resident delivery");
|
|
230
|
+
return {
|
|
231
|
+
kind: "bound",
|
|
232
|
+
request: i
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (i.dispatchAttempts !== 0) return {
|
|
236
|
+
kind: "unknown",
|
|
237
|
+
request: i
|
|
238
|
+
};
|
|
239
|
+
let c = await n({
|
|
240
|
+
workflowName: i.workflowName,
|
|
241
|
+
executionId: i.executionId,
|
|
242
|
+
...s
|
|
243
|
+
});
|
|
244
|
+
if (c === void 0) return {
|
|
245
|
+
kind: "waiting-journal",
|
|
246
|
+
request: i
|
|
247
|
+
};
|
|
248
|
+
let l = r.decodeUnknownSync(g)(c);
|
|
249
|
+
if (l.requestId !== s.requestId || l.restartToken !== s.restartToken) throw Error("Observed suspension belongs to another engine delivery");
|
|
250
|
+
if (await e.update("_voltro_workflow_control_requests", i.id, { resumeJournal: l }) === null) throw Error("Workflow control request disappeared during suspension binding");
|
|
251
|
+
return {
|
|
252
|
+
kind: "bound",
|
|
253
|
+
request: {
|
|
254
|
+
...i,
|
|
255
|
+
resumeJournal: l
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}, j = async (e, t) => {
|
|
259
|
+
let n = await D(e, t);
|
|
260
|
+
if (n === void 0) throw Error("Workflow control request is missing");
|
|
261
|
+
if (n.result !== null) return {
|
|
262
|
+
kind: "settled",
|
|
263
|
+
request: n
|
|
264
|
+
};
|
|
265
|
+
let [r] = await e.query({
|
|
266
|
+
table: "_voltro_workflow_runs",
|
|
267
|
+
predicate: o("id", n.runId),
|
|
268
|
+
lock: "update",
|
|
269
|
+
order: [],
|
|
270
|
+
take: 1,
|
|
271
|
+
skip: void 0,
|
|
272
|
+
projection: void 0
|
|
273
|
+
});
|
|
274
|
+
if (r === void 0) return {
|
|
275
|
+
kind: "unknown",
|
|
276
|
+
request: n
|
|
277
|
+
};
|
|
278
|
+
if (r.tag !== n.workflowName || r.executionId !== n.executionId) throw Error("Workflow control target does not match its resident run");
|
|
279
|
+
if (r.runnerGeneration !== n.generation) return {
|
|
280
|
+
kind: "superseded",
|
|
281
|
+
request: n
|
|
282
|
+
};
|
|
283
|
+
if (r.status === "succeeded" || r.status === "failed" || r.status === "cancelled") return {
|
|
284
|
+
kind: "already-terminal",
|
|
285
|
+
request: n,
|
|
286
|
+
run: r
|
|
287
|
+
};
|
|
288
|
+
if (r.status !== "running" && r.status !== "suspended") throw Error("Workflow control target has an invalid resident status");
|
|
289
|
+
let i = n.dispatchAttempts + 1;
|
|
290
|
+
if (!Number.isSafeInteger(i)) throw Error("Workflow control attempt counter exhausted");
|
|
291
|
+
if (await e.update("_voltro_workflow_control_requests", n.id, { dispatchAttempts: i }) === null) throw Error("Workflow control request disappeared before dispatch");
|
|
292
|
+
return {
|
|
293
|
+
kind: "dispatch",
|
|
294
|
+
request: {
|
|
295
|
+
...n,
|
|
296
|
+
dispatchAttempts: i
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
}, M = async (a, c) => {
|
|
300
|
+
let u = c.owner, d;
|
|
301
|
+
for (;;) {
|
|
302
|
+
let f = await a.query({
|
|
303
|
+
table: l,
|
|
304
|
+
predicate: i(o("runId", u.runId), o("workflowName", u.workflowName), o("executionId", u.executionId), o("generation", u.generation), ...d === void 0 ? [] : [s("id", d)]),
|
|
305
|
+
lock: "update",
|
|
306
|
+
order: [{
|
|
307
|
+
column: "id",
|
|
308
|
+
direction: "asc"
|
|
309
|
+
}],
|
|
310
|
+
take: 100,
|
|
311
|
+
skip: void 0,
|
|
312
|
+
projection: void 0
|
|
313
|
+
});
|
|
314
|
+
for (let i of f) {
|
|
315
|
+
if (typeof i.id != "string") throw Error("Workflow control request has an invalid identity");
|
|
316
|
+
let o = await D(a, i.id);
|
|
317
|
+
if (o === void 0) throw Error("Workflow control request disappeared during completion");
|
|
318
|
+
if (o.result !== null) continue;
|
|
319
|
+
let s = c.outcome;
|
|
320
|
+
await b(a, o, r.decodeUnknownSync(p)({
|
|
321
|
+
kind: s.status === "cancelled" && o.operation !== "resume" ? "confirmed" : "already-terminal",
|
|
322
|
+
status: s.status,
|
|
323
|
+
completedAt: c.now,
|
|
324
|
+
output: s.status === "succeeded" ? e(u.workflowName) ? n(s.output, `workflow '${u.workflowName}' result`) : s.output : null,
|
|
325
|
+
errorTag: s.status === "failed" ? s.errorTag : null,
|
|
326
|
+
errorMessage: s.status === "failed" ? t(u.workflowName, s.errorMessage) : null
|
|
327
|
+
}), c.now);
|
|
328
|
+
}
|
|
329
|
+
if (f.length < 100) return;
|
|
330
|
+
let m = f[f.length - 1];
|
|
331
|
+
if (typeof m.id != "string") throw Error("Workflow control cursor has an invalid identity");
|
|
332
|
+
d = m.id;
|
|
333
|
+
}
|
|
334
|
+
}, N = async (e, t, n, a) => {
|
|
335
|
+
let c = r.decodeUnknownSync(f)({
|
|
336
|
+
kind: "confirmed",
|
|
337
|
+
status: "running",
|
|
338
|
+
generation: n,
|
|
339
|
+
enteredAt: a
|
|
340
|
+
}), u;
|
|
341
|
+
for (;;) {
|
|
342
|
+
let n = await e.query({
|
|
343
|
+
table: l,
|
|
344
|
+
predicate: i(o("runId", t.runId), o("workflowName", t.workflowName), o("executionId", t.executionId), o("generation", t.generation), o("operation", "resume"), ...u === void 0 ? [] : [s("id", u)]),
|
|
345
|
+
lock: "update",
|
|
346
|
+
order: [{
|
|
347
|
+
column: "id",
|
|
348
|
+
direction: "asc"
|
|
349
|
+
}],
|
|
350
|
+
take: 100,
|
|
351
|
+
skip: void 0,
|
|
352
|
+
projection: void 0
|
|
353
|
+
});
|
|
354
|
+
for (let t of n) {
|
|
355
|
+
if (typeof t.id != "string") throw Error("Workflow control request has an invalid identity");
|
|
356
|
+
let n = await D(e, t.id);
|
|
357
|
+
if (n === void 0) throw Error("Workflow control request disappeared during resume");
|
|
358
|
+
n.result === null && await b(e, n, c, a);
|
|
359
|
+
}
|
|
360
|
+
if (n.length < 100) return;
|
|
361
|
+
let r = n[n.length - 1];
|
|
362
|
+
if (typeof r.id != "string") throw Error("Workflow control cursor has an invalid identity");
|
|
363
|
+
u = r.id;
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
//#endregion
|
|
367
|
+
export { l as CONTROL_REQUESTS_TABLE, w as FLOW_CONTROL_FINISH_TIMEOUT, k as acceptWorkflowControlInTransaction, j as beginWorkflowControlDispatchInTransaction, A as bindWorkflowResumeJournalInTransaction, E as finishTimeoutCause, C as hasPendingWorkflowCancellation, O as hasUnsettledParentClosureControls, S as readPendingWorkflowCancellation, x as readPendingWorkflowCancellations, D as readWorkflowControlInTransaction, M as settleWorkflowControlsInTransaction, N as settleWorkflowResumesInTransaction };
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { isWorkflowEncrypted as e, loggableCause as t } from "./journalEncryption.js";
|
|
2
|
+
import { CurrentWorkflowDelivery as n } from "./workflowDelivery.js";
|
|
3
|
+
import { completeWorkflowRunGenerationInTransaction as r, enterWorkflowRunGenerationInTransaction as i } from "./runGeneration.js";
|
|
4
|
+
import { serialiseWorkflowRowForWire as a } from "./runRecording.js";
|
|
5
|
+
import { Cause as o, Effect as s, Schema as c } from "effect";
|
|
6
|
+
import { SubjectIdentity as l, publishServerError as u } from "@voltro/protocol";
|
|
7
|
+
import { randomUUID as d } from "node:crypto";
|
|
8
|
+
import { WorkflowInstance as f } from "effect/workflow/WorkflowEngine";
|
|
9
|
+
//#region src/executionRefusal.ts
|
|
10
|
+
var p = (p, m) => s.gen(function* () {
|
|
11
|
+
let h = yield* s.sync(() => c.decodeUnknownSync(l)(p.subject));
|
|
12
|
+
if (h.tenantId !== p.failureContext.tenantId) return yield* s.die(/* @__PURE__ */ Error("Workflow refusal has inconsistent caller tenant"));
|
|
13
|
+
let g = `wfrg_${d()}`, _ = yield* s.serviceOption(n), v = Date.now(), y = o.findErrorOption(m.cause), b = y._tag === "Some" ? y.value : o.squash(m.cause), x = b && typeof b == "object" && "_tag" in b ? String(b._tag) : null, S = b && typeof b == "object" && "message" in b ? String(b.message) : String(b), C = yield* s.promise(() => p.transaction(async (e) => {
|
|
14
|
+
let t = await i(e, {
|
|
15
|
+
workflowName: p.name,
|
|
16
|
+
executionId: m.executionId,
|
|
17
|
+
generation: g,
|
|
18
|
+
now: v,
|
|
19
|
+
delivery: _._tag === "Some" ? _.value : null,
|
|
20
|
+
initialRow: {
|
|
21
|
+
id: p.makeId?.("wr") ?? `wr_${d()}`,
|
|
22
|
+
payload: p.encodePayload === void 0 ? m.payload : p.encodePayload(m.payload),
|
|
23
|
+
status: "running",
|
|
24
|
+
subject: p.subject,
|
|
25
|
+
source: p.source ?? null,
|
|
26
|
+
traceId: p.traceId ?? null,
|
|
27
|
+
workflowVersion: p.workflowVersion ?? null,
|
|
28
|
+
workflowPatches: p.workflowPatches ?? null,
|
|
29
|
+
output: null,
|
|
30
|
+
errorTag: null,
|
|
31
|
+
errorMessage: null,
|
|
32
|
+
cancelled: !1,
|
|
33
|
+
startedAt: new Date(v),
|
|
34
|
+
completedAt: null,
|
|
35
|
+
durationMs: null,
|
|
36
|
+
parentExecutionId: p.parentExecutionId ?? null,
|
|
37
|
+
parentGeneration: p.parentGeneration ?? null,
|
|
38
|
+
parentClosePolicy: p.parentClosePolicy ?? null
|
|
39
|
+
}
|
|
40
|
+
}), n = c.decodeUnknownSync(l)(t.row.subject);
|
|
41
|
+
if (n.type !== h.type || n.id !== h.id || n.tenantId !== h.tenantId || h.type === "service" && (n.type !== "service" || n.service !== h.service)) throw Error("Workflow refusal belongs to a different recorded caller");
|
|
42
|
+
if (t.kind === "cancelled" || t.kind === "already-cancelled") return t;
|
|
43
|
+
if (typeof t.row.id != "string") throw Error("Workflow refusal has no allocated run identity");
|
|
44
|
+
let a = await r(e, {
|
|
45
|
+
owner: {
|
|
46
|
+
runId: t.row.id,
|
|
47
|
+
workflowName: p.name,
|
|
48
|
+
executionId: m.executionId,
|
|
49
|
+
generation: g
|
|
50
|
+
},
|
|
51
|
+
now: v,
|
|
52
|
+
durationMs: 0,
|
|
53
|
+
outcome: {
|
|
54
|
+
status: "failed",
|
|
55
|
+
errorTag: x,
|
|
56
|
+
errorMessage: S,
|
|
57
|
+
...p.failureContext
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
if (a.kind !== "committed") throw Error("Workflow refusal lost its resident generation");
|
|
61
|
+
return {
|
|
62
|
+
...a,
|
|
63
|
+
created: t.kind === "created",
|
|
64
|
+
restartEvent: t.restartEvent
|
|
65
|
+
};
|
|
66
|
+
}));
|
|
67
|
+
if (C.kind === "already-cancelled") {
|
|
68
|
+
let e = yield* f;
|
|
69
|
+
return e.interrupted = !0, yield* s.interrupt;
|
|
70
|
+
}
|
|
71
|
+
let w = (e) => {
|
|
72
|
+
try {
|
|
73
|
+
p.log?.warn(e, {
|
|
74
|
+
workflow: p.name,
|
|
75
|
+
executionId: m.executionId
|
|
76
|
+
});
|
|
77
|
+
} catch {}
|
|
78
|
+
};
|
|
79
|
+
yield* s.sync(() => {
|
|
80
|
+
try {
|
|
81
|
+
p.emit?.("workflowRuns", {
|
|
82
|
+
op: C.kind === "committed" && C.created ? "insert" : "update",
|
|
83
|
+
row: a(C.row)
|
|
84
|
+
}), C.restartEvent !== void 0 && p.emit?.("workflowEvents", {
|
|
85
|
+
op: "insert",
|
|
86
|
+
row: a(C.restartEvent)
|
|
87
|
+
}), C.terminalEvent !== void 0 && p.emit?.("workflowEvents", {
|
|
88
|
+
op: "insert",
|
|
89
|
+
row: a(C.terminalEvent)
|
|
90
|
+
});
|
|
91
|
+
} catch {
|
|
92
|
+
w("workflow refusal observer failed after terminal commit");
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
let T = C.kind === "cancelled" ? "cancelled" : "failed", E = String(C.row.id);
|
|
96
|
+
if (yield* s.tryPromise({
|
|
97
|
+
try: async () => {
|
|
98
|
+
await p.wakeups?.cancel(E, h.tenantId ?? void 0);
|
|
99
|
+
},
|
|
100
|
+
catch: (e) => e
|
|
101
|
+
}).pipe(s.catch(() => s.sync(() => w("workflow refusal wakeup observer failed after terminal commit")))), yield* s.tryPromise({
|
|
102
|
+
try: async () => {
|
|
103
|
+
await p.onParentClose?.({
|
|
104
|
+
workflowName: p.name,
|
|
105
|
+
executionId: m.executionId,
|
|
106
|
+
runId: E,
|
|
107
|
+
status: T
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
catch: (e) => e
|
|
111
|
+
}).pipe(s.catch(() => s.sync(() => w("workflow refusal parent observer failed after terminal commit")))), yield* s.tryPromise({
|
|
112
|
+
try: async () => {
|
|
113
|
+
await p.onTerminal?.({
|
|
114
|
+
workflowName: p.name,
|
|
115
|
+
executionId: m.executionId,
|
|
116
|
+
runId: E,
|
|
117
|
+
status: T,
|
|
118
|
+
...T === "failed" ? {
|
|
119
|
+
payload: m.payload,
|
|
120
|
+
errorTag: x,
|
|
121
|
+
errorMessage: S
|
|
122
|
+
} : {}
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
catch: (e) => e
|
|
126
|
+
}).pipe(s.catch(() => s.sync(() => w("workflow refusal terminal observer failed after commit")))), yield* s.sync(() => {
|
|
127
|
+
try {
|
|
128
|
+
p.recordRun?.({
|
|
129
|
+
name: p.name,
|
|
130
|
+
status: T,
|
|
131
|
+
durationMs: 0
|
|
132
|
+
});
|
|
133
|
+
} catch {
|
|
134
|
+
w("workflow refusal metrics observer failed after terminal commit");
|
|
135
|
+
}
|
|
136
|
+
}), C.kind === "cancelled") {
|
|
137
|
+
let e = yield* f;
|
|
138
|
+
return e.interrupted = !0, yield* s.interrupt;
|
|
139
|
+
}
|
|
140
|
+
return yield* s.sync(() => {
|
|
141
|
+
try {
|
|
142
|
+
p.log?.warn("workflow execution refused before application body", {
|
|
143
|
+
workflow: p.name,
|
|
144
|
+
executionId: m.executionId,
|
|
145
|
+
cause: t(p.name, b)
|
|
146
|
+
});
|
|
147
|
+
} catch {}
|
|
148
|
+
u({
|
|
149
|
+
error: b,
|
|
150
|
+
source: "workflow",
|
|
151
|
+
name: p.name,
|
|
152
|
+
contentSensitive: e(p.name),
|
|
153
|
+
...p.traceId ? { traceId: p.traceId } : {},
|
|
154
|
+
fields: {
|
|
155
|
+
executionId: m.executionId,
|
|
156
|
+
runId: C.row.id
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}), yield* s.failCause(m.cause);
|
|
160
|
+
});
|
|
161
|
+
//#endregion
|
|
162
|
+
export { p as recordWorkflowExecutionRefusal };
|