@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.
Files changed (103) hide show
  1. package/CHANGELOG.md +5987 -0
  2. package/THIRD-PARTY-NOTICES.md +1122 -31
  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.d.ts +111 -27
  16. package/dist/cluster.js +12 -2
  17. package/dist/clusterCron.js +19 -0
  18. package/dist/clusterDiagnostics.js +20 -0
  19. package/dist/clusterDiagnosticsSql.js +145 -0
  20. package/dist/clusterLayer.js +145 -0
  21. package/dist/clusterMigrationGate.js +61 -0
  22. package/dist/clusterRunnerLayer.js +31 -0
  23. package/dist/clusterShardGroups.js +33 -0
  24. package/dist/clusterTestSuite.d.ts +23 -0
  25. package/dist/clusterTestSuite.js +209 -179
  26. package/dist/controlMonitor.js +13 -0
  27. package/dist/controlRequest.js +367 -0
  28. package/dist/executionRefusal.js +162 -0
  29. package/dist/failureHandoff.js +183 -0
  30. package/dist/failureMessage.js +12 -0
  31. package/dist/failureRecovery.js +50 -0
  32. package/dist/failureReportStore.js +60 -0
  33. package/dist/flowControl.js +142 -0
  34. package/dist/forwardStartReceipts.js +41 -0
  35. package/dist/inMemoryLayer.js +7 -0
  36. package/dist/inMemoryRecorder.js +56 -0
  37. package/dist/index.d.ts +129 -30
  38. package/dist/index.js +50 -5
  39. package/dist/inspectWorkflow.js +101 -0
  40. package/dist/{journalEncryption-DTyhUzaS.js → journalEncryption.js} +30 -37
  41. package/dist/journalRoutingGuard.js +50 -0
  42. package/dist/journalRoutingLayout.js +47 -0
  43. package/dist/journalRoutingLock.js +39 -0
  44. package/dist/journalRoutingMaintenance.js +24 -0
  45. package/dist/journalRoutingMessageStorage.js +32 -0
  46. package/dist/journalRoutingNamespace.js +23 -0
  47. package/dist/journalRoutingRuntime.js +33 -0
  48. package/dist/journalRoutingSharding.js +28 -0
  49. package/dist/journalRoutingState.js +93 -0
  50. package/dist/journalRoutingTable.js +11 -0
  51. package/dist/journalRoutingTransaction.js +16 -0
  52. package/dist/messages.js +92 -0
  53. package/dist/nondeterminism.js +51 -0
  54. package/dist/parentClosure.js +141 -0
  55. package/dist/parentClosureRecovery.js +54 -0
  56. package/dist/pendingSubmission.js +85 -0
  57. package/dist/prepareSubmissionDispatch.js +17 -0
  58. package/dist/preparedExecution.js +14 -0
  59. package/dist/primitives.js +225 -2
  60. package/dist/queuedCancellationRequest.js +364 -0
  61. package/dist/queuedControl.js +21 -0
  62. package/dist/recorder.js +11 -0
  63. package/dist/residentExecutionEvidence.js +64 -0
  64. package/dist/retryPolicy.js +57 -0
  65. package/dist/runEventsTable.js +17 -0
  66. package/dist/runGeneration.js +345 -0
  67. package/dist/runRecording.js +615 -0
  68. package/dist/runStepsTable.js +28 -0
  69. package/dist/runnerStorageGuard.js +29 -0
  70. package/dist/runsTable.js +42 -0
  71. package/dist/scheduleDeclaration.js +44 -0
  72. package/dist/signalHint.js +23 -0
  73. package/dist/signals.js +70 -0
  74. package/dist/signalsSuspending.js +57 -0
  75. package/dist/sqliteWorkflowOwnership.js +26 -0
  76. package/dist/stalenessSweep.js +191 -0
  77. package/dist/startContextsTable.js +20 -0
  78. package/dist/startReceipt.js +143 -0
  79. package/dist/submissionRecovery.js +122 -0
  80. package/dist/submissionRejection.js +10 -0
  81. package/dist/submissionRetry.js +71 -0
  82. package/dist/suspensionUnavailable.js +9 -0
  83. package/dist/updates.js +87 -0
  84. package/dist/watermarkTable.js +9 -0
  85. package/dist/webCrypto.js +17 -0
  86. package/dist/workflowDelivery.js +24 -0
  87. package/dist/workflowExecutorRouting.js +33 -0
  88. package/dist/workflowJournal.js +124 -0
  89. package/dist/workflowJournalReset.js +111 -0
  90. package/dist/workflowReadSelections.js +12 -0
  91. package/dist/workflowRestartDispatch.js +35 -0
  92. package/dist/workflowRestartObservation.js +60 -0
  93. package/dist/workflowRestartPreparation.js +109 -0
  94. package/dist/workflowRestartReservation.js +427 -0
  95. package/dist/workflowResumeDispatch.js +30 -0
  96. package/dist/workflowRuntimeReadiness.js +12 -0
  97. package/dist/workflowShardRelocation.js +58 -0
  98. package/dist/workflowShardRelocationSql.js +72 -0
  99. package/package.json +4 -4
  100. package/dist/cluster-F_bhqVP7.js +0 -679
  101. package/dist/primitives-DX04WTWh.js +0 -507
  102. package/dist/src-C2NEyUM8.js +0 -3925
  103. package/dist/workflowReadSelections-CZAUjP7f.js +0 -2096
