@voltro/workflow 0.34.0 → 0.35.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 +167 -0
- package/dist/{cluster-CyIyBO39.js → cluster-CVyPUB9X.js} +43 -34
- package/dist/cluster.js +1 -1
- package/dist/clusterTestSuite.js +4 -4
- package/dist/index.d.ts +192 -1
- package/dist/index.js +3 -3
- package/dist/{primitives-oLKOTbvJ.js → primitives-CyQZeT1t.js} +142 -98
- package/dist/primitives.d.ts +94 -0
- package/dist/primitives.js +2 -2
- package/dist/{src-BXObaqb-.js → src-DPkbBZDD.js} +535 -452
- package/package.json +3 -3
|
@@ -1,44 +1,44 @@
|
|
|
1
1
|
import { Activity as e, Activity as t, DurableClock as n, DurableClock as r, DurableQueue as i, DurableQueue as a, DurableRateLimiter as o, DurableRateLimiter as s, Workflow as c, Workflow as l } from "@effect/workflow";
|
|
2
|
-
import { Cause as u, Context as d,
|
|
2
|
+
import { Cause as u, Context as d, Cron as f, Duration as p, Effect as m, Either as h, Exit as ee, FiberRef as g, Option as te, Schedule as _, Schema as ne } from "effect";
|
|
3
3
|
//#region src/flowControl.ts
|
|
4
|
-
var
|
|
4
|
+
var re = (e) => e !== void 0 && (e.debounce !== void 0 || e.singleton !== void 0 || e.concurrency !== void 0 || e.throttle !== void 0 || e.rateLimit !== void 0 || e.batch !== void 0 || e.timeouts?.startMs !== void 0 || e.timeouts?.finishMs !== void 0), v = (e) => e?.cancelOn !== void 0 && e.cancelOn.length > 0, y = [
|
|
5
5
|
"debounce",
|
|
6
6
|
"batch",
|
|
7
7
|
"throttle",
|
|
8
8
|
"concurrency"
|
|
9
|
-
],
|
|
9
|
+
], b = (e) => {
|
|
10
10
|
if (e === void 0) return [];
|
|
11
11
|
let t = [];
|
|
12
12
|
return e.debounce !== void 0 && t.push("debounce"), e.batch !== void 0 && t.push("batch"), e.throttle !== void 0 && t.push("throttle"), e.concurrency !== void 0 && t.push("concurrency"), t;
|
|
13
|
-
},
|
|
13
|
+
}, x = (e, t, n) => {
|
|
14
14
|
let r;
|
|
15
15
|
try {
|
|
16
|
-
r =
|
|
16
|
+
r = p.toMillis(p.decode(e));
|
|
17
17
|
} catch {
|
|
18
18
|
throw Error(`workflow '${n}': ${t} is not a duration Effect can read (got ${JSON.stringify(e)}). Use a spelled-out unit ('15 minutes', '1 hour', '30 seconds'), a number of milliseconds, or a Duration — the abbreviated forms ('15m', '1h') are NOT accepted.`);
|
|
19
19
|
}
|
|
20
20
|
if (!Number.isFinite(r) || r <= 0) throw Error(`workflow '${n}': ${t} must be a positive finite duration, got ${String(e)}`);
|
|
21
21
|
return r;
|
|
22
|
-
},
|
|
22
|
+
}, S = (e, t, n) => {
|
|
23
23
|
if (!Number.isInteger(e) || e <= 0) throw Error(`workflow '${n}': ${t} must be a positive integer, got ${String(e)}`);
|
|
24
24
|
return e;
|
|
25
|
-
},
|
|
25
|
+
}, C = (e, t, n) => {
|
|
26
26
|
if (typeof e != "function") throw Error(`workflow '${n}': ${t} must be a function of the payload`);
|
|
27
27
|
return e;
|
|
28
|
-
},
|
|
28
|
+
}, w = (e) => {
|
|
29
29
|
let t = e?.ast?.propertySignatures;
|
|
30
30
|
if (!Array.isArray(t)) return !1;
|
|
31
31
|
let n = t.find((e) => e.name === "items");
|
|
32
32
|
return n !== void 0 && n.type?._tag === "TupleType";
|
|
33
|
-
},
|
|
33
|
+
}, T = (e) => {
|
|
34
34
|
let { workflowName: t, control: n } = e;
|
|
35
35
|
if (n === void 0) return;
|
|
36
36
|
let r = { workflowName: t };
|
|
37
37
|
if (n.debounce !== void 0) {
|
|
38
|
-
let e = n.debounce, i =
|
|
38
|
+
let e = n.debounce, i = x(e.period, "debounce.period", t), a = e.timeout === void 0 ? void 0 : x(e.timeout, "debounce.timeout", t);
|
|
39
39
|
if (a !== void 0 && a < i) throw Error(`workflow '${t}': debounce.timeout (${a}ms) is shorter than debounce.period (${i}ms), so the burst can never be collapsed — the run would be admitted before the quiet period could ever elapse. Raise the timeout or lower the period.`);
|
|
40
40
|
r.debounce = {
|
|
41
|
-
key:
|
|
41
|
+
key: C(e.key, "debounce.key", t),
|
|
42
42
|
periodMs: i,
|
|
43
43
|
...a === void 0 ? {} : { timeoutMs: a }
|
|
44
44
|
};
|
|
@@ -47,7 +47,7 @@ var v = (e) => e !== void 0 && (e.debounce !== void 0 || e.singleton !== void 0
|
|
|
47
47
|
let e = n.singleton;
|
|
48
48
|
if (e.mode !== "skip" && e.mode !== "cancel") throw Error(`workflow '${t}': singleton.mode must be 'skip' or 'cancel'. There is no default — 'skip' discards the incoming request and 'cancel' discards the running one, and choosing for you would silently throw away work either way.`);
|
|
49
49
|
r.singleton = {
|
|
50
|
-
key:
|
|
50
|
+
key: C(e.key, "singleton.key", t),
|
|
51
51
|
mode: e.mode
|
|
52
52
|
};
|
|
53
53
|
}
|
|
@@ -55,34 +55,34 @@ var v = (e) => e !== void 0 && (e.debounce !== void 0 || e.singleton !== void 0
|
|
|
55
55
|
let e = n.concurrency;
|
|
56
56
|
if (e.pool !== void 0 && (typeof e.pool != "string" || e.pool.trim() === "")) throw Error(`workflow '${t}': concurrency.pool must be a non-empty name — it is the identity other workflows declare to share this budget, and an empty one would silently merge every empty-named pool in the deployment into one.`);
|
|
57
57
|
r.concurrency = {
|
|
58
|
-
limit:
|
|
59
|
-
...e.key === void 0 ? {} : { key:
|
|
58
|
+
limit: S(e.limit, "concurrency.limit", t),
|
|
59
|
+
...e.key === void 0 ? {} : { key: C(e.key, "concurrency.key", t) },
|
|
60
60
|
...e.pool === void 0 ? {} : { pool: e.pool.trim() }
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
63
|
if (n.throttle !== void 0) {
|
|
64
64
|
let e = n.throttle;
|
|
65
65
|
r.throttle = {
|
|
66
|
-
limit:
|
|
67
|
-
periodMs:
|
|
68
|
-
...e.key === void 0 ? {} : { key:
|
|
66
|
+
limit: S(e.limit, "throttle.limit", t),
|
|
67
|
+
periodMs: x(e.period, "throttle.period", t),
|
|
68
|
+
...e.key === void 0 ? {} : { key: C(e.key, "throttle.key", t) }
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
71
|
if (n.rateLimit !== void 0) {
|
|
72
72
|
let e = n.rateLimit;
|
|
73
73
|
r.rateLimit = {
|
|
74
|
-
limit:
|
|
75
|
-
periodMs:
|
|
76
|
-
...e.key === void 0 ? {} : { key:
|
|
74
|
+
limit: S(e.limit, "rateLimit.limit", t),
|
|
75
|
+
periodMs: x(e.period, "rateLimit.period", t),
|
|
76
|
+
...e.key === void 0 ? {} : { key: C(e.key, "rateLimit.key", t) }
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
79
|
if (n.batch !== void 0) {
|
|
80
80
|
let i = n.batch;
|
|
81
|
-
if (e.payloadSchema !== void 0 && !
|
|
81
|
+
if (e.payloadSchema !== void 0 && !w(e.payloadSchema)) throw Error(`workflow '${t}': batch is declared, so the workflow's own \`payload\` must be the BATCH shape — \`Schema.Struct({ items: Schema.Array(Item) })\` — while callers start it with a SINGLE item. Declare the item's schema as \`batch.item\`. Checked here rather than at admission because a decode failure on the batching replica is a failure nobody is watching.`);
|
|
82
82
|
r.batch = {
|
|
83
|
-
...i.key === void 0 ? {} : { key:
|
|
84
|
-
maxSize:
|
|
85
|
-
timeoutMs:
|
|
83
|
+
...i.key === void 0 ? {} : { key: C(i.key, "batch.key", t) },
|
|
84
|
+
maxSize: S(i.maxSize, "batch.maxSize", t),
|
|
85
|
+
timeoutMs: x(i.timeout, "batch.timeout", t),
|
|
86
86
|
...i.item === void 0 ? {} : { item: i.item }
|
|
87
87
|
};
|
|
88
88
|
}
|
|
@@ -91,7 +91,7 @@ var v = (e) => e !== void 0 && (e.debounce !== void 0 || e.singleton !== void 0
|
|
|
91
91
|
r.priority = n.priority;
|
|
92
92
|
}
|
|
93
93
|
if (n.timeouts !== void 0) {
|
|
94
|
-
let e = n.timeouts, i = e.start === void 0 ? void 0 :
|
|
94
|
+
let e = n.timeouts, i = e.start === void 0 ? void 0 : x(e.start, "timeouts.start", t), a = e.finish === void 0 ? void 0 : x(e.finish, "timeouts.finish", t);
|
|
95
95
|
(i !== void 0 || a !== void 0) && (r.timeouts = {
|
|
96
96
|
...i === void 0 ? {} : { startMs: i },
|
|
97
97
|
...a === void 0 ? {} : { finishMs: a }
|
|
@@ -111,7 +111,7 @@ var v = (e) => e !== void 0 && (e.debounce !== void 0 || e.singleton !== void 0
|
|
|
111
111
|
if (typeof e?.event != "string" || e.event.length === 0) throw Error(`workflow '${t}': ${r}.event must be a non-empty event name, exactly as ctx.events.publish writes it.`);
|
|
112
112
|
if (e.schema === void 0 || typeof e.schema.ast != "object") throw Error(`workflow '${t}': ${r}.schema must be a Schema for the event payload. It is decoded before \`match\` runs, so the predicate reads the domain type rather than raw JSON.`);
|
|
113
113
|
if (typeof e.match != "function") throw Error(`workflow '${t}': ${r}.match is required. Omitting it would mean "cancel every live run of this workflow whenever '${e.event}' is published" — write \`match: () => true\` if that is what you mean, so it is visible in review.`);
|
|
114
|
-
let i = e.within === void 0 ? void 0 :
|
|
114
|
+
let i = e.within === void 0 ? void 0 : x(e.within, `${r}.within`, t);
|
|
115
115
|
return {
|
|
116
116
|
event: e.event,
|
|
117
117
|
schema: e.schema,
|
|
@@ -122,9 +122,9 @@ var v = (e) => e !== void 0 && (e.debounce !== void 0 || e.singleton !== void 0
|
|
|
122
122
|
});
|
|
123
123
|
e.length > 0 && (r.cancelOn = e);
|
|
124
124
|
}
|
|
125
|
-
if (r.timeouts?.startMs !== void 0 &&
|
|
125
|
+
if (r.timeouts?.startMs !== void 0 && b(r).length === 0) throw Error(`workflow '${t}': timeouts.start bounds how long a run may sit in the ADMISSION queue, but this workflow declares no control that can defer one (debounce, batch, throttle, concurrency). It would never fire. Use timeouts.finish to bound the run itself.`);
|
|
126
126
|
return r;
|
|
127
|
-
},
|
|
127
|
+
}, E = (e) => {
|
|
128
128
|
let t = /* @__PURE__ */ new Map();
|
|
129
129
|
for (let n of e) {
|
|
130
130
|
let e = n?.concurrency;
|
|
@@ -137,7 +137,46 @@ var v = (e) => e !== void 0 && (e.debounce !== void 0 || e.singleton !== void 0
|
|
|
137
137
|
else if (r.limit !== e.limit) throw Error(`concurrency pool '${e.pool}': '${n.workflowName}' declares limit ${e.limit}, but ${r.workflows.map((e) => `'${e}'`).join(", ")} declare${r.workflows.length === 1 ? "s" : ""} limit ${r.limit}. A pool is ONE budget — every member must declare the same number, or the enforced limit would depend on which workflow's start arrived first. Align the limits, or give the workflows different pool names if they were never meant to share.`);
|
|
138
138
|
else r.workflows.push(n.workflowName);
|
|
139
139
|
}
|
|
140
|
-
},
|
|
140
|
+
}, D = class extends d.Tag("@voltro/WorkflowRunRecorder")() {}, O = g.unsafeMake(void 0), k = g.unsafeMake(void 0), A = g.unsafeMake(void 0), j = g.unsafeMake([]), M = () => g.get(O), N = () => g.get(k), P = class extends d.Tag("@voltro/WorkflowStepInterceptor")() {}, F = 1440 * 6e4, ie = /* @__PURE__ */ new Set([
|
|
141
|
+
"skip",
|
|
142
|
+
"buffer",
|
|
143
|
+
"cancelOther"
|
|
144
|
+
]), ae = /* @__PURE__ */ new Set([
|
|
145
|
+
"skip",
|
|
146
|
+
"latest",
|
|
147
|
+
"all"
|
|
148
|
+
]), I = (e, t) => {
|
|
149
|
+
if (t === void 0) return;
|
|
150
|
+
if (typeof t.timezone != "string" || t.timezone.trim() === "") throw Error(`workflow '${e}': schedule.timezone is required (e.g. "UTC", "Europe/Berlin") — never server-local, which differs between your machine and the container.`);
|
|
151
|
+
let n = f.parse(t.cron, t.timezone);
|
|
152
|
+
if (h.isLeft(n)) throw Error(`workflow '${e}': schedule.cron "${t.cron}" (tz="${t.timezone}") is not a valid cron expression — ${String(n.left)}`);
|
|
153
|
+
let r = t.onOverlap ?? "skip";
|
|
154
|
+
if (!ie.has(r)) throw Error(`workflow '${e}': schedule.onOverlap must be 'skip' | 'buffer' | 'cancelOther', got ${JSON.stringify(r)}`);
|
|
155
|
+
let i = t.backfill ?? "skip";
|
|
156
|
+
if (!ae.has(i)) throw Error(`workflow '${e}': schedule.backfill must be 'skip' | 'latest' | 'all', got ${JSON.stringify(i)}`);
|
|
157
|
+
let a = F;
|
|
158
|
+
return t.maxRuntime !== void 0 && (a = L(t.maxRuntime, e)), {
|
|
159
|
+
cron: t.cron,
|
|
160
|
+
timezone: t.timezone,
|
|
161
|
+
...t.payload === void 0 ? {} : { payload: t.payload },
|
|
162
|
+
onOverlap: r,
|
|
163
|
+
backfill: i,
|
|
164
|
+
maxRuntimeMs: a
|
|
165
|
+
};
|
|
166
|
+
}, L = (e, t) => {
|
|
167
|
+
let n;
|
|
168
|
+
try {
|
|
169
|
+
n = p.toMillis(p.decode(e));
|
|
170
|
+
} catch {
|
|
171
|
+
throw Error(`workflow '${t}': schedule.maxRuntime is not a duration Effect can read (got ${JSON.stringify(e)}). Use '2 hours', a number of milliseconds, or a Duration.`);
|
|
172
|
+
}
|
|
173
|
+
if (!Number.isFinite(n) || n <= 0) throw Error(`workflow '${t}': schedule.maxRuntime must be a positive finite duration`);
|
|
174
|
+
return n;
|
|
175
|
+
}, R = "__voltroWorkflowSchedule", z = (e) => {
|
|
176
|
+
if (typeof e != "object" || !e) return;
|
|
177
|
+
let t = e[R];
|
|
178
|
+
return t === void 0 ? void 0 : t;
|
|
179
|
+
}, B = (e) => {
|
|
141
180
|
let t = /* @__PURE__ */ new Map();
|
|
142
181
|
for (let n of e) t.set(n.stepName, (t.get(n.stepName) ?? 0) + 1);
|
|
143
182
|
let n = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Set(), i = (e) => r.has(e) ? !1 : (r.add(e), !0);
|
|
@@ -166,79 +205,79 @@ var v = (e) => e !== void 0 && (e.debounce !== void 0 || e.singleton !== void 0
|
|
|
166
205
|
return e;
|
|
167
206
|
}
|
|
168
207
|
};
|
|
169
|
-
},
|
|
208
|
+
}, V = g.unsafeMake(void 0), H = (e) => ({
|
|
170
209
|
kind: e.kind,
|
|
171
210
|
stepName: e.stepName,
|
|
172
211
|
recorded: e.recorded,
|
|
173
212
|
reached: e.reached,
|
|
174
213
|
detail: e.detail
|
|
175
|
-
}),
|
|
214
|
+
}), U = (e) => m.gen(function* () {
|
|
176
215
|
if (e.length === 0) return;
|
|
177
|
-
let t = yield*
|
|
216
|
+
let t = yield* g.get(O), n = yield* m.serviceOption(D);
|
|
178
217
|
if (t === void 0 || n._tag !== "Some") return;
|
|
179
218
|
let r = n.value;
|
|
180
|
-
for (let n of e) yield*
|
|
219
|
+
for (let n of e) yield* m.promise(() => r.recordEvent({
|
|
181
220
|
runId: t,
|
|
182
221
|
eventType: "nondeterminism-suspected",
|
|
183
|
-
payload:
|
|
222
|
+
payload: H(n)
|
|
184
223
|
}).then(() => void 0).catch(() => void 0));
|
|
185
|
-
}),
|
|
224
|
+
}), W = (e) => {
|
|
186
225
|
if (typeof e != "object" || !e) return;
|
|
187
226
|
let t = e.retryAfterMillis;
|
|
188
|
-
if (typeof t == "number" && Number.isFinite(t) && t >= 0) return
|
|
227
|
+
if (typeof t == "number" && Number.isFinite(t) && t >= 0) return p.millis(t);
|
|
189
228
|
let n = e.retryAfter;
|
|
190
|
-
if (typeof n == "number" && Number.isFinite(n) && n >= 0) return
|
|
191
|
-
},
|
|
229
|
+
if (typeof n == "number" && Number.isFinite(n) && n >= 0) return p.seconds(n);
|
|
230
|
+
}, G = (e) => p.decode(e), K = (e) => typeof e == "object" && e && "_tag" in e ? String(e._tag) : void 0, q = (e) => e !== void 0 && (e.maxAttempts ?? 3) > 1, J = (e) => {
|
|
192
231
|
if (e.retryable) return e.retryable;
|
|
193
232
|
if (e.retryableErrors && e.retryableErrors.length > 0) {
|
|
194
233
|
let t = new Set(e.retryableErrors);
|
|
195
234
|
return (e) => {
|
|
196
|
-
let n =
|
|
235
|
+
let n = K(e);
|
|
197
236
|
return n !== void 0 && t.has(n);
|
|
198
237
|
};
|
|
199
238
|
}
|
|
200
239
|
return () => !0;
|
|
201
|
-
},
|
|
202
|
-
let t = e.baseDelay ?? "200 millis", n = e.strategy ?? "exponential", r = e.maxAttempts ?? 3, i = n === "linear" ?
|
|
240
|
+
}, oe = (e) => {
|
|
241
|
+
let t = e.baseDelay ?? "200 millis", n = e.strategy ?? "exponential", r = e.maxAttempts ?? 3, i = n === "linear" ? _.linear(G(e.step ?? t)) : n === "fixed" ? _.spaced(G(t)) : _.exponential(G(t), e.factor ?? 2);
|
|
203
242
|
if (e.maxDelay !== void 0) {
|
|
204
|
-
let t =
|
|
205
|
-
i =
|
|
243
|
+
let t = G(e.maxDelay);
|
|
244
|
+
i = _.modifyDelay(i, (e, n) => p.min(n, t));
|
|
206
245
|
}
|
|
207
|
-
(e.jitter ?? !0) === !0 && (i =
|
|
208
|
-
let a =
|
|
209
|
-
return e.maxElapsed !== void 0 && (a =
|
|
210
|
-
},
|
|
211
|
-
let n =
|
|
212
|
-
return e.maxDelay !== void 0 && (i = Math.min(i,
|
|
213
|
-
},
|
|
214
|
-
let i = t.maxAttempts ?? 3, a = t.maxElapsed === void 0 ? Infinity :
|
|
246
|
+
(e.jitter ?? !0) === !0 && (i = _.jittered(i));
|
|
247
|
+
let a = _.intersect(i, _.recurs(Math.max(0, r - 1)));
|
|
248
|
+
return e.maxElapsed !== void 0 && (a = _.upTo(G(e.maxElapsed))(a)), a;
|
|
249
|
+
}, se = (e, t) => {
|
|
250
|
+
let n = p.toMillis(G(e.baseDelay ?? "200 millis")), r = e.strategy ?? "exponential", i = r === "fixed" ? n : r === "linear" ? n + p.toMillis(G(e.step ?? e.baseDelay ?? "200 millis")) * (t - 1) : n * (e.factor ?? 2) ** (t - 1);
|
|
251
|
+
return e.maxDelay !== void 0 && (i = Math.min(i, p.toMillis(G(e.maxDelay)))), (e.jitter ?? !0) === !0 && (i *= Math.random()), i;
|
|
252
|
+
}, ce = (e, t, n, r) => m.gen(function* () {
|
|
253
|
+
let i = t.maxAttempts ?? 3, a = t.maxElapsed === void 0 ? Infinity : p.toMillis(G(t.maxElapsed)), o = yield* m.sync(() => Date.now()), s = 0;
|
|
215
254
|
for (;;) {
|
|
216
|
-
let c = yield*
|
|
217
|
-
if (
|
|
218
|
-
let l = c.cause, d = u.failureOption(l),
|
|
255
|
+
let c = yield* m.exit(e);
|
|
256
|
+
if (ee.isSuccess(c)) return c.value;
|
|
257
|
+
let l = c.cause, d = u.failureOption(l), f = te.isSome(d) ? d.value : u.squash(l);
|
|
219
258
|
s += 1;
|
|
220
|
-
let
|
|
221
|
-
if (s >= i || !n(
|
|
222
|
-
r?.(
|
|
223
|
-
let
|
|
224
|
-
yield*
|
|
259
|
+
let h = (yield* m.sync(() => Date.now())) - o;
|
|
260
|
+
if (s >= i || !n(f) || h >= a) return yield* m.failCause(l);
|
|
261
|
+
r?.(f);
|
|
262
|
+
let g = W(f), _ = g === void 0 ? se(t, s) : p.toMillis(g);
|
|
263
|
+
yield* m.sleep(p.millis(_));
|
|
225
264
|
}
|
|
226
|
-
}),
|
|
227
|
-
if (!
|
|
228
|
-
let r =
|
|
229
|
-
if (t.respectRetryAfter === !0) return
|
|
230
|
-
let i =
|
|
265
|
+
}), le = (e, t, n) => {
|
|
266
|
+
if (!q(t)) return e;
|
|
267
|
+
let r = J(t);
|
|
268
|
+
if (t.respectRetryAfter === !0) return ce(e, t, r, n);
|
|
269
|
+
let i = oe(t), a = n ? m.tapError(e, (e) => m.sync(() => {
|
|
231
270
|
r(e) && n(e);
|
|
232
271
|
})) : e;
|
|
233
|
-
return
|
|
272
|
+
return m.retry(a, {
|
|
234
273
|
schedule: i,
|
|
235
274
|
while: r
|
|
236
275
|
});
|
|
237
|
-
}, Y = Symbol.for("voltro.workflow.workerLayer"), X = Symbol.for("voltro.workflow.version"), Z = "__voltroWorkflowMessages", Q = "__voltroWorkflowFlowControl",
|
|
276
|
+
}, Y = Symbol.for("voltro.workflow.workerLayer"), X = Symbol.for("voltro.workflow.version"), Z = "__voltroWorkflowMessages", Q = "__voltroWorkflowFlowControl", ue = (e) => typeof e == "object" && !!e && e[Y] === !0, de = (e) => typeof e == "object" && e && e[X] !== void 0 ? e[X] : {
|
|
238
277
|
version: "1",
|
|
239
278
|
compatibleWith: ["1"],
|
|
240
279
|
patches: []
|
|
241
|
-
},
|
|
280
|
+
}, fe = ((e) => {
|
|
242
281
|
let t = c.make(e), n = e.suspendOnFailure === !0 ? t.annotate(c.SuspendOnFailure, !0) : t, r = String(e.version ?? "1"), i = {
|
|
243
282
|
version: r,
|
|
244
283
|
compatibleWith: (e.compatibleWith ?? [r]).map(String),
|
|
@@ -254,30 +293,35 @@ var v = (e) => e !== void 0 && (e.debounce !== void 0 || e.singleton !== void 0
|
|
|
254
293
|
value: a,
|
|
255
294
|
enumerable: !1
|
|
256
295
|
});
|
|
257
|
-
let o =
|
|
296
|
+
let o = T({
|
|
258
297
|
workflowName: e.name,
|
|
259
298
|
payloadSchema: e.payload,
|
|
260
299
|
control: e
|
|
261
300
|
});
|
|
262
|
-
|
|
301
|
+
o !== void 0 && Object.defineProperty(n, Q, {
|
|
263
302
|
value: o,
|
|
264
303
|
enumerable: !1
|
|
304
|
+
});
|
|
305
|
+
let s = I(e.name, e.schedule);
|
|
306
|
+
return s !== void 0 && Object.defineProperty(n, R, {
|
|
307
|
+
value: s,
|
|
308
|
+
enumerable: !1
|
|
265
309
|
}), n;
|
|
266
|
-
}),
|
|
310
|
+
}), pe = (e) => {
|
|
267
311
|
if (typeof e != "object" || !e) return;
|
|
268
312
|
let t = e[Q];
|
|
269
313
|
return t === void 0 ? void 0 : t;
|
|
270
|
-
},
|
|
314
|
+
}, me = i.make, he = i.process, ge = ((...e) => {
|
|
271
315
|
let t = i.worker(...e);
|
|
272
316
|
return Object.defineProperty(t, Y, {
|
|
273
317
|
value: !0,
|
|
274
318
|
enumerable: !1
|
|
275
319
|
}), t;
|
|
276
|
-
}),
|
|
277
|
-
let t = yield*
|
|
320
|
+
}), _e = o.rateLimit, $ = (e) => m.gen(function* () {
|
|
321
|
+
let t = yield* O, r = yield* m.serviceOption(D), i = p.toMillis(p.decode(e.duration));
|
|
278
322
|
if (t !== void 0 && r._tag === "Some") {
|
|
279
|
-
let a = r.value, o = new Date(Date.now() + i).toISOString(), s = yield*
|
|
280
|
-
yield*
|
|
323
|
+
let a = r.value, o = new Date(Date.now() + i).toISOString(), s = yield* A;
|
|
324
|
+
yield* m.promise(() => a.recordEvent({
|
|
281
325
|
runId: t,
|
|
282
326
|
eventType: "timer-set",
|
|
283
327
|
payload: {
|
|
@@ -288,7 +332,7 @@ var v = (e) => e !== void 0 && (e.debounce !== void 0 || e.singleton !== void 0
|
|
|
288
332
|
}
|
|
289
333
|
}).catch(() => {}));
|
|
290
334
|
let c = Date.now();
|
|
291
|
-
yield* n.sleep(e), yield*
|
|
335
|
+
yield* n.sleep(e), yield* m.promise(() => a.recordEvent({
|
|
292
336
|
runId: t,
|
|
293
337
|
eventType: "timer-fired",
|
|
294
338
|
payload: {
|
|
@@ -300,18 +344,18 @@ var v = (e) => e !== void 0 && (e.debounce !== void 0 || e.singleton !== void 0
|
|
|
300
344
|
return;
|
|
301
345
|
}
|
|
302
346
|
yield* n.sleep(e);
|
|
303
|
-
}),
|
|
347
|
+
}), ve = (t) => m.gen(function* () {
|
|
304
348
|
let n = (yield* e.make({
|
|
305
349
|
name: `sleepUntil:${t.name}`,
|
|
306
|
-
success:
|
|
307
|
-
execute:
|
|
350
|
+
success: ne.Number,
|
|
351
|
+
execute: m.sync(() => t.until instanceof Date ? t.until.getTime() : t.until)
|
|
308
352
|
})) - Date.now();
|
|
309
353
|
n <= 0 || (yield* $({
|
|
310
354
|
name: t.name,
|
|
311
355
|
duration: `${n} millis`
|
|
312
356
|
}));
|
|
313
|
-
}),
|
|
314
|
-
let a = yield*
|
|
357
|
+
}), ye = (t, n, r, i) => m.gen(function* () {
|
|
358
|
+
let a = yield* O, o = yield* e.CurrentAttempt, s = yield* m.serviceOption(D), c = yield* m.serviceOption(P), l = c._tag === "Some" ? c.value.interceptor(n, {
|
|
315
359
|
runId: a ?? "unrecorded",
|
|
316
360
|
stepName: t,
|
|
317
361
|
attempt: o
|
|
@@ -320,7 +364,7 @@ var v = (e) => e !== void 0 && (e.debounce !== void 0 || e.singleton !== void 0
|
|
|
320
364
|
f = s.value;
|
|
321
365
|
let e = i === void 0 ? void 0 : (({ retryable: e, ...t }) => t)(i);
|
|
322
366
|
try {
|
|
323
|
-
d = yield*
|
|
367
|
+
d = yield* m.promise(() => f.startStep({
|
|
324
368
|
runId: a,
|
|
325
369
|
stepName: t,
|
|
326
370
|
attempt: o,
|
|
@@ -331,16 +375,16 @@ var v = (e) => e !== void 0 && (e.debounce !== void 0 || e.singleton !== void 0
|
|
|
331
375
|
d = void 0;
|
|
332
376
|
}
|
|
333
377
|
}
|
|
334
|
-
let
|
|
335
|
-
return yield*
|
|
378
|
+
let p = Date.now();
|
|
379
|
+
return yield* le(l, i).pipe(m.tap((e) => d && f ? m.promise(async () => {
|
|
336
380
|
try {
|
|
337
381
|
await f.endStepSuccess({
|
|
338
382
|
stepRecordId: d,
|
|
339
383
|
output: e,
|
|
340
|
-
durationMs: Date.now() -
|
|
384
|
+
durationMs: Date.now() - p
|
|
341
385
|
});
|
|
342
386
|
} catch {}
|
|
343
|
-
}) :
|
|
387
|
+
}) : m.void), m.tapErrorCause((e) => d && f && !u.isInterruptedOnly(e) ? m.promise(async () => {
|
|
344
388
|
try {
|
|
345
389
|
let t = u.failureOption(e), n = Array.from(u.failures(e)), r = Array.from(u.defects(e)), i = t._tag === "Some" ? t.value : r[0] ?? u.squash(e), a = i?.message ?? String(i), o = i && typeof i == "object" && "_tag" in i ? String(i._tag) : null;
|
|
346
390
|
await f.endStepFailure({
|
|
@@ -352,27 +396,27 @@ var v = (e) => e !== void 0 && (e.debounce !== void 0 || e.singleton !== void 0
|
|
|
352
396
|
failures: n,
|
|
353
397
|
defects: r
|
|
354
398
|
},
|
|
355
|
-
durationMs: Date.now() -
|
|
399
|
+
durationMs: Date.now() - p
|
|
356
400
|
});
|
|
357
401
|
} catch {}
|
|
358
|
-
}) :
|
|
359
|
-
}),
|
|
360
|
-
let t = yield*
|
|
402
|
+
}) : m.void));
|
|
403
|
+
}), be = (e) => m.gen(function* () {
|
|
404
|
+
let t = yield* g.get(V);
|
|
361
405
|
if (t === void 0) return;
|
|
362
406
|
let n = t.reach(e);
|
|
363
|
-
n !== void 0 && (yield*
|
|
364
|
-
}),
|
|
407
|
+
n !== void 0 && (yield* U([n]));
|
|
408
|
+
}), xe = (e, t) => {
|
|
365
409
|
let n = e;
|
|
366
410
|
return {
|
|
367
411
|
...e,
|
|
368
|
-
commit: () =>
|
|
412
|
+
commit: () => m.zipRight(be(t), n)
|
|
369
413
|
};
|
|
370
|
-
},
|
|
414
|
+
}, Se = (t) => {
|
|
371
415
|
let { input: n, retry: r, ...i } = t;
|
|
372
|
-
return
|
|
416
|
+
return xe(e.make({
|
|
373
417
|
...i,
|
|
374
|
-
execute:
|
|
418
|
+
execute: ye(i.name, i.execute, n, r)
|
|
375
419
|
}), i.name);
|
|
376
|
-
},
|
|
420
|
+
}, Ce = (e) => m.map(g.get(j), (t) => t.includes(e)), we = c.withCompensation, Te = e.idempotencyKey;
|
|
377
421
|
//#endregion
|
|
378
|
-
export {
|
|
422
|
+
export { z as A, y as B, l as C, U as D, H as E, A as F, T as G, b as H, D as I, P as L, k as M, j as N, F as O, O as P, N as R, fe as S, B as T, v as U, E as V, re as W, ve as _, r as a, t as b, s as c, ue as d, Ce as f, $ as g, _e as h, Y as i, I as j, R as k, pe as l, ge as m, Z as n, me as o, he as p, X as r, a as s, Q as t, de as u, Se as v, V as w, we as x, Te as y, M as z };
|
package/dist/primitives.d.ts
CHANGED
|
@@ -41,6 +41,9 @@ declare type DecodedPayload<P> = P extends Schema.Struct.Fields ? Schema.Struct.
|
|
|
41
41
|
readonly Type: infer T;
|
|
42
42
|
} ? T : never;
|
|
43
43
|
|
|
44
|
+
/** 24 h — see {@link WorkflowScheduleDeclaration.maxRuntime}. */
|
|
45
|
+
export declare const DEFAULT_WORKFLOW_SCHEDULE_MAX_RUNTIME_MS: number;
|
|
46
|
+
|
|
44
47
|
/**
|
|
45
48
|
* Controls that can DEFER a start rather than answer it immediately.
|
|
46
49
|
*
|
|
@@ -77,6 +80,9 @@ export declare type FlowDuration = Duration.DurationInput;
|
|
|
77
80
|
* the path it took before this feature existed. */
|
|
78
81
|
export declare const getWorkflowFlowControl: (value: unknown) => ResolvedFlowControl | undefined;
|
|
79
82
|
|
|
83
|
+
/** The resolved schedule attached to a workflow definition, or undefined. */
|
|
84
|
+
export declare const getWorkflowSchedule: (value: unknown) => ResolvedWorkflowSchedule | undefined;
|
|
85
|
+
|
|
80
86
|
export declare const getWorkflowVersionMetadata: (value: unknown) => WorkflowVersionMetadata;
|
|
81
87
|
|
|
82
88
|
/** `true` when this workflow declares at least one `cancelOn` entry — i.e. when
|
|
@@ -303,6 +309,18 @@ export declare interface ResolvedFlowControl {
|
|
|
303
309
|
readonly cancelOn?: ReadonlyArray<ResolvedCancelOn>;
|
|
304
310
|
}
|
|
305
311
|
|
|
312
|
+
/** The declaration with defaults filled in — what the CLI lowering reads. */
|
|
313
|
+
export declare interface ResolvedWorkflowSchedule {
|
|
314
|
+
readonly cron: string;
|
|
315
|
+
readonly timezone: string;
|
|
316
|
+
readonly payload?: unknown | ((fire: {
|
|
317
|
+
readonly scheduledAt: Date;
|
|
318
|
+
}) => unknown | Promise<unknown>);
|
|
319
|
+
readonly onOverlap: WorkflowScheduleOverlap;
|
|
320
|
+
readonly backfill: 'skip' | 'latest' | 'all';
|
|
321
|
+
readonly maxRuntimeMs: number;
|
|
322
|
+
}
|
|
323
|
+
|
|
306
324
|
/**
|
|
307
325
|
* Durable sleep — wake time journaled in the cluster. Wrapper around
|
|
308
326
|
* `DurableClock.sleep` that emits `timer-set` / `timer-fired` events
|
|
@@ -462,6 +480,14 @@ export declare interface ValidateFlowControlInput {
|
|
|
462
480
|
readonly control: (WorkflowFlowControl<never> & WorkflowCancelOnDeclared) | undefined;
|
|
463
481
|
}
|
|
464
482
|
|
|
483
|
+
/**
|
|
484
|
+
* Validate at DEFINITION time — module load, i.e. boot — with a message naming
|
|
485
|
+
* the workflow and the field. Same posture as `validateFlowControl` and
|
|
486
|
+
* `defineSchedule`: a cron typo that surfaces as "never fired" three days in
|
|
487
|
+
* is the failure class this repo keeps paying for.
|
|
488
|
+
*/
|
|
489
|
+
export declare const validateWorkflowSchedule: (workflowName: string, declaration: WorkflowScheduleDeclaration<never> | undefined) => ResolvedWorkflowSchedule | undefined;
|
|
490
|
+
|
|
465
491
|
/**
|
|
466
492
|
* Add compensating (rollback) logic to a top-level effect in a workflow
|
|
467
493
|
* body. The finalizer runs if the WHOLE workflow later fails — use it for
|
|
@@ -848,6 +874,12 @@ declare type WorkflowFn = <const Name extends string, Payload extends WorkflowPa
|
|
|
848
874
|
* type comes from its OWN `schema` — see {@link WorkflowCancelOnList} for
|
|
849
875
|
* why that needs a tuple type parameter rather than a plain array. */
|
|
850
876
|
readonly cancelOn?: WorkflowCancelOnList<Cancels, DecodedPayload<Payload>>;
|
|
877
|
+
/** Run this workflow on a cron — the workflow-side spelling of a
|
|
878
|
+
* `defineSchedule({ workflow })` target, with Temporal's overlap
|
|
879
|
+
* vocabulary (`skip` / `buffer` / `cancelOther`). Lowered by the CLI into
|
|
880
|
+
* a real schedule named `workflow:<name>`; see
|
|
881
|
+
* {@link WorkflowScheduleDeclaration}. */
|
|
882
|
+
readonly schedule?: WorkflowScheduleDeclaration<DecodedPayload<Payload>>;
|
|
851
883
|
} & Omit<WorkflowBaseOptions, 'name' | 'payload' | 'idempotencyKey' | 'success' | 'error'> & WorkflowVersionOptions & {
|
|
852
884
|
readonly messages?: Messages;
|
|
853
885
|
} & WorkflowFlowControl<DecodedPayload<Payload>>) => workflowModule.Workflow<Name, PayloadSchemaOf<Payload>, Success, Error> & WorkflowMessagesCarrier<NormaliseWorkflowMessages<Messages>>;
|
|
@@ -907,6 +939,68 @@ export declare interface WorkflowRateLimit<Payload> {
|
|
|
907
939
|
readonly key?: (payload: Payload) => string;
|
|
908
940
|
}
|
|
909
941
|
|
|
942
|
+
export declare interface WorkflowScheduleDeclaration<Payload> {
|
|
943
|
+
/** Standard cron expression — 5-field or 6-field (leading seconds). */
|
|
944
|
+
readonly cron: string;
|
|
945
|
+
/** IANA timezone the expression is interpreted in. REQUIRED, like
|
|
946
|
+
* `defineSchedule` — server-local time in a container is a bug factory. */
|
|
947
|
+
readonly timezone: string;
|
|
948
|
+
/**
|
|
949
|
+
* The payload each firing starts the workflow with. A value, or a function
|
|
950
|
+
* of the firing (`({ scheduledAt }) => …`) for payloads that carry the slot
|
|
951
|
+
* — a backfilled firing then computes against ITS instant, not "now".
|
|
952
|
+
* Omitted ⇒ `{}`, which only typechecks for workflows whose payload has no
|
|
953
|
+
* required fields; a mismatch fails the start with `WorkflowPayloadError`
|
|
954
|
+
* naming the missing fields, same as any other start.
|
|
955
|
+
*/
|
|
956
|
+
readonly payload?: Payload | ((fire: {
|
|
957
|
+
readonly scheduledAt: Date;
|
|
958
|
+
}) => Payload | Promise<Payload>);
|
|
959
|
+
/** Default `'skip'` — Temporal's default too, and the only answer that is
|
|
960
|
+
* safe for every job shape. */
|
|
961
|
+
readonly onOverlap?: WorkflowScheduleOverlap;
|
|
962
|
+
/** Boot catch-up policy for firings missed during downtime — the scheduler's
|
|
963
|
+
* own vocabulary, unchanged. Default `'skip'`. For a RANGE older than boot
|
|
964
|
+
* backfill reaches, use `voltro schedule backfill`. */
|
|
965
|
+
readonly backfill?: 'skip' | 'latest' | 'all';
|
|
966
|
+
/**
|
|
967
|
+
* Watchdog on ONE firing — which, for a workflow schedule, INCLUDES the
|
|
968
|
+
* awaited run (that await is what makes `onOverlap` bind on the run's
|
|
969
|
+
* duration). Default 24 hours rather than `defineSchedule`'s 30 minutes,
|
|
970
|
+
* because a durable run legitimately outlives a handler; set it above your
|
|
971
|
+
* slowest expected run. Past it the FIRING records `failed`/timeout and
|
|
972
|
+
* releases the overlap guard — the workflow run itself is NOT cancelled
|
|
973
|
+
* (declare flow-control `timeouts.finish` to bound the run).
|
|
974
|
+
*/
|
|
975
|
+
readonly maxRuntime?: FlowDuration;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
/**
|
|
979
|
+
* What happens when a firing arrives while the RUN from the previous firing is
|
|
980
|
+
* still going — Temporal Schedules' overlap vocabulary, applied to the
|
|
981
|
+
* workflow run (not merely to the handler invocation):
|
|
982
|
+
*
|
|
983
|
+
* `'skip'` — the new firing stands down; recorded as `skipped`.
|
|
984
|
+
* `'buffer'` — the new firing waits and runs after the previous one
|
|
985
|
+
* finishes; firings serialize, none is lost.
|
|
986
|
+
* `'cancelOther'` — the new firing CANCELS the still-running previous run
|
|
987
|
+
* and starts fresh — for "recompute the latest state"
|
|
988
|
+
* jobs where the old run's partial work is worthless.
|
|
989
|
+
*
|
|
990
|
+
* The first two lower into the scheduler's own `onOverlap: 'skip' | 'queue'`;
|
|
991
|
+
* what makes them bind on the RUN's duration is that the synthesised handler
|
|
992
|
+
* AWAITS the workflow run to completion, so the schedule-run row is `running`
|
|
993
|
+
* for exactly as long as the workflow is.
|
|
994
|
+
*/
|
|
995
|
+
export declare type WorkflowScheduleOverlap = 'skip' | 'buffer' | 'cancelOther';
|
|
996
|
+
|
|
997
|
+
/** Attached via `defineProperty` like the flow-control carrier — the value's
|
|
998
|
+
* TYPE does not know about it, and the web bundle never reads it. A STRING
|
|
999
|
+
* property rather than a symbol for the same reason as
|
|
1000
|
+
* `WorkflowFlowControlProperty`: the CLI's discovery reads it off a module
|
|
1001
|
+
* that crossed a bundler boundary, and symbols do not survive every one. */
|
|
1002
|
+
export declare const WorkflowScheduleProperty = "__voltroWorkflowSchedule";
|
|
1003
|
+
|
|
910
1004
|
/**
|
|
911
1005
|
* At most one run per key. The newcomer either stands down or evicts.
|
|
912
1006
|
*
|
package/dist/primitives.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { A as e, B as t, C as n, D as r, E as i, G as a, H as o, O as s, S as c, T as l, U as u, V as d, W as f, _ as p, a as m, b as h, c as g, d as _, f as v, g as y, h as b, i as x, j as S, k as C, l as w, m as T, n as E, o as D, p as O, r as k, s as A, t as j, u as M, v as N, w as P, x as F, y as I } from "./primitives-CyQZeT1t.js";
|
|
2
|
+
export { P as CurrentWorkflowReplayShape, s as DEFAULT_WORKFLOW_SCHEDULE_MAX_RUNTIME_MS, t as DEFERRING_CONTROLS, j as WorkflowFlowControlProperty, E as WorkflowMessagesProperty, C as WorkflowScheduleProperty, k as WorkflowVersionTypeId, x as WorkflowWorkerLayerTypeId, d as assertConsistentPools, o as deferringControlsOf, m as durableClock, D as durableQueue, A as durableQueueModule, g as durableRateLimiterModule, w as getWorkflowFlowControl, e as getWorkflowSchedule, M as getWorkflowVersionMetadata, u as hasCancelOn, f as hasFlowControl, _ as isWorkflowWorkerLayer, l as makeReplayShapeTracker, i as nondeterminismEventPayload, v as patch, O as processQueue, T as queueWorker, b as rateLimit, r as reportNondeterminism, y as sleep, p as sleepUntil, N as step, I as stepIdempotencyKey, h as stepModule, a as validateFlowControl, S as validateWorkflowSchedule, F as withCompensation, c as workflow, n as workflowModule };
|