@voltro/workflow 0.70.0 → 0.71.1

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.
@@ -0,0 +1,379 @@
1
+ import { Context as e, Effect as t, Schema as n } from "effect";
2
+ import { and as r, derivedRowId as i, eq as a, gt as o, or as s } from "@voltro/database";
3
+ //#region src/controlRequest.ts
4
+ var c = "_voltro_workflow_control_requests", l = n.String.pipe(n.minLength(1)), u = n.Struct({
5
+ kind: n.Literal("confirmed", "already-terminal"),
6
+ status: n.Literal("succeeded", "failed", "cancelled"),
7
+ completedAt: n.Number.pipe(n.finite()),
8
+ output: n.Unknown,
9
+ errorTag: n.NullOr(n.String),
10
+ errorMessage: n.NullOr(n.String)
11
+ }), d = n.Struct({
12
+ kind: n.Literal("confirmed"),
13
+ status: n.Literal("running"),
14
+ enteredAt: n.Number.pipe(n.finite()),
15
+ generation: l
16
+ }), f = n.Union(u, d), p = n.Struct({
17
+ reason: n.NonEmptyString,
18
+ detail: n.optional(n.Record({
19
+ key: n.String,
20
+ value: n.Unknown
21
+ }))
22
+ }), m = n.Struct({ tenantId: n.NullOr(n.String) }), h = n.Struct({
23
+ requestId: n.String.pipe(n.pattern(/^[1-9]\d*$/)),
24
+ replyId: n.String.pipe(n.pattern(/^[1-9]\d*$/)),
25
+ restartToken: n.NullOr(n.NonEmptyString)
26
+ }), g = n.Struct({
27
+ id: l,
28
+ runId: l,
29
+ workflowName: l,
30
+ executionId: l,
31
+ generation: l,
32
+ operation: n.Literal("cancel", "terminate", "resume"),
33
+ requestedAt: n.Number.pipe(n.finite()),
34
+ owner: n.NullOr(m),
35
+ cause: n.optional(n.NullOr(p)),
36
+ dispatchAttempts: n.Number.pipe(n.int(), n.nonNegative()),
37
+ resumeJournal: n.NullOr(h),
38
+ result: n.NullOr(f)
39
+ }), _ = n.Struct({
40
+ ...g.fields,
41
+ requestedAt: n.DateFromSelf
42
+ }), v = (e) => {
43
+ let t = n.decodeUnknownSync(_)(e);
44
+ return n.decodeUnknownSync(g)({
45
+ ...t,
46
+ requestedAt: t.requestedAt.getTime()
47
+ });
48
+ }, y = async (e, t, n, r) => {
49
+ if (await e.update("_voltro_workflow_control_requests", t.id, { result: n }) === null) throw Error("Workflow control request disappeared during settlement");
50
+ await e.insert("_voltro_workflow_run_events", {
51
+ id: i("wfev", JSON.stringify(["control-settled", t.id])),
52
+ runId: t.runId,
53
+ eventType: "control-settled",
54
+ occurredAt: new Date(r),
55
+ stepName: null,
56
+ attempt: null,
57
+ payload: {
58
+ requestId: t.id,
59
+ operation: t.operation,
60
+ workflowName: t.workflowName,
61
+ executionId: t.executionId,
62
+ generation: t.generation,
63
+ cause: t.cause ?? null,
64
+ kind: n.kind,
65
+ status: n.status,
66
+ ..."generation" in n ? { enteredGeneration: n.generation } : {}
67
+ }
68
+ });
69
+ }, b = async (e, t) => {
70
+ let n;
71
+ for (;;) {
72
+ let i = await e({
73
+ table: c,
74
+ predicate: r(a("runId", t.runId), a("workflowName", t.workflowName), a("executionId", t.executionId), a("generation", t.generation), s(a("operation", "cancel"), a("operation", "terminate")), ...n === void 0 ? [] : [o("id", n)]),
75
+ order: [{
76
+ column: "id",
77
+ direction: "asc"
78
+ }],
79
+ take: 100,
80
+ skip: void 0,
81
+ projection: void 0
82
+ });
83
+ for (let e of i) if (v(e).result === null) return !0;
84
+ if (i.length < 100) return !1;
85
+ n = v(i[i.length - 1]).id;
86
+ }
87
+ }, x = (e, t) => b((t) => e.query({
88
+ ...t,
89
+ lock: "update"
90
+ }), t), S = async (e, t) => {
91
+ let [n] = await e.query({
92
+ table: c,
93
+ predicate: a("id", t),
94
+ lock: "update",
95
+ order: [],
96
+ take: 1,
97
+ skip: void 0,
98
+ projection: void 0
99
+ });
100
+ if (n !== void 0) return v(n);
101
+ }, C = async (e, t) => {
102
+ let n;
103
+ for (;;) {
104
+ let i = await e.query({
105
+ table: c,
106
+ predicate: r(a("parentClosureId", t), ...n === void 0 ? [] : [o("id", n)]),
107
+ lock: "update",
108
+ order: [{
109
+ column: "id",
110
+ direction: "asc"
111
+ }],
112
+ take: 100,
113
+ skip: void 0,
114
+ projection: void 0
115
+ });
116
+ for (let e of i) {
117
+ let r = v(e);
118
+ if (r.cause?.reason !== "parent-close" || r.cause.detail?.closureId !== t || r.operation === "resume") throw Error("Parent closure control has contradictory ownership");
119
+ if (r.result === null) return !0;
120
+ n = r.id;
121
+ }
122
+ if (i.length < 100) return !1;
123
+ }
124
+ }, w = async (e, t) => {
125
+ let r = n.decodeUnknownSync(g)({
126
+ ...t,
127
+ owner: null,
128
+ cause: t.cause ?? null,
129
+ dispatchAttempts: 0,
130
+ resumeJournal: null,
131
+ result: null
132
+ }), i = await S(e, r.id);
133
+ if (i !== void 0) {
134
+ if (i.runId !== r.runId || i.workflowName !== r.workflowName || i.executionId !== r.executionId || i.generation !== r.generation || i.operation !== r.operation) throw Error("Workflow control identity belongs to a different target or operation");
135
+ return i;
136
+ }
137
+ let o = new Date(r.requestedAt);
138
+ if (!Number.isFinite(o.getTime())) throw Error("Workflow control timestamp is outside the supported date range");
139
+ let [s] = await e.query({
140
+ table: "_voltro_workflow_runs",
141
+ predicate: a("id", r.runId),
142
+ lock: "update",
143
+ order: [],
144
+ take: 1,
145
+ skip: void 0,
146
+ projection: void 0
147
+ });
148
+ if (s !== void 0 && (s.tag !== r.workflowName || s.executionId !== r.executionId)) throw Error("Workflow control target does not match its resident run");
149
+ let l = n.decodeUnknownOption(m)(s?.runnerGeneration === r.generation ? s.subject : void 0), u = {
150
+ ...r,
151
+ owner: l._tag === "Some" ? l.value : null
152
+ }, d = s !== void 0 && s.runnerGeneration === r.generation && (s.status === "succeeded" || s.status === "failed" || s.status === "cancelled") ? {
153
+ ...u,
154
+ result: n.decodeUnknownSync(f)({
155
+ kind: "already-terminal",
156
+ status: s.status,
157
+ completedAt: n.decodeUnknownSync(n.DateFromSelf)(s.completedAt).getTime(),
158
+ output: s.output,
159
+ errorTag: s.errorTag,
160
+ errorMessage: s.errorMessage
161
+ })
162
+ } : u, p = d.cause?.reason === "parent-close" ? n.decodeUnknownSync(n.NonEmptyString)(d.cause.detail?.closureId) : null;
163
+ return await e.insert(c, {
164
+ ...d,
165
+ requestedAt: o,
166
+ parentClosureId: p
167
+ }), d.result !== null && await y(e, d, d.result, r.requestedAt), d;
168
+ }, T = async (e, t, r) => {
169
+ let i = await S(e, t);
170
+ if (i === void 0) throw Error("Workflow control request is missing");
171
+ if (i.operation !== "resume") throw Error("Only resume controls can bind a suspension journal");
172
+ if (i.result !== null) return {
173
+ kind: "settled",
174
+ request: i
175
+ };
176
+ let [o] = await e.query({
177
+ table: "_voltro_workflow_runs",
178
+ predicate: a("id", i.runId),
179
+ lock: "update",
180
+ order: [],
181
+ take: 1,
182
+ skip: void 0,
183
+ projection: void 0
184
+ });
185
+ if (o === void 0) return {
186
+ kind: "unknown",
187
+ request: i
188
+ };
189
+ if (o.tag !== i.workflowName || o.executionId !== i.executionId) throw Error("Workflow control target does not match its resident run");
190
+ if (o.runnerGeneration !== i.generation) return {
191
+ kind: "superseded",
192
+ request: i
193
+ };
194
+ if (o.status !== "suspended" || o.runnerEnteredAt !== null) return {
195
+ kind: "not-suspended",
196
+ request: i
197
+ };
198
+ let s = n.decodeUnknownSync(n.NullOr(n.Struct({
199
+ requestId: h.fields.requestId,
200
+ restartToken: h.fields.restartToken
201
+ })))(o.engineDelivery);
202
+ if (s === null) return {
203
+ kind: "unknown",
204
+ request: i
205
+ };
206
+ if (i.resumeJournal !== null) {
207
+ if (i.resumeJournal.requestId !== s.requestId || i.resumeJournal.restartToken !== s.restartToken) throw Error("Resume journal binding does not match its resident delivery");
208
+ return {
209
+ kind: "bound",
210
+ request: i
211
+ };
212
+ }
213
+ if (i.dispatchAttempts !== 0) return {
214
+ kind: "unknown",
215
+ request: i
216
+ };
217
+ let c = await r({
218
+ workflowName: i.workflowName,
219
+ executionId: i.executionId,
220
+ ...s
221
+ });
222
+ if (c === void 0) return {
223
+ kind: "waiting-journal",
224
+ request: i
225
+ };
226
+ let l = n.decodeUnknownSync(h)(c);
227
+ if (l.requestId !== s.requestId || l.restartToken !== s.restartToken) throw Error("Observed suspension belongs to another engine delivery");
228
+ if (await e.update("_voltro_workflow_control_requests", i.id, { resumeJournal: l }) === null) throw Error("Workflow control request disappeared during suspension binding");
229
+ return {
230
+ kind: "bound",
231
+ request: {
232
+ ...i,
233
+ resumeJournal: l
234
+ }
235
+ };
236
+ }, E = async (e, t) => {
237
+ let n = await S(e, t);
238
+ if (n === void 0) throw Error("Workflow control request is missing");
239
+ if (n.result !== null) return {
240
+ kind: "settled",
241
+ request: n
242
+ };
243
+ let [r] = await e.query({
244
+ table: "_voltro_workflow_runs",
245
+ predicate: a("id", n.runId),
246
+ lock: "update",
247
+ order: [],
248
+ take: 1,
249
+ skip: void 0,
250
+ projection: void 0
251
+ });
252
+ if (r === void 0) return {
253
+ kind: "unknown",
254
+ request: n
255
+ };
256
+ if (r.tag !== n.workflowName || r.executionId !== n.executionId) throw Error("Workflow control target does not match its resident run");
257
+ if (r.runnerGeneration !== n.generation) return {
258
+ kind: "superseded",
259
+ request: n
260
+ };
261
+ if (r.status === "succeeded" || r.status === "failed" || r.status === "cancelled") return {
262
+ kind: "already-terminal",
263
+ request: n,
264
+ run: r
265
+ };
266
+ if (r.status !== "running" && r.status !== "suspended") throw Error("Workflow control target has an invalid resident status");
267
+ let i = n.dispatchAttempts + 1;
268
+ if (!Number.isSafeInteger(i)) throw Error("Workflow control attempt counter exhausted");
269
+ if (await e.update("_voltro_workflow_control_requests", n.id, { dispatchAttempts: i }) === null) throw Error("Workflow control request disappeared before dispatch");
270
+ return {
271
+ kind: "dispatch",
272
+ request: {
273
+ ...n,
274
+ dispatchAttempts: i
275
+ }
276
+ };
277
+ }, D = async (e, t) => {
278
+ let i = t.owner, s;
279
+ for (;;) {
280
+ let l = await e.query({
281
+ table: c,
282
+ predicate: r(a("runId", i.runId), a("workflowName", i.workflowName), a("executionId", i.executionId), a("generation", i.generation), ...s === void 0 ? [] : [o("id", s)]),
283
+ lock: "update",
284
+ order: [{
285
+ column: "id",
286
+ direction: "asc"
287
+ }],
288
+ take: 100,
289
+ skip: void 0,
290
+ projection: void 0
291
+ });
292
+ for (let r of l) {
293
+ if (typeof r.id != "string") throw Error("Workflow control request has an invalid identity");
294
+ let i = await S(e, r.id);
295
+ if (i === void 0) throw Error("Workflow control request disappeared during completion");
296
+ if (i.result !== null) continue;
297
+ let a = t.outcome;
298
+ await y(e, i, n.decodeUnknownSync(f)({
299
+ kind: a.status === "cancelled" && i.operation !== "resume" ? "confirmed" : "already-terminal",
300
+ status: a.status,
301
+ completedAt: t.now,
302
+ output: a.status === "succeeded" ? a.output : null,
303
+ errorTag: a.status === "failed" ? a.errorTag : null,
304
+ errorMessage: a.status === "failed" ? a.errorMessage : null
305
+ }), t.now);
306
+ }
307
+ if (l.length < 100) return;
308
+ let u = l[l.length - 1];
309
+ if (typeof u.id != "string") throw Error("Workflow control cursor has an invalid identity");
310
+ s = u.id;
311
+ }
312
+ }, O = async (e, t, i, s) => {
313
+ let l = n.decodeUnknownSync(d)({
314
+ kind: "confirmed",
315
+ status: "running",
316
+ generation: i,
317
+ enteredAt: s
318
+ }), u;
319
+ for (;;) {
320
+ let n = await e.query({
321
+ table: c,
322
+ predicate: r(a("runId", t.runId), a("workflowName", t.workflowName), a("executionId", t.executionId), a("generation", t.generation), a("operation", "resume"), ...u === void 0 ? [] : [o("id", u)]),
323
+ lock: "update",
324
+ order: [{
325
+ column: "id",
326
+ direction: "asc"
327
+ }],
328
+ take: 100,
329
+ skip: void 0,
330
+ projection: void 0
331
+ });
332
+ for (let t of n) {
333
+ if (typeof t.id != "string") throw Error("Workflow control request has an invalid identity");
334
+ let n = await S(e, t.id);
335
+ if (n === void 0) throw Error("Workflow control request disappeared during resume");
336
+ n.result === null && await y(e, n, l, s);
337
+ }
338
+ if (n.length < 100) return;
339
+ let i = n[n.length - 1];
340
+ if (typeof i.id != "string") throw Error("Workflow control cursor has an invalid identity");
341
+ u = i.id;
342
+ }
343
+ }, k = "voltro-workflow-restart", A = class extends e.Tag("voltro/CurrentWorkflowDelivery")() {}, j = (e) => ({
344
+ ...e,
345
+ registerEntity: (n, r, i) => e.registerEntity(n, n.type.startsWith("Workflow/") ? r.pipe(t.map((e) => {
346
+ let n = e.run;
347
+ if (typeof n != "function") throw Error("Workflow entity is missing its run handler");
348
+ return {
349
+ ...e,
350
+ run: (e) => {
351
+ let r = {
352
+ requestId: String(e.requestId),
353
+ restartToken: e.headers["voltro-workflow-restart"] ?? null
354
+ };
355
+ return t.suspend(() => {
356
+ let r = n(e);
357
+ return t.isEffect(r) ? r : t.die(/* @__PURE__ */ Error("Workflow run handler must return an Effect"));
358
+ }).pipe(t.provideService(A, r));
359
+ }
360
+ };
361
+ })) : r, i)
362
+ }), M = 6e5, N = class extends e.Tag("@voltro/workflow/ClusterDiagnostics")() {}, P = (e, n, r = Date.now()) => t.flatMap(e.messagesFor(n), (n) => {
363
+ if (n.length === 0) return t.succeed(void 0);
364
+ let i = [...new Set(n.filter((e) => !e.processed).map((e) => e.shardId))], a = i.length === 0 ? t.succeed([]) : e.shardLockHolders(i);
365
+ return t.map(a, (t) => ({
366
+ runner: e.ownAddress,
367
+ shardLockMode: e.shardLockMode,
368
+ messages: n.map((e) => ({
369
+ tag: e.tag,
370
+ shardId: e.shardId,
371
+ processed: e.processed,
372
+ lastRead: e.lastRead === null ? null : e.lastRead.toISOString(),
373
+ unreadForMs: !e.processed && e.lastRead !== null ? Math.max(0, r - e.lastRead.getTime()) : null
374
+ })),
375
+ shardLocks: t
376
+ }));
377
+ });
378
+ //#endregion
379
+ export { k as a, E as c, C as d, b as f, O as h, A as i, T as l, D as m, N as n, j as o, S as p, P as r, w as s, M as t, x as u };
@@ -1,6 +1,6 @@
1
- import { I as e, P as t, S as n, g as r, v as i } from "./primitives-CqHxfASq.js";
2
- import { o as a } from "./src-TRpoU54e.js";
3
- import { i as o, p as s } from "./cluster-CSycI4sz.js";
1
+ import { F as e, L as t, S as n, g as r, v as i } from "./primitives-DsMsMr5S.js";
2
+ import { r as a } from "./src-OEBqLBJl.js";
3
+ import { i as o, p as s } from "./cluster-BRSoL1tb.js";
4
4
  import { Cron as c, Deferred as l, Effect as u, Fiber as d, Layer as f, Schema as p } from "effect";
5
5
  import { SqlClient as m } from "@effect/sql";
6
6
  import { beforeAll as h, describe as g, expect as _, test as v } from "vitest";
@@ -188,7 +188,7 @@ var y = async (e, t, n = 750) => {
188
188
  l.push(a);
189
189
  })
190
190
  }), "done";
191
- }).pipe(u.locally(t, c), u.provideService(e, g))), S = m.runnerPort + 2, T = (e) => {
191
+ }).pipe(u.locally(e, c), u.provideService(t, g))), S = m.runnerPort + 2, T = (e) => {
192
192
  let t = v();
193
193
  return {
194
194
  wf: t,