@voltro/workflow 0.84.0 → 0.85.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 +549 -0
- package/THIRD-PARTY-NOTICES.md +1 -119
- package/dist/cluster-F_bhqVP7.js +679 -0
- package/dist/cluster.d.ts +304 -280
- package/dist/cluster.js +2 -2
- package/dist/clusterTestSuite.d.ts +11 -11
- package/dist/clusterTestSuite.js +256 -256
- package/dist/index.d.ts +376 -354
- package/dist/index.js +5 -5
- package/dist/journalEncryption-DTyhUzaS.js +154 -0
- package/dist/{primitives-Btjhjm8P.js → primitives-DX04WTWh.js} +223 -199
- package/dist/primitives.d.ts +56 -31
- package/dist/primitives.js +2 -2
- package/dist/{src-DP-lKqaG.js → src-C2NEyUM8.js} +1111 -1053
- package/dist/{clusterDiagnostics-CGUtnJkX.js → workflowReadSelections-CZAUjP7f.js} +58 -46
- package/package.json +6 -9
- package/dist/cluster-i3frC3Da.js +0 -646
- package/dist/journalEncryption--n5VDdYO.js +0 -148
package/dist/clusterTestSuite.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { C as
|
|
4
|
-
import { t as
|
|
5
|
-
import { Cause as
|
|
6
|
-
import { SqlClient as
|
|
7
|
-
import { afterEach as
|
|
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";
|
|
8
8
|
//#region src/clusterTestSuite.ts
|
|
9
|
-
var
|
|
9
|
+
var D = async (e, t, n = 750) => {
|
|
10
10
|
try {
|
|
11
11
|
let r = await import("node:net");
|
|
12
12
|
return await new Promise((i) => {
|
|
@@ -22,140 +22,140 @@ var T = async (e, t, n = 750) => {
|
|
|
22
22
|
} catch {
|
|
23
23
|
return !1;
|
|
24
24
|
}
|
|
25
|
-
},
|
|
25
|
+
}, O = [
|
|
26
26
|
"cluster_messages",
|
|
27
27
|
"cluster_replies",
|
|
28
28
|
"cluster_locks",
|
|
29
29
|
"cluster_runners"
|
|
30
|
-
],
|
|
30
|
+
], k = async (e) => {
|
|
31
31
|
try {
|
|
32
|
-
await
|
|
33
|
-
let e = yield*
|
|
34
|
-
for (let t of
|
|
35
|
-
}).pipe(
|
|
32
|
+
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);
|
|
35
|
+
}).pipe(g.scoped, g.provide(e.sqlClientLayer)));
|
|
36
36
|
} catch {}
|
|
37
|
-
},
|
|
38
|
-
let n = yield*
|
|
37
|
+
}, A = "10 seconds", j = "30 seconds", M = (e, t) => g.gen(function* () {
|
|
38
|
+
let n = yield* g.sync(() => Date.now() + t);
|
|
39
39
|
for (; !e();) {
|
|
40
40
|
if (Date.now() > n) return !1;
|
|
41
|
-
yield*
|
|
41
|
+
yield* g.sleep("250 millis");
|
|
42
42
|
}
|
|
43
43
|
return !0;
|
|
44
|
-
}),
|
|
45
|
-
let
|
|
46
|
-
sqlClientLayer:
|
|
47
|
-
runnerStorage:
|
|
48
|
-
runnerListenPort:
|
|
49
|
-
...
|
|
50
|
-
...
|
|
51
|
-
...
|
|
44
|
+
}), N = (e) => {
|
|
45
|
+
let t = `${e.name}-${Date.now()}`, n = (t, n) => f({
|
|
46
|
+
sqlClientLayer: e.sqlClientLayer,
|
|
47
|
+
runnerStorage: e.runnerStorage,
|
|
48
|
+
runnerListenPort: t,
|
|
49
|
+
...e.dialectId ? { dialectId: e.dialectId } : {},
|
|
50
|
+
...e.shardLockMode ? { shardLockMode: e.shardLockMode } : {},
|
|
51
|
+
...n && n.length > 0 ? { extraShardingLayers: n } : {}
|
|
52
52
|
});
|
|
53
|
-
|
|
54
|
-
let
|
|
55
|
-
|
|
56
|
-
if (
|
|
57
|
-
console.warn(`[skip] cluster suite — ${
|
|
53
|
+
w(`cluster workflow engine — ${e.name}`, () => {
|
|
54
|
+
let c = !1;
|
|
55
|
+
C(async () => {
|
|
56
|
+
if (c = await e.reachable(), !c) {
|
|
57
|
+
console.warn(`[skip] cluster suite — ${e.name} not reachable`);
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
60
|
-
await
|
|
61
|
-
}, 3e4),
|
|
62
|
-
if (!
|
|
63
|
-
let
|
|
64
|
-
name:
|
|
65
|
-
payload: { id:
|
|
66
|
-
success:
|
|
67
|
-
idempotencyKey: ({ id:
|
|
68
|
-
}),
|
|
69
|
-
let e = yield*
|
|
60
|
+
await k(e);
|
|
61
|
+
}, 3e4), E("a two-step workflow runs end-to-end", async () => {
|
|
62
|
+
if (!c) return;
|
|
63
|
+
let r = `clusterTwoStep_${t}`, i = a({
|
|
64
|
+
name: r,
|
|
65
|
+
payload: { id: b.String },
|
|
66
|
+
success: b.Number,
|
|
67
|
+
idempotencyKey: ({ id: e }) => `${r}:${e}`
|
|
68
|
+
}), o = i.toLayer(() => g.gen(function* () {
|
|
69
|
+
let e = yield* s({
|
|
70
70
|
name: "first",
|
|
71
|
-
success:
|
|
72
|
-
execute:
|
|
71
|
+
success: b.Number,
|
|
72
|
+
execute: g.succeed(40)
|
|
73
73
|
});
|
|
74
|
-
return yield*
|
|
74
|
+
return yield* s({
|
|
75
75
|
name: "second",
|
|
76
|
-
success:
|
|
77
|
-
execute:
|
|
76
|
+
success: b.Number,
|
|
77
|
+
execute: g.succeed(e + 2)
|
|
78
78
|
});
|
|
79
|
-
})),
|
|
80
|
-
|
|
81
|
-
}, 3e4),
|
|
82
|
-
if (!
|
|
83
|
-
let
|
|
84
|
-
name:
|
|
85
|
-
payload: { id:
|
|
86
|
-
success:
|
|
87
|
-
idempotencyKey: ({ id:
|
|
88
|
-
}),
|
|
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 () => {
|
|
82
|
+
if (!c) return;
|
|
83
|
+
let r = `clusterIdempotency_${t}`, i = 0, o = a({
|
|
84
|
+
name: r,
|
|
85
|
+
payload: { id: b.String },
|
|
86
|
+
success: b.Number,
|
|
87
|
+
idempotencyKey: ({ id: e }) => `${r}:${e}`
|
|
88
|
+
}), l = o.toLayer(() => s({
|
|
89
89
|
name: "compute",
|
|
90
|
-
success:
|
|
91
|
-
execute:
|
|
92
|
-
})).pipe(
|
|
93
|
-
return [yield*
|
|
94
|
-
}).pipe(
|
|
95
|
-
|
|
96
|
-
}, 3e4),
|
|
97
|
-
if (!
|
|
98
|
-
let
|
|
99
|
-
name: `clusterCron_${
|
|
100
|
-
cron:
|
|
90
|
+
success: b.Number,
|
|
91
|
+
execute: g.sync(() => (i++, i * 100))
|
|
92
|
+
})).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}` })];
|
|
94
|
+
}).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 () => {
|
|
97
|
+
if (!c) return;
|
|
98
|
+
let r = await g.runPromise(h.make()), i = 0, a = d({
|
|
99
|
+
name: `clusterCron_${t}`,
|
|
100
|
+
cron: m.parseUnsafe("*/1 * * * * *", "UTC"),
|
|
101
101
|
execute: async () => {
|
|
102
|
-
|
|
102
|
+
i += 1, await g.runPromise(h.succeed(r, !0)).catch(() => {});
|
|
103
103
|
}
|
|
104
|
-
}),
|
|
105
|
-
return yield*
|
|
104
|
+
}), o = await g.runPromise(g.scoped(g.gen(function* () {
|
|
105
|
+
return yield* v.build(n(e.runnerPort + 1, [a])), yield* h.await(r).pipe(g.timeout("25 seconds"), g.orElseSucceed(() => !1));
|
|
106
106
|
})));
|
|
107
|
-
|
|
107
|
+
T(o).toBe(!0), T(i).toBeGreaterThanOrEqual(1);
|
|
108
108
|
}, 35e3);
|
|
109
|
-
let
|
|
110
|
-
|
|
111
|
-
if (!
|
|
112
|
-
let
|
|
113
|
-
name:
|
|
114
|
-
payload: { id:
|
|
115
|
-
success:
|
|
116
|
-
idempotencyKey: ({ id:
|
|
117
|
-
}), f = (e,
|
|
118
|
-
return yield*
|
|
109
|
+
let u = e.clusterResume ? E : (() => {});
|
|
110
|
+
u("a sleeping workflow resumes on a fresh runner after the first is torn down", async () => {
|
|
111
|
+
if (!c) return;
|
|
112
|
+
let r = `dormancyResume_${t}`, i = [], l = [], u = [], d = () => a({
|
|
113
|
+
name: r,
|
|
114
|
+
payload: { id: b.String },
|
|
115
|
+
success: b.String,
|
|
116
|
+
idempotencyKey: ({ id: e }) => `${r}:${e}`
|
|
117
|
+
}), f = (e, t) => e.toLayer(() => g.gen(function* () {
|
|
118
|
+
return yield* s({
|
|
119
119
|
name: "step1",
|
|
120
|
-
success:
|
|
121
|
-
execute:
|
|
122
|
-
|
|
120
|
+
success: b.Void,
|
|
121
|
+
execute: g.sync(() => {
|
|
122
|
+
i.push(t);
|
|
123
123
|
})
|
|
124
|
-
}), yield*
|
|
124
|
+
}), yield* s({
|
|
125
125
|
name: "armed",
|
|
126
|
-
success:
|
|
127
|
-
execute:
|
|
128
|
-
u.push(
|
|
126
|
+
success: b.Void,
|
|
127
|
+
execute: g.sync(() => {
|
|
128
|
+
u.push(t);
|
|
129
129
|
})
|
|
130
|
-
}), yield*
|
|
130
|
+
}), yield* o({
|
|
131
131
|
name: "nap",
|
|
132
|
-
duration:
|
|
133
|
-
}), yield*
|
|
132
|
+
duration: A
|
|
133
|
+
}), yield* s({
|
|
134
134
|
name: "step2",
|
|
135
|
-
success:
|
|
136
|
-
execute:
|
|
137
|
-
|
|
135
|
+
success: b.Void,
|
|
136
|
+
execute: g.sync(() => {
|
|
137
|
+
l.push(t);
|
|
138
138
|
})
|
|
139
139
|
}), "done";
|
|
140
|
-
})), p =
|
|
140
|
+
})), p = e.runnerPort + 2, m = (e) => {
|
|
141
141
|
let t = d();
|
|
142
142
|
return {
|
|
143
143
|
wf: t,
|
|
144
|
-
layer: f(t, e).pipe(
|
|
144
|
+
layer: f(t, e).pipe(v.provideMerge(n(p)))
|
|
145
145
|
};
|
|
146
|
-
},
|
|
147
|
-
let { wf: e, layer: t } = m("A"), n = yield*
|
|
148
|
-
return (yield*
|
|
146
|
+
}, h = { id: `resume-${t}` }, y = await g.runPromise(g.scoped(g.gen(function* () {
|
|
147
|
+
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"));
|
|
149
149
|
})));
|
|
150
|
-
await
|
|
151
|
-
let x = await
|
|
152
|
-
let { wf: e, layer: t } = m("B"), n = yield*
|
|
153
|
-
return yield* e.execute(
|
|
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* () {
|
|
152
|
+
let { wf: e, layer: t } = m("B"), n = yield* v.build(t);
|
|
153
|
+
return yield* e.execute(h).pipe(g.provide(n), g.timeout("240 seconds"), g.orElseSucceed(() => "TIMEOUT"));
|
|
154
154
|
})));
|
|
155
|
-
|
|
156
|
-
}, 48e4),
|
|
157
|
-
if (!
|
|
158
|
-
let u = `dormancyLoop_${
|
|
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 () => {
|
|
157
|
+
if (!c) return;
|
|
158
|
+
let u = `dormancyLoop_${t}`, d = `wr_${u}`, f = [], p = /* @__PURE__ */ new Map(), m = l({
|
|
159
159
|
store: {
|
|
160
160
|
insert: async (e, t) => t,
|
|
161
161
|
update: async () => null,
|
|
@@ -169,235 +169,235 @@ var T = async (e, t, n = 750) => {
|
|
|
169
169
|
p.delete(e);
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
|
-
}),
|
|
172
|
+
}), h = () => a({
|
|
173
173
|
name: u,
|
|
174
|
-
payload: { id:
|
|
175
|
-
success:
|
|
174
|
+
payload: { id: b.String },
|
|
175
|
+
success: b.String,
|
|
176
176
|
idempotencyKey: ({ id: e }) => `${u}:${e}`
|
|
177
|
-
}),
|
|
178
|
-
return yield*
|
|
177
|
+
}), y = (e, t) => e.toLayer(() => g.gen(function* () {
|
|
178
|
+
return yield* s({
|
|
179
179
|
name: "step1",
|
|
180
|
-
success:
|
|
181
|
-
execute:
|
|
182
|
-
}), yield*
|
|
180
|
+
success: b.Void,
|
|
181
|
+
execute: g.void
|
|
182
|
+
}), yield* o({
|
|
183
183
|
name: "nap",
|
|
184
|
-
duration:
|
|
185
|
-
}), yield*
|
|
184
|
+
duration: j
|
|
185
|
+
}), yield* s({
|
|
186
186
|
name: "step2",
|
|
187
|
-
success:
|
|
188
|
-
execute:
|
|
189
|
-
f.push(
|
|
187
|
+
success: b.Void,
|
|
188
|
+
execute: g.sync(() => {
|
|
189
|
+
f.push(t);
|
|
190
190
|
})
|
|
191
191
|
}), "done";
|
|
192
|
-
}).pipe(
|
|
193
|
-
let t =
|
|
192
|
+
}).pipe(g.provideService(r, d), g.provideService(i, m))), x = e.runnerPort + 2, S = (e) => {
|
|
193
|
+
let t = h();
|
|
194
194
|
return {
|
|
195
195
|
wf: t,
|
|
196
|
-
layer:
|
|
196
|
+
layer: y(t, e).pipe(v.provideMerge(n(x)))
|
|
197
197
|
};
|
|
198
|
-
},
|
|
199
|
-
let { wf: e, layer: t } = S("A"), n = yield*
|
|
200
|
-
return (yield*
|
|
198
|
+
}, C = { id: `loop-${t}` }, w = await g.runPromise(g.scoped(g.gen(function* () {
|
|
199
|
+
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"));
|
|
201
201
|
})));
|
|
202
|
-
await
|
|
203
|
-
let E = await
|
|
204
|
-
let { wf: e, layer: t } = S("B"), n = yield*
|
|
205
|
-
return yield* e.execute(
|
|
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* () {
|
|
204
|
+
let { wf: e, layer: t } = S("B"), n = yield* v.build(t);
|
|
205
|
+
return yield* e.execute(C).pipe(g.provide(n), g.timeout("600 seconds"), g.orElseSucceed(() => "TIMEOUT"));
|
|
206
206
|
})));
|
|
207
|
-
|
|
207
|
+
T(E).toBe("done"), T(f).toEqual(["B"]), T(p.has(d)).toBe(!1);
|
|
208
208
|
}, 9e5);
|
|
209
209
|
});
|
|
210
|
-
},
|
|
210
|
+
}, P = {
|
|
211
211
|
encrypt: (e) => `enc:v2:c:${Buffer.from(e, "utf8").toString("base64")}`,
|
|
212
212
|
decrypt: (e) => Buffer.from(e.slice(9), "base64").toString("utf8")
|
|
213
|
-
},
|
|
214
|
-
let
|
|
215
|
-
sqlClientLayer:
|
|
216
|
-
runnerStorage:
|
|
217
|
-
runnerListenPort:
|
|
218
|
-
...
|
|
219
|
-
}),
|
|
220
|
-
let
|
|
221
|
-
return [...n, ...r].filter((
|
|
222
|
-
}).pipe(
|
|
223
|
-
let
|
|
213
|
+
}, F = (r) => {
|
|
214
|
+
let i = `${r.name}-enc-${Date.now()}`, o = () => f({
|
|
215
|
+
sqlClientLayer: r.sqlClientLayer,
|
|
216
|
+
runnerStorage: r.runnerStorage,
|
|
217
|
+
runnerListenPort: r.runnerPort + 7,
|
|
218
|
+
...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);
|
|
221
|
+
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({
|
|
224
224
|
name: e,
|
|
225
225
|
payload: {
|
|
226
|
-
note:
|
|
227
|
-
count:
|
|
226
|
+
note: b.String,
|
|
227
|
+
count: b.Number
|
|
228
228
|
},
|
|
229
|
-
success:
|
|
229
|
+
success: b.Struct({ summary: b.String }),
|
|
230
230
|
idempotencyKey: ({ count: t }) => `${e}:${t}`,
|
|
231
231
|
encrypted: t
|
|
232
|
-
}),
|
|
232
|
+
}), r = 0;
|
|
233
233
|
return {
|
|
234
|
-
wf:
|
|
235
|
-
handler:
|
|
236
|
-
return { summary: `RESULT-${yield*
|
|
234
|
+
wf: n,
|
|
235
|
+
handler: n.toLayer(({ note: e }) => g.gen(function* () {
|
|
236
|
+
return { summary: `RESULT-${yield* s({
|
|
237
237
|
name: "summarise",
|
|
238
|
-
success:
|
|
239
|
-
execute:
|
|
238
|
+
success: b.String,
|
|
239
|
+
execute: g.sync(() => (r++, `STEP-${e}`))
|
|
240
240
|
})}` };
|
|
241
241
|
})),
|
|
242
|
-
executions: () =>
|
|
242
|
+
executions: () => r
|
|
243
243
|
};
|
|
244
|
-
},
|
|
245
|
-
|
|
246
|
-
let
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}, 3e4),
|
|
250
|
-
|
|
251
|
-
let n =
|
|
252
|
-
|
|
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, {
|
|
253
253
|
note: "PLAINMARK",
|
|
254
254
|
count: 1
|
|
255
|
-
})).toEqual({ summary: "RESULT-STEP-PLAINMARK" }),
|
|
255
|
+
})).toEqual({ summary: "RESULT-STEP-PLAINMARK" }), T(await m(r, {
|
|
256
256
|
note: "SEALMARK",
|
|
257
257
|
count: 1
|
|
258
|
-
})).toEqual({ summary: "RESULT-STEP-SEALMARK" }),
|
|
259
|
-
}, 6e4),
|
|
260
|
-
|
|
261
|
-
let n =
|
|
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, {
|
|
262
262
|
note: "REPLAYMARK",
|
|
263
263
|
count: 7
|
|
264
|
-
}),
|
|
264
|
+
}), a = await m(n, {
|
|
265
265
|
note: "REPLAYMARK",
|
|
266
266
|
count: 7
|
|
267
267
|
});
|
|
268
|
-
|
|
269
|
-
}, 6e4),
|
|
270
|
-
|
|
271
|
-
let n = `journalBefore_${
|
|
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), {
|
|
272
272
|
note: "OLDMARK",
|
|
273
273
|
count: 3
|
|
274
274
|
});
|
|
275
|
-
|
|
276
|
-
let
|
|
277
|
-
|
|
275
|
+
e(P);
|
|
276
|
+
let a = d(n, !0);
|
|
277
|
+
T(await m(a, {
|
|
278
278
|
note: "OLDMARK",
|
|
279
279
|
count: 3
|
|
280
|
-
})).toEqual(r),
|
|
280
|
+
})).toEqual(r), T(a.executions(), "answered from the plaintext journal, not re-run").toBe(0);
|
|
281
281
|
}, 6e4);
|
|
282
|
-
let
|
|
283
|
-
class
|
|
284
|
-
let
|
|
282
|
+
let h = (e, t, n) => {
|
|
283
|
+
class r extends b.TaggedError()("Refused", { detail: b.String }) {}
|
|
284
|
+
let i = a({
|
|
285
285
|
name: e,
|
|
286
|
-
payload: { note:
|
|
287
|
-
error:
|
|
286
|
+
payload: { note: b.String },
|
|
287
|
+
error: r,
|
|
288
288
|
idempotencyKey: ({ note: t }) => `${e}:${t}`,
|
|
289
289
|
encrypted: t
|
|
290
|
-
}), c =
|
|
290
|
+
}), c = i.toLayer(({ note: e }) => s({
|
|
291
291
|
name: "call-upstream",
|
|
292
|
-
success:
|
|
293
|
-
error:
|
|
294
|
-
execute:
|
|
292
|
+
success: b.String,
|
|
293
|
+
error: r,
|
|
294
|
+
execute: n === "defect" ? g.die(/* @__PURE__ */ Error(`upstream rejected FAILMARK-${e}`)) : g.fail(new r({ detail: `FAILMARK-${e}` }))
|
|
295
295
|
}));
|
|
296
|
-
return
|
|
296
|
+
return g.runPromiseExit(g.scoped(i.execute({ note: e }).pipe(g.provide(c.pipe(v.provideMerge(o()))))));
|
|
297
297
|
};
|
|
298
|
-
for (let
|
|
299
|
-
|
|
300
|
-
let
|
|
301
|
-
|
|
302
|
-
let
|
|
303
|
-
|
|
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}`);
|
|
304
304
|
}, 6e4);
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
let
|
|
308
|
-
let
|
|
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({
|
|
309
309
|
name: e,
|
|
310
|
-
payload: { note:
|
|
311
|
-
success:
|
|
310
|
+
payload: { note: b.String },
|
|
311
|
+
success: b.String,
|
|
312
312
|
idempotencyKey: ({ note: t }) => `${e}:${t}`,
|
|
313
313
|
encrypted: t
|
|
314
|
-
}),
|
|
314
|
+
}), i = r.toLayer(({ note: e }) => n({
|
|
315
315
|
name: "await-signal:approval",
|
|
316
|
-
success:
|
|
317
|
-
execute:
|
|
316
|
+
success: b.String,
|
|
317
|
+
execute: g.succeed(`WAITMARK-${e}`)
|
|
318
318
|
}));
|
|
319
|
-
return
|
|
319
|
+
return g.runPromise(g.scoped(r.execute({ note: e }).pipe(g.provide(i.pipe(v.provideMerge(o()))))));
|
|
320
320
|
};
|
|
321
|
-
|
|
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);
|
|
322
322
|
}, 6e4);
|
|
323
|
-
let
|
|
323
|
+
let _ = (e, t, n) => g.gen(function* () {
|
|
324
324
|
for (let r = 0; r < 400; r++) {
|
|
325
325
|
let r = yield* e.poll(t);
|
|
326
326
|
if (n(r)) return r;
|
|
327
|
-
yield*
|
|
327
|
+
yield* g.sleep("50 millis");
|
|
328
328
|
}
|
|
329
|
-
return yield*
|
|
330
|
-
}),
|
|
331
|
-
for (let
|
|
332
|
-
|
|
333
|
-
let
|
|
334
|
-
name:
|
|
335
|
-
payload: { id:
|
|
336
|
-
success:
|
|
337
|
-
idempotencyKey: ({ id: e }) => `${
|
|
338
|
-
encrypted:
|
|
339
|
-
}),
|
|
329
|
+
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({
|
|
334
|
+
name: r,
|
|
335
|
+
payload: { id: b.String },
|
|
336
|
+
success: b.Unknown,
|
|
337
|
+
idempotencyKey: ({ id: e }) => `${r}:${e}`,
|
|
338
|
+
encrypted: t
|
|
339
|
+
}), d = s.toLayer(() => c({ store: { query: () => Promise.resolve([]) } }, {
|
|
340
340
|
name: "approval",
|
|
341
|
-
schema:
|
|
341
|
+
schema: b.Struct({ comment: b.String }),
|
|
342
342
|
timeoutMs: 36e5
|
|
343
|
-
})), p = `SIGNALMARK-${
|
|
344
|
-
let e = yield*
|
|
345
|
-
return yield*
|
|
343
|
+
})), 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({
|
|
346
346
|
target: {
|
|
347
347
|
executionId: e,
|
|
348
|
-
workflowName:
|
|
348
|
+
workflowName: r
|
|
349
349
|
},
|
|
350
350
|
signalName: "approval",
|
|
351
351
|
payload: { comment: p }
|
|
352
|
-
}), yield*
|
|
353
|
-
}).pipe(
|
|
354
|
-
|
|
355
|
-
let
|
|
356
|
-
|
|
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);
|
|
357
357
|
}, 9e4);
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
let
|
|
361
|
-
name:
|
|
362
|
-
payload: { id:
|
|
363
|
-
success:
|
|
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,
|
|
364
364
|
idempotencyKey: ({ id: e }) => e,
|
|
365
365
|
encrypted: !0
|
|
366
|
-
}), u =
|
|
367
|
-
name:
|
|
368
|
-
payload: { id:
|
|
369
|
-
success:
|
|
366
|
+
}), u = a({
|
|
367
|
+
name: r,
|
|
368
|
+
payload: { id: b.String },
|
|
369
|
+
success: b.String,
|
|
370
370
|
idempotencyKey: ({ id: e }) => e,
|
|
371
371
|
encrypted: !0
|
|
372
|
-
}),
|
|
373
|
-
|
|
374
|
-
}, 6e4),
|
|
375
|
-
|
|
376
|
-
let
|
|
377
|
-
name:
|
|
378
|
-
payload: { id:
|
|
379
|
-
success:
|
|
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,
|
|
380
380
|
idempotencyKey: ({ id: e }) => e,
|
|
381
381
|
encrypted: !0,
|
|
382
382
|
suspendOnFailure: !0
|
|
383
|
-
}), u =
|
|
383
|
+
}), u = c.toLayer(() => s({
|
|
384
384
|
name: "call",
|
|
385
|
-
success:
|
|
386
|
-
execute:
|
|
385
|
+
success: b.String,
|
|
386
|
+
execute: g.die(/* @__PURE__ */ Error(`step failed ${r}`))
|
|
387
387
|
}));
|
|
388
|
-
await
|
|
389
|
-
let e = yield*
|
|
390
|
-
yield*
|
|
391
|
-
}).pipe(
|
|
392
|
-
}, 6e4),
|
|
393
|
-
|
|
394
|
-
let
|
|
395
|
-
await
|
|
388
|
+
await g.runPromise(g.scoped(g.gen(function* () {
|
|
389
|
+
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, {
|
|
396
396
|
note: "NOCIPHERMARK",
|
|
397
397
|
count: 1
|
|
398
|
-
})).rejects.toThrow(),
|
|
398
|
+
})).rejects.toThrow(), T(await l("NOCIPHERMARK")).toBe(0);
|
|
399
399
|
}, 6e4);
|
|
400
400
|
});
|
|
401
401
|
};
|
|
402
402
|
//#endregion
|
|
403
|
-
export {
|
|
403
|
+
export { k as purgeClusterState, N as runClusterEngineSuite, F as runJournalEncryptionSuite, D as tcpReachable };
|