@@ -1,12 +1,17 @@
1
- import { E as e, O as t, a as n } from "./journalEncryption-DTyhUzaS.js";
2
- import { F as r, R as i, S as a, g as o, v as s } from "./primitives-DX04WTWh.js";
3
- import { C as c, r as l, w as u } from "./src-C2NEyUM8.js";
4
- import { t as d, u as f } from "./cluster-F_bhqVP7.js";
5
- import { Cause as p, Cron as m, Deferred as h, Effect as g, Fiber as _, Layer as v, Option as y, Schema as b } from "effect";
6
- import { SqlClient as x } from "effect/sql";
7
- import { afterEach as S, beforeAll as C, describe as w, expect as T, test as E } from "vitest";
1
+ import { failureMessage as e } from "./failureMessage.js";
2
+ import { encryptionAwareActivity as t, setWorkflowJournalCipher as n } from "./journalEncryption.js";
3
+ import { CurrentWorkflowRunId as r, WorkflowRunRecorder as i } from "./recorder.js";
4
+ import { sleep as a, step as o, workflow as s } from "./primitives.js";
5
+ import { awaitSignalSuspending as c, completeSuspendingSignal as l } from "./signalsSuspending.js";
6
+ import { makeWorkflowRunRecorder as u } from "./runRecording.js";
7
+ import { workflowEngineLayer as d } from "./clusterLayer.js";
8
+ import { makeClusterCronLayer as f } from "./clusterCron.js";
9
+ import { Cause as p, Cron as m, Deferred as h, Effect as g, Fiber as _, Layer as v, ManagedRuntime as y, Option as b, Schema as x } from "effect";
10
+ import { SqlClient as S } from "effect/sql";
11
+ import { EntityId as C, Sharding as w } from "effect/cluster";
12
+ import { afterEach as T, beforeAll as E, describe as D, expect as O, test as k } from "vitest";
8
13
  //#region src/clusterTestSuite.ts
9
- var D = async (e, t, n = 750) => {
14
+ var A = async (e, t, n = 750) => {
10
15
  try {
11
16
  let r = await import("node:net");
12
17
  return await new Promise((i) => {
@@ -22,27 +27,27 @@ var D = async (e, t, n = 750) => {
22
27
  } catch {
23
28
  return !1;
24
29
  }
25
- }, O = [
30
+ }, j = [
26
31
  "cluster_messages",
27
32
  "cluster_replies",
28
33
  "cluster_locks",
29
34
  "cluster_runners"
30
- ], k = async (e) => {
35
+ ], M = async (e) => {
31
36
  try {
32
37
  await g.runPromise(g.gen(function* () {
33
- let e = yield* x.SqlClient;
34
- for (let t of O) yield* e.unsafe(`DELETE FROM ${t}`).pipe(g.ignore);
38
+ let e = yield* S.SqlClient;
39
+ for (let t of j) yield* e.unsafe(`DELETE FROM ${t}`).pipe(g.ignore);
35
40
  }).pipe(g.scoped, g.provide(e.sqlClientLayer)));
36
41
  } catch {}
37
- }, A = "10 seconds", j = "30 seconds", M = (e, t) => g.gen(function* () {
42
+ }, N = "10 seconds", P = "30 seconds", F = (e, t) => g.gen(function* () {
38
43
  let n = yield* g.sync(() => Date.now() + t);
39
44
  for (; !e();) {
40
45
  if (Date.now() > n) return !1;
41
46
  yield* g.sleep("250 millis");
42
47
  }
43
48
  return !0;
44
- }), N = (e) => {
45
- let t = `${e.name}-${Date.now()}`, n = (t, n) => f({
49
+ }), I = (e) => {
50
+ let t = `${e.name}-${Date.now()}`, n = (t, n) => d({
46
51
  sqlClientLayer: e.sqlClientLayer,
47
52
  runnerStorage: e.runnerStorage,
48
53
  runnerListenPort: t,
@@ -50,52 +55,77 @@ var D = async (e, t, n = 750) => {
50
55
  ...e.shardLockMode ? { shardLockMode: e.shardLockMode } : {},
51
56
  ...n && n.length > 0 ? { extraShardingLayers: n } : {}
52
57
  });
53
- w(`cluster workflow engine — ${e.name}`, () => {
58
+ D(`cluster workflow engine — ${e.name}`, () => {
54
59
  let c = !1;
55
- C(async () => {
56
- if (c = await e.reachable(), !c) {
57
- console.warn(`[skip] cluster suite — ${e.name} not reachable`);
60
+ E(async () => {
61
+ c = await e.reachable(), c ? await M(e) : console.warn(`[skip] cluster suite — ${e.name} not reachable`);
62
+ }, 3e4), (e.runnerStorage === "sql" && e.dialectId !== "sqlite" ? k : (() => {}))("client-only submission executes on a distinct worker and never owns its shard", async (r) => {
63
+ if (!c) {
64
+ r.skip();
58
65
  return;
59
66
  }
60
- await k(e);
61
- }, 3e4), E("a two-step workflow runs end-to-end", async () => {
67
+ let i = `roleClient_${t}`, a = s({
68
+ name: i,
69
+ payload: { id: x.String },
70
+ success: x.Number,
71
+ idempotencyKey: ({ id: e }) => `${i}:${e}`
72
+ }), o = 0, l = 0, u = y.make(a.toLayer(() => g.sync(() => (o++, 42))).pipe(v.provideMerge(n(e.runnerPort)))), f = y.make(a.toLayer(() => g.sync(() => (l++, -1))).pipe(v.provideMerge(d({
73
+ sqlClientLayer: e.sqlClientLayer,
74
+ runnerStorage: "sql",
75
+ runnerMode: "client",
76
+ runnerListenPort: e.runnerPort + 19,
77
+ ...e.dialectId ? { dialectId: e.dialectId } : {},
78
+ ...e.shardLockMode ? { shardLockMode: e.shardLockMode } : {}
79
+ }))));
80
+ try {
81
+ await u.runPromise(g.void), await f.runPromise(g.void);
82
+ let e = await f.runPromise(a.execute({ id: "from-client" }).pipe(g.timeout("60 seconds")));
83
+ O(e).toBe(42), O(o).toBe(1), O(l).toBe(0), O(await f.runPromise(g.map(w.Sharding, (e) => e.hasShardId(e.getShardId(C.make("client-probe"), "default"))))).toBe(!1), O(await f.runPromise(a.execute({ id: "from-client" }).pipe(g.timeout("60 seconds")))).toBe(42), O(o).toBe(1), O(l).toBe(0);
84
+ } finally {
85
+ try {
86
+ await f.dispose();
87
+ } finally {
88
+ await u.dispose();
89
+ }
90
+ }
91
+ }, 15e4), k("a two-step workflow runs end-to-end", async () => {
62
92
  if (!c) return;
63
- let r = `clusterTwoStep_${t}`, i = a({
93
+ let r = `clusterTwoStep_${t}`, i = s({
64
94
  name: r,
65
- payload: { id: b.String },
66
- success: b.Number,
95
+ payload: { id: x.String },
96
+ success: x.Number,
67
97
  idempotencyKey: ({ id: e }) => `${r}:${e}`
68
- }), o = i.toLayer(() => g.gen(function* () {
69
- let e = yield* s({
98
+ }), a = i.toLayer(() => g.gen(function* () {
99
+ let e = yield* o({
70
100
  name: "first",
71
- success: b.Number,
101
+ success: x.Number,
72
102
  execute: g.succeed(40)
73
103
  });
74
- return yield* s({
104
+ return yield* o({
75
105
  name: "second",
76
- success: b.Number,
106
+ success: x.Number,
77
107
  execute: g.succeed(e + 2)
78
108
  });
79
- })), l = await g.runPromise(g.scoped(i.execute({ id: `e2e-${t}` }).pipe(g.provide(o.pipe(v.provideMerge(n(e.runnerPort)))))));
80
- T(l).toBe(42);
81
- }, 3e4), E("idempotency-key journal — same payload returns same result", async () => {
109
+ })), l = await g.runPromise(g.scoped(i.execute({ id: `e2e-${t}` }).pipe(g.provide(a.pipe(v.provideMerge(n(e.runnerPort)))))));
110
+ O(l).toBe(42);
111
+ }, 3e4), k("idempotency-key journal — same payload returns same result", async () => {
82
112
  if (!c) return;
83
- let r = `clusterIdempotency_${t}`, i = 0, o = a({
113
+ let r = `clusterIdempotency_${t}`, i = 0, a = s({
84
114
  name: r,
85
- payload: { id: b.String },
86
- success: b.Number,
115
+ payload: { id: x.String },
116
+ success: x.Number,
87
117
  idempotencyKey: ({ id: e }) => `${r}:${e}`
88
- }), l = o.toLayer(() => s({
118
+ }), l = a.toLayer(() => o({
89
119
  name: "compute",
90
- success: b.Number,
120
+ success: x.Number,
91
121
  execute: g.sync(() => (i++, i * 100))
92
122
  })).pipe(v.provideMerge(n(e.runnerPort))), [u, d] = await g.runPromise(g.scoped(g.gen(function* () {
93
- return [yield* o.execute({ id: `same-${t}` }), yield* o.execute({ id: `same-${t}` })];
123
+ return [yield* a.execute({ id: `same-${t}` }), yield* a.execute({ id: `same-${t}` })];
94
124
  }).pipe(g.provide(l))));
95
- T(u).toBe(d), T(i).toBe(1);
96
- }, 3e4), E("ClusterCron fires its execute on the shard owner", async () => {
125
+ O(u).toBe(d), O(i).toBe(1);
126
+ }, 3e4), k("ClusterCron fires its execute on the shard owner", async () => {
97
127
  if (!c) return;
98
- let r = await g.runPromise(h.make()), i = 0, a = d({
128
+ let r = await g.runPromise(h.make()), i = 0, a = f({
99
129
  name: `clusterCron_${t}`,
100
130
  cron: m.parseUnsafe("*/1 * * * * *", "UTC"),
101
131
  execute: async () => {
@@ -104,35 +134,35 @@ var D = async (e, t, n = 750) => {
104
134
  }), o = await g.runPromise(g.scoped(g.gen(function* () {
105
135
  return yield* v.build(n(e.runnerPort + 1, [a])), yield* h.await(r).pipe(g.timeout("25 seconds"), g.orElseSucceed(() => !1));
106
136
  })));
107
- T(o).toBe(!0), T(i).toBeGreaterThanOrEqual(1);
137
+ O(o).toBe(!0), O(i).toBeGreaterThanOrEqual(1);
108
138
  }, 35e3);
109
- let u = e.clusterResume ? E : (() => {});
110
- u("a sleeping workflow resumes on a fresh runner after the first is torn down", async () => {
139
+ let l = e.clusterResume ? k : (() => {});
140
+ l("a sleeping workflow resumes on a fresh runner after the first is torn down", async () => {
111
141
  if (!c) return;
112
- let r = `dormancyResume_${t}`, i = [], l = [], u = [], d = () => a({
142
+ let r = `dormancyResume_${t}`, i = [], l = [], u = [], d = () => s({
113
143
  name: r,
114
- payload: { id: b.String },
115
- success: b.String,
144
+ payload: { id: x.String },
145
+ success: x.String,
116
146
  idempotencyKey: ({ id: e }) => `${r}:${e}`
117
147
  }), f = (e, t) => e.toLayer(() => g.gen(function* () {
118
- return yield* s({
148
+ return yield* o({
119
149
  name: "step1",
120
- success: b.Void,
150
+ success: x.Void,
121
151
  execute: g.sync(() => {
122
152
  i.push(t);
123
153
  })
124
- }), yield* s({
154
+ }), yield* o({
125
155
  name: "armed",
126
- success: b.Void,
156
+ success: x.Void,
127
157
  execute: g.sync(() => {
128
158
  u.push(t);
129
159
  })
130
- }), yield* o({
160
+ }), yield* a({
131
161
  name: "nap",
132
- duration: A
133
- }), yield* s({
162
+ duration: N
163
+ }), yield* o({
134
164
  name: "step2",
135
- success: b.Void,
165
+ success: x.Void,
136
166
  execute: g.sync(() => {
137
167
  l.push(t);
138
168
  })
@@ -145,17 +175,17 @@ var D = async (e, t, n = 750) => {
145
175
  };
146
176
  }, h = { id: `resume-${t}` }, y = await g.runPromise(g.scoped(g.gen(function* () {
147
177
  let { wf: e, layer: t } = m("A"), n = yield* v.build(t), r = yield* g.forkDetach(e.execute(h).pipe(g.provide(n), g.ignore));
148
- return (yield* M(() => u.length > 0, 6e4)) ? r : yield* g.die(/* @__PURE__ */ Error("runner A never journaled step1"));
178
+ return (yield* F(() => u.length > 0, 6e4)) ? r : yield* g.die(/* @__PURE__ */ Error("runner A never journaled step1"));
149
179
  })));
150
- await g.runPromise(_.interrupt(y).pipe(g.ignore)), T(i).toEqual(["A"]), T(l).toEqual([]);
151
- let x = await g.runPromise(g.scoped(g.gen(function* () {
180
+ await g.runPromise(_.interrupt(y).pipe(g.ignore)), O(i).toEqual(["A"]), O(l).toEqual([]);
181
+ let b = await g.runPromise(g.scoped(g.gen(function* () {
152
182
  let { wf: e, layer: t } = m("B"), n = yield* v.build(t);
153
183
  return yield* e.execute(h).pipe(g.provide(n), g.timeout("240 seconds"), g.orElseSucceed(() => "TIMEOUT"));
154
184
  })));
155
- T(x).toBe("done"), T(i).toEqual(["A"]), T(l).toEqual(["B"]);
156
- }, 48e4), u("dormancy loop: a wake signal survives teardown and is cleared on resume", async () => {
185
+ O(b).toBe("done"), O(i).toEqual(["A"]), O(l).toEqual(["B"]);
186
+ }, 48e4), l("dormancy loop: a wake signal survives teardown and is cleared on resume", async () => {
157
187
  if (!c) return;
158
- let u = `dormancyLoop_${t}`, d = `wr_${u}`, f = [], p = /* @__PURE__ */ new Map(), m = l({
188
+ let l = `dormancyLoop_${t}`, d = `wr_${l}`, f = [], p = /* @__PURE__ */ new Map(), m = u({
159
189
  store: {
160
190
  insert: async (e, t) => t,
161
191
  update: async () => null,
@@ -169,156 +199,156 @@ var D = async (e, t, n = 750) => {
169
199
  p.delete(e);
170
200
  }
171
201
  }
172
- }), h = () => a({
173
- name: u,
174
- payload: { id: b.String },
175
- success: b.String,
176
- idempotencyKey: ({ id: e }) => `${u}:${e}`
202
+ }), h = () => s({
203
+ name: l,
204
+ payload: { id: x.String },
205
+ success: x.String,
206
+ idempotencyKey: ({ id: e }) => `${l}:${e}`
177
207
  }), y = (e, t) => e.toLayer(() => g.gen(function* () {
178
- return yield* s({
208
+ return yield* o({
179
209
  name: "step1",
180
- success: b.Void,
210
+ success: x.Void,
181
211
  execute: g.void
182
- }), yield* o({
212
+ }), yield* a({
183
213
  name: "nap",
184
- duration: j
185
- }), yield* s({
214
+ duration: P
215
+ }), yield* o({
186
216
  name: "step2",
187
- success: b.Void,
217
+ success: x.Void,
188
218
  execute: g.sync(() => {
189
219
  f.push(t);
190
220
  })
191
221
  }), "done";
192
- }).pipe(g.provideService(r, d), g.provideService(i, m))), x = e.runnerPort + 2, S = (e) => {
222
+ }).pipe(g.provideService(r, d), g.provideService(i, m))), b = e.runnerPort + 2, S = (e) => {
193
223
  let t = h();
194
224
  return {
195
225
  wf: t,
196
- layer: y(t, e).pipe(v.provideMerge(n(x)))
226
+ layer: y(t, e).pipe(v.provideMerge(n(b)))
197
227
  };
198
228
  }, C = { id: `loop-${t}` }, w = await g.runPromise(g.scoped(g.gen(function* () {
199
229
  let { wf: e, layer: t } = S("A"), n = yield* v.build(t), r = yield* g.forkDetach(e.execute(C).pipe(g.provide(n), g.ignore));
200
- return (yield* M(() => p.has(d), 6e4)) ? (yield* g.sleep("1 second"), r) : yield* g.die(/* @__PURE__ */ Error("workflow never registered a wakeup"));
230
+ return (yield* F(() => p.has(d), 6e4)) ? (yield* g.sleep("1 second"), r) : yield* g.die(/* @__PURE__ */ Error("workflow never registered a wakeup"));
201
231
  })));
202
- await g.runPromise(_.interrupt(w).pipe(g.ignore)), T(p.has(d)).toBe(!0), T(f).toEqual([]);
203
- let E = await g.runPromise(g.scoped(g.gen(function* () {
232
+ await g.runPromise(_.interrupt(w).pipe(g.ignore)), O(p.has(d)).toBe(!0), O(f).toEqual([]);
233
+ let T = await g.runPromise(g.scoped(g.gen(function* () {
204
234
  let { wf: e, layer: t } = S("B"), n = yield* v.build(t);
205
235
  return yield* e.execute(C).pipe(g.provide(n), g.timeout("600 seconds"), g.orElseSucceed(() => "TIMEOUT"));
206
236
  })));
207
- T(E).toBe("done"), T(f).toEqual(["B"]), T(p.has(d)).toBe(!1);
237
+ O(T).toBe("done"), O(f).toEqual(["B"]), O(p.has(d)).toBe(!1);
208
238
  }, 9e5);
209
239
  });
210
- }, P = {
240
+ }, L = {
211
241
  encrypt: (e) => `enc:v2:c:${Buffer.from(e, "utf8").toString("base64")}`,
212
242
  decrypt: (e) => Buffer.from(e.slice(9), "base64").toString("utf8")
213
- }, F = (r) => {
214
- let i = `${r.name}-enc-${Date.now()}`, o = () => f({
243
+ }, R = (r) => {
244
+ let i = `${r.name}-enc-${Date.now()}`, a = () => d({
215
245
  sqlClientLayer: r.sqlClientLayer,
216
246
  runnerStorage: r.runnerStorage,
217
247
  runnerListenPort: r.runnerPort + 7,
218
248
  ...r.dialectId ? { dialectId: r.dialectId } : {}
219
- }), l = (e) => g.runPromise(g.gen(function* () {
220
- let t = yield* x.SqlClient, n = yield* t.unsafe("SELECT * FROM cluster_messages"), r = yield* t.unsafe("SELECT * FROM cluster_replies"), i = (e) => JSON.stringify(e, (e, t) => typeof t == "bigint" ? String(t) : t);
249
+ }), u = (e) => g.runPromise(g.gen(function* () {
250
+ let t = yield* S.SqlClient, n = yield* t.unsafe("SELECT * FROM cluster_messages"), r = yield* t.unsafe("SELECT * FROM cluster_replies"), i = (e) => JSON.stringify(e, (e, t) => typeof t == "bigint" ? String(t) : t);
221
251
  return [...n, ...r].filter((t) => i(t).includes(e)).length;
222
- }).pipe(g.provideService(x.SafeIntegers, !0), g.scoped, g.provide(r.sqlClientLayer))), d = (e, t) => {
223
- let n = a({
252
+ }).pipe(g.provideService(S.SafeIntegers, !0), g.scoped, g.provide(r.sqlClientLayer))), f = (e, t) => {
253
+ let n = s({
224
254
  name: e,
225
255
  payload: {
226
- note: b.String,
227
- count: b.Number
256
+ note: x.String,
257
+ count: x.Number
228
258
  },
229
- success: b.Struct({ summary: b.String }),
259
+ success: x.Struct({ summary: x.String }),
230
260
  idempotencyKey: ({ count: t }) => `${e}:${t}`,
231
261
  encrypted: t
232
262
  }), r = 0;
233
263
  return {
234
264
  wf: n,
235
265
  handler: n.toLayer(({ note: e }) => g.gen(function* () {
236
- return { summary: `RESULT-${yield* s({
266
+ return { summary: `RESULT-${yield* o({
237
267
  name: "summarise",
238
- success: b.String,
268
+ success: x.String,
239
269
  execute: g.sync(() => (r++, `STEP-${e}`))
240
270
  })}` };
241
271
  })),
242
272
  executions: () => r
243
273
  };
244
- }, m = (e, t) => g.runPromise(g.scoped(e.wf.execute(t).pipe(g.provide(e.handler.pipe(v.provideMerge(o()))))));
245
- w(`encrypted workflow journal — ${r.name}`, () => {
246
- let f = !1;
247
- C(async () => {
248
- f = await r.reachable(), f && await k(r);
249
- }, 3e4), S(() => e(void 0)), E("payload, step result and result are ciphertext; the unencrypted twin is found (control)", async (t) => {
250
- f || t.skip(), e(P);
251
- let n = d(`journalPlain_${i}`, !1), r = d(`journalSealed_${i}`, !0);
252
- T(await m(n, {
274
+ }, m = (e, t) => g.runPromise(g.scoped(e.wf.execute(t).pipe(g.provide(e.handler.pipe(v.provideMerge(a()))))));
275
+ D(`encrypted workflow journal — ${r.name}`, () => {
276
+ let d = !1;
277
+ E(async () => {
278
+ d = await r.reachable(), d && await M(r);
279
+ }, 3e4), T(() => n(void 0)), k("payload, step result and result are ciphertext; the unencrypted twin is found (control)", async (e) => {
280
+ d || e.skip(), n(L);
281
+ let t = f(`journalPlain_${i}`, !1), r = f(`journalSealed_${i}`, !0);
282
+ O(await m(t, {
253
283
  note: "PLAINMARK",
254
284
  count: 1
255
- })).toEqual({ summary: "RESULT-STEP-PLAINMARK" }), T(await m(r, {
285
+ })).toEqual({ summary: "RESULT-STEP-PLAINMARK" }), O(await m(r, {
256
286
  note: "SEALMARK",
257
287
  count: 1
258
- })).toEqual({ summary: "RESULT-STEP-SEALMARK" }), T(await l("PLAINMARK"), "the probe finds plaintext").toBeGreaterThan(0), T(await l("SEALMARK"), "no journal row carries the encrypted run in plaintext").toBe(0), T(await l("enc:v2:c:"), "and the ciphertext is there").toBeGreaterThan(0);
259
- }, 6e4), E("a second execute replays the result from the encrypted journal without re-running the step", async (t) => {
260
- f || t.skip(), e(P);
261
- let n = d(`journalReplay_${i}`, !0), r = await m(n, {
288
+ })).toEqual({ summary: "RESULT-STEP-SEALMARK" }), O(await u("PLAINMARK"), "the probe finds plaintext").toBeGreaterThan(0), O(await u("SEALMARK"), "no journal row carries the encrypted run in plaintext").toBe(0), O(await u("enc:v2:c:"), "and the ciphertext is there").toBeGreaterThan(0);
289
+ }, 6e4), k("a second execute replays the result from the encrypted journal without re-running the step", async (e) => {
290
+ d || e.skip(), n(L);
291
+ let t = f(`journalReplay_${i}`, !0), r = await m(t, {
262
292
  note: "REPLAYMARK",
263
293
  count: 7
264
- }), a = await m(n, {
294
+ }), a = await m(t, {
265
295
  note: "REPLAYMARK",
266
296
  count: 7
267
297
  });
268
- T(a).toEqual(r), T(n.executions()).toBe(1);
269
- }, 6e4), E("a run journaled in plaintext before the declaration is still read", async (t) => {
270
- f || t.skip();
271
- let n = `journalBefore_${i}`, r = await m(d(n, !1), {
298
+ O(a).toEqual(r), O(t.executions()).toBe(1);
299
+ }, 6e4), k("a run journaled in plaintext before the declaration is still read", async (e) => {
300
+ d || e.skip();
301
+ let t = `journalBefore_${i}`, r = await m(f(t, !1), {
272
302
  note: "OLDMARK",
273
303
  count: 3
274
304
  });
275
- e(P);
276
- let a = d(n, !0);
277
- T(await m(a, {
305
+ n(L);
306
+ let a = f(t, !0);
307
+ O(await m(a, {
278
308
  note: "OLDMARK",
279
309
  count: 3
280
- })).toEqual(r), T(a.executions(), "answered from the plaintext journal, not re-run").toBe(0);
310
+ })).toEqual(r), O(a.executions(), "answered from the plaintext journal, not re-run").toBe(0);
281
311
  }, 6e4);
282
312
  let h = (e, t, n) => {
283
- class r extends b.TaggedError()("Refused", { detail: b.String }) {}
284
- let i = a({
313
+ class r extends x.TaggedError()("Refused", { detail: x.String }) {}
314
+ let i = s({
285
315
  name: e,
286
- payload: { note: b.String },
316
+ payload: { note: x.String },
287
317
  error: r,
288
318
  idempotencyKey: ({ note: t }) => `${e}:${t}`,
289
319
  encrypted: t
290
- }), c = i.toLayer(({ note: e }) => s({
320
+ }), c = i.toLayer(({ note: e }) => o({
291
321
  name: "call-upstream",
292
- success: b.String,
322
+ success: x.String,
293
323
  error: r,
294
324
  execute: n === "defect" ? g.die(/* @__PURE__ */ Error(`upstream rejected FAILMARK-${e}`)) : g.fail(new r({ detail: `FAILMARK-${e}` }))
295
325
  }));
296
- return g.runPromiseExit(g.scoped(i.execute({ note: e }).pipe(g.provide(c.pipe(v.provideMerge(o()))))));
326
+ return g.runPromiseExit(g.scoped(i.execute({ note: e }).pipe(g.provide(c.pipe(v.provideMerge(a()))))));
297
327
  };
298
- for (let n of ["defect", "typed"]) E(`a ${n} failure of an encrypted run leaves no plaintext; the unencrypted twin does (control)`, async (r) => {
299
- f || r.skip(), e(P);
300
- let a = await h(`journalFailPlain_${n}_${i}`, !1, n);
301
- T(a._tag).toBe("Failure"), T(await l(`FAILMARK-journalFailPlain_${n}`), "the probe finds plaintext").toBeGreaterThan(0);
302
- let o = await h(`journalFailSealed_${n}_${i}`, !0, n);
303
- T(o._tag).toBe("Failure"), T(await l(`FAILMARK-journalFailSealed_${n}`), "no journal row carries the failure in plaintext").toBe(0), T(t(p.squash(o.cause))).toContain(`FAILMARK-journalFailSealed_${n}`);
328
+ for (let t of ["defect", "typed"]) k(`a ${t} failure of an encrypted run leaves no plaintext; the unencrypted twin does (control)`, async (r) => {
329
+ d || r.skip(), n(L);
330
+ let a = await h(`journalFailPlain_${t}_${i}`, !1, t);
331
+ O(a._tag).toBe("Failure"), O(await u(`FAILMARK-journalFailPlain_${t}`), "the probe finds plaintext").toBeGreaterThan(0);
332
+ let o = await h(`journalFailSealed_${t}_${i}`, !0, t);
333
+ O(o._tag).toBe("Failure"), O(await u(`FAILMARK-journalFailSealed_${t}`), "no journal row carries the failure in plaintext").toBe(0), O(e(p.squash(o.cause))).toContain(`FAILMARK-journalFailSealed_${t}`);
304
334
  }, 6e4);
305
- E("a journaled wait value of an encrypted run is ciphertext; the unencrypted twin is found (control)", async (t) => {
306
- f || t.skip(), e(P);
307
- let r = (e, t) => {
308
- let r = a({
335
+ k("a journaled wait value of an encrypted run is ciphertext; the unencrypted twin is found (control)", async (e) => {
336
+ d || e.skip(), n(L);
337
+ let r = (e, n) => {
338
+ let r = s({
309
339
  name: e,
310
- payload: { note: b.String },
311
- success: b.String,
340
+ payload: { note: x.String },
341
+ success: x.String,
312
342
  idempotencyKey: ({ note: t }) => `${e}:${t}`,
313
- encrypted: t
314
- }), i = r.toLayer(({ note: e }) => n({
343
+ encrypted: n
344
+ }), i = r.toLayer(({ note: e }) => t({
315
345
  name: "await-signal:approval",
316
- success: b.String,
346
+ success: x.String,
317
347
  execute: g.succeed(`WAITMARK-${e}`)
318
348
  }));
319
- return g.runPromise(g.scoped(r.execute({ note: e }).pipe(g.provide(i.pipe(v.provideMerge(o()))))));
349
+ return g.runPromise(g.scoped(r.execute({ note: e }).pipe(g.provide(i.pipe(v.provideMerge(a()))))));
320
350
  };
321
- T(await r(`journalWaitPlain_${i}`, !1)).toBe(`WAITMARK-journalWaitPlain_${i}`), T(await l(`WAITMARK-journalWaitPlain_${i}`), "the probe finds plaintext").toBeGreaterThan(0), T(await r(`journalWaitSealed_${i}`, !0)).toBe(`WAITMARK-journalWaitSealed_${i}`), T(await l(`WAITMARK-journalWaitSealed_${i}`), "no plaintext wait value in the journal").toBe(0);
351
+ O(await r(`journalWaitPlain_${i}`, !1)).toBe(`WAITMARK-journalWaitPlain_${i}`), O(await u(`WAITMARK-journalWaitPlain_${i}`), "the probe finds plaintext").toBeGreaterThan(0), O(await r(`journalWaitSealed_${i}`, !0)).toBe(`WAITMARK-journalWaitSealed_${i}`), O(await u(`WAITMARK-journalWaitSealed_${i}`), "no plaintext wait value in the journal").toBe(0);
322
352
  }, 6e4);
323
353
  let _ = (e, t, n) => g.gen(function* () {
324
354
  for (let r = 0; r < 400; r++) {
@@ -327,77 +357,77 @@ var D = async (e, t, n = 750) => {
327
357
  yield* g.sleep("50 millis");
328
358
  }
329
359
  return yield* g.die(/* @__PURE__ */ Error("pollUntil: never satisfied"));
330
- }), x = (e) => y.isOption(e) && y.isSome(e) ? e.value._tag : void 0;
331
- for (let t of [!1, !0]) E(`a suspending signal's value is ${t ? "ciphertext" : "found (control)"} in the journal, and the run resumes with it`, async (n) => {
332
- f || n.skip(), e(P);
333
- let r = `journalSignal_${t ? "sealed" : "plain"}_${i}`, s = a({
360
+ }), y = (e) => b.isOption(e) && b.isSome(e) ? e.value._tag : void 0;
361
+ for (let e of [!1, !0]) k(`a suspending signal's value is ${e ? "ciphertext" : "found (control)"} in the journal, and the run resumes with it`, async (t) => {
362
+ d || t.skip(), n(L);
363
+ let r = `journalSignal_${e ? "sealed" : "plain"}_${i}`, o = s({
334
364
  name: r,
335
- payload: { id: b.String },
336
- success: b.Unknown,
365
+ payload: { id: x.String },
366
+ success: x.Unknown,
337
367
  idempotencyKey: ({ id: e }) => `${r}:${e}`,
338
- encrypted: t
339
- }), d = s.toLayer(() => c({ store: { query: () => Promise.resolve([]) } }, {
368
+ encrypted: e
369
+ }), f = o.toLayer(() => c({ store: { query: () => Promise.resolve([]) } }, {
340
370
  name: "approval",
341
- schema: b.Struct({ comment: b.String }),
371
+ schema: x.Struct({ comment: x.String }),
342
372
  timeoutMs: 36e5
343
373
  })), p = `SIGNALMARK-${r}`, m = await g.runPromise(g.scoped(g.gen(function* () {
344
- let e = yield* s.executionId({ id: "one" });
345
- return yield* s.execute({ id: "one" }, { discard: !0 }), yield* _(s, e, (e) => x(e) === "Suspended"), yield* u({
374
+ let e = yield* o.executionId({ id: "one" });
375
+ return yield* o.execute({ id: "one" }, { discard: !0 }), yield* _(o, e, (e) => y(e) === "Suspended"), yield* l({
346
376
  target: {
347
377
  executionId: e,
348
378
  workflowName: r
349
379
  },
350
380
  signalName: "approval",
351
381
  payload: { comment: p }
352
- }), yield* _(s, e, (e) => x(e) === "Complete");
353
- }).pipe(g.provide(d.pipe(v.provideMerge(o()))))));
354
- T(JSON.stringify(y.getOrThrow(m).exit), "the body received the value").toContain(p);
355
- let h = await l(p);
356
- t ? T(h, "no plaintext signal value in the journal").toBe(0) : T(h, "the probe finds plaintext").toBeGreaterThan(0);
382
+ }), yield* _(o, e, (e) => y(e) === "Complete");
383
+ }).pipe(g.provide(f.pipe(v.provideMerge(a()))))));
384
+ O(JSON.stringify(b.getOrThrow(m).exit), "the body received the value").toContain(p);
385
+ let h = await u(p);
386
+ e ? O(h, "no plaintext signal value in the journal").toBe(0) : O(h, "the probe finds plaintext").toBeGreaterThan(0);
357
387
  }, 9e4);
358
- E("an encrypted child's defect reaches its encrypted parent as thrown, and no journal row carries it", async (t) => {
359
- f || t.skip(), e(P);
360
- let n = `journalChild_${i}`, r = `journalParent_${i}`, s = `CHILDMARK-${i}`, c = a({
361
- name: n,
362
- payload: { id: b.String },
363
- success: b.String,
388
+ k("an encrypted child's defect reaches its encrypted parent as thrown, and no journal row carries it", async (e) => {
389
+ d || e.skip(), n(L);
390
+ let t = `journalChild_${i}`, r = `journalParent_${i}`, o = `CHILDMARK-${i}`, c = s({
391
+ name: t,
392
+ payload: { id: x.String },
393
+ success: x.String,
364
394
  idempotencyKey: ({ id: e }) => e,
365
395
  encrypted: !0
366
- }), u = a({
396
+ }), l = s({
367
397
  name: r,
368
- payload: { id: b.String },
369
- success: b.String,
398
+ payload: { id: x.String },
399
+ success: x.String,
370
400
  idempotencyKey: ({ id: e }) => e,
371
401
  encrypted: !0
372
- }), d = c.toLayer(() => g.die(/* @__PURE__ */ Error(`child failed ${s}`))), p = u.toLayer(({ id: e }) => c.execute({ id: `${e}-child` }).pipe(g.catchDefect((e) => g.succeed(`caught: ${e.message}`)))), m = await g.runPromise(g.scoped(u.execute({ id: "p1" }).pipe(g.provide(v.mergeAll(p, d).pipe(v.provideMerge(o()))))));
373
- T(m).toBe(`caught: child failed ${s}`), T(await l(s)).toBe(0);
374
- }, 6e4), E("an encrypted run that suspends on a failed step journals no plaintext", async (t) => {
375
- f || t.skip(), e(P);
376
- let n = `journalSuspendOnFailure_${i}`, r = `SOFMARK-${i}`, c = a({
377
- name: n,
378
- payload: { id: b.String },
379
- success: b.String,
402
+ }), f = c.toLayer(() => g.die(/* @__PURE__ */ Error(`child failed ${o}`))), p = l.toLayer(({ id: e }) => c.execute({ id: `${e}-child` }).pipe(g.catchDefect((e) => g.succeed(`caught: ${e.message}`)))), m = await g.runPromise(g.scoped(l.execute({ id: "p1" }).pipe(g.provide(v.mergeAll(p, f).pipe(v.provideMerge(a()))))));
403
+ O(m).toBe(`caught: child failed ${o}`), O(await u(o)).toBe(0);
404
+ }, 6e4), k("an encrypted run that suspends on a failed step journals no plaintext", async (e) => {
405
+ d || e.skip(), n(L);
406
+ let t = `journalSuspendOnFailure_${i}`, r = `SOFMARK-${i}`, c = s({
407
+ name: t,
408
+ payload: { id: x.String },
409
+ success: x.String,
380
410
  idempotencyKey: ({ id: e }) => e,
381
411
  encrypted: !0,
382
412
  suspendOnFailure: !0
383
- }), u = c.toLayer(() => s({
413
+ }), l = c.toLayer(() => o({
384
414
  name: "call",
385
- success: b.String,
415
+ success: x.String,
386
416
  execute: g.die(/* @__PURE__ */ Error(`step failed ${r}`))
387
417
  }));
388
418
  await g.runPromise(g.scoped(g.gen(function* () {
389
419
  let e = yield* c.executionId({ id: "s1" });
390
- yield* c.execute({ id: "s1" }, { discard: !0 }), yield* _(c, e, (e) => x(e) === "Suspended");
391
- }).pipe(g.provide(u.pipe(v.provideMerge(o())))))), T(await l(r)).toBe(0);
392
- }, 6e4), E("without a registered cipher an encrypted run fails instead of writing plaintext", async (e) => {
393
- f || e.skip();
394
- let t = d(`journalNoCipher_${i}`, !0);
395
- await T(m(t, {
420
+ yield* c.execute({ id: "s1" }, { discard: !0 }), yield* _(c, e, (e) => y(e) === "Suspended");
421
+ }).pipe(g.provide(l.pipe(v.provideMerge(a())))))), O(await u(r)).toBe(0);
422
+ }, 6e4), k("without a registered cipher an encrypted run fails instead of writing plaintext", async (e) => {
423
+ d || e.skip();
424
+ let t = f(`journalNoCipher_${i}`, !0);
425
+ await O(m(t, {
396
426
  note: "NOCIPHERMARK",
397
427
  count: 1
398
- })).rejects.toThrow(), T(await l("NOCIPHERMARK")).toBe(0);
428
+ })).rejects.toThrow(), O(await u("NOCIPHERMARK")).toBe(0);
399
429
  }, 6e4);
400
430
  });
401
431
  };
402
432
  //#endregion
403
- export { k as purgeClusterState, N as runClusterEngineSuite, F as runJournalEncryptionSuite, D as tcpReachable };
433
+ export { M as purgeClusterState, I as runClusterEngineSuite, R as runJournalEncryptionSuite, A as tcpReachable };
@@ -0,0 +1,13 @@
1
+ import { readPendingWorkflowCancellation as e } from "./controlRequest.js";
2
+ import { Effect as t } from "effect";
3
+ import { WorkflowInstance as n } from "effect/workflow/WorkflowEngine";
4
+ //#region src/controlMonitor.ts
5
+ var r = (r, i, a) => t.gen(function* () {
6
+ let o = yield* n;
7
+ for (;;) {
8
+ if (yield* t.tryPromise(() => e(i, r)).pipe(t.catch(() => t.sync(() => (a(), !1))))) return o.interrupted = !0, yield* t.interrupt;
9
+ yield* t.sleep(1e3);
10
+ }
11
+ });
12
+ //#endregion
13
+ export { r as awaitWorkflowCancellation };