@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.
@@ -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, Duration as f, Effect as p, Exit as m, FiberRef as h, Option as ee, Schedule as g, Schema as _ } from "effect";
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 v = (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), y = (e) => e?.cancelOn !== void 0 && e.cancelOn.length > 0, b = [
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
- ], x = (e) => {
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
- }, S = (e, t, n) => {
13
+ }, x = (e, t, n) => {
14
14
  let r;
15
15
  try {
16
- r = f.toMillis(f.decode(e));
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
- }, C = (e, t, n) => {
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
- }, w = (e, t, n) => {
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
- }, T = (e) => {
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
- }, E = (e) => {
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 = S(e.period, "debounce.period", t), a = e.timeout === void 0 ? void 0 : S(e.timeout, "debounce.timeout", t);
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: w(e.key, "debounce.key", t),
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: w(e.key, "singleton.key", t),
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: C(e.limit, "concurrency.limit", t),
59
- ...e.key === void 0 ? {} : { key: w(e.key, "concurrency.key", t) },
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: C(e.limit, "throttle.limit", t),
67
- periodMs: S(e.period, "throttle.period", t),
68
- ...e.key === void 0 ? {} : { key: w(e.key, "throttle.key", t) }
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: C(e.limit, "rateLimit.limit", t),
75
- periodMs: S(e.period, "rateLimit.period", t),
76
- ...e.key === void 0 ? {} : { key: w(e.key, "rateLimit.key", t) }
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 && !T(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.`);
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: w(i.key, "batch.key", t) },
84
- maxSize: C(i.maxSize, "batch.maxSize", t),
85
- timeoutMs: S(i.timeout, "batch.timeout", t),
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 : S(e.start, "timeouts.start", t), a = e.finish === void 0 ? void 0 : S(e.finish, "timeouts.finish", t);
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 : S(e.within, `${r}.within`, t);
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 && x(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.`);
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
- }, D = (e) => {
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
- }, O = class extends d.Tag("@voltro/WorkflowRunRecorder")() {}, k = h.unsafeMake(void 0), A = h.unsafeMake(void 0), j = h.unsafeMake(void 0), M = h.unsafeMake([]), N = () => h.get(k), P = () => h.get(A), F = class extends d.Tag("@voltro/WorkflowStepInterceptor")() {}, I = (e) => {
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
- }, L = h.unsafeMake(void 0), R = (e) => ({
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
- }), z = (e) => p.gen(function* () {
214
+ }), U = (e) => m.gen(function* () {
176
215
  if (e.length === 0) return;
177
- let t = yield* h.get(k), n = yield* p.serviceOption(O);
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* p.promise(() => r.recordEvent({
219
+ for (let n of e) yield* m.promise(() => r.recordEvent({
181
220
  runId: t,
182
221
  eventType: "nondeterminism-suspected",
183
- payload: R(n)
222
+ payload: H(n)
184
223
  }).then(() => void 0).catch(() => void 0));
185
- }), B = (e) => {
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 f.millis(t);
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 f.seconds(n);
191
- }, V = (e) => f.decode(e), H = (e) => typeof e == "object" && e && "_tag" in e ? String(e._tag) : void 0, U = (e) => e !== void 0 && (e.maxAttempts ?? 3) > 1, W = (e) => {
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 = H(e);
235
+ let n = K(e);
197
236
  return n !== void 0 && t.has(n);
198
237
  };
199
238
  }
200
239
  return () => !0;
201
- }, G = (e) => {
202
- let t = e.baseDelay ?? "200 millis", n = e.strategy ?? "exponential", r = e.maxAttempts ?? 3, i = n === "linear" ? g.linear(V(e.step ?? t)) : n === "fixed" ? g.spaced(V(t)) : g.exponential(V(t), e.factor ?? 2);
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 = V(e.maxDelay);
205
- i = g.modifyDelay(i, (e, n) => f.min(n, t));
243
+ let t = G(e.maxDelay);
244
+ i = _.modifyDelay(i, (e, n) => p.min(n, t));
206
245
  }
207
- (e.jitter ?? !0) === !0 && (i = g.jittered(i));
208
- let a = g.intersect(i, g.recurs(Math.max(0, r - 1)));
209
- return e.maxElapsed !== void 0 && (a = g.upTo(V(e.maxElapsed))(a)), a;
210
- }, K = (e, t) => {
211
- let n = f.toMillis(V(e.baseDelay ?? "200 millis")), r = e.strategy ?? "exponential", i = r === "fixed" ? n : r === "linear" ? n + f.toMillis(V(e.step ?? e.baseDelay ?? "200 millis")) * (t - 1) : n * (e.factor ?? 2) ** (t - 1);
212
- return e.maxDelay !== void 0 && (i = Math.min(i, f.toMillis(V(e.maxDelay)))), (e.jitter ?? !0) === !0 && (i *= Math.random()), i;
213
- }, q = (e, t, n, r) => p.gen(function* () {
214
- let i = t.maxAttempts ?? 3, a = t.maxElapsed === void 0 ? Infinity : f.toMillis(V(t.maxElapsed)), o = yield* p.sync(() => Date.now()), s = 0;
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* p.exit(e);
217
- if (m.isSuccess(c)) return c.value;
218
- let l = c.cause, d = u.failureOption(l), h = ee.isSome(d) ? d.value : u.squash(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 g = (yield* p.sync(() => Date.now())) - o;
221
- if (s >= i || !n(h) || g >= a) return yield* p.failCause(l);
222
- r?.(h);
223
- let _ = B(h), v = _ === void 0 ? K(t, s) : f.toMillis(_);
224
- yield* p.sleep(f.millis(v));
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
- }), J = (e, t, n) => {
227
- if (!U(t)) return e;
228
- let r = W(t);
229
- if (t.respectRetryAfter === !0) return q(e, t, r, n);
230
- let i = G(t), a = n ? p.tapError(e, (e) => p.sync(() => {
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 p.retry(a, {
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", te = (e) => typeof e == "object" && !!e && e[Y] === !0, ne = (e) => typeof e == "object" && e && e[X] !== void 0 ? e[X] : {
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
- }, re = ((e) => {
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 = E({
296
+ let o = T({
258
297
  workflowName: e.name,
259
298
  payloadSchema: e.payload,
260
299
  control: e
261
300
  });
262
- return o !== void 0 && Object.defineProperty(n, Q, {
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
- }), ie = (e) => {
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
- }, ae = i.make, oe = i.process, se = ((...e) => {
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
- }), ce = o.rateLimit, $ = (e) => p.gen(function* () {
277
- let t = yield* k, r = yield* p.serviceOption(O), i = f.toMillis(f.decode(e.duration));
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* j;
280
- yield* p.promise(() => a.recordEvent({
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* p.promise(() => a.recordEvent({
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
- }), le = (t) => p.gen(function* () {
347
+ }), ve = (t) => m.gen(function* () {
304
348
  let n = (yield* e.make({
305
349
  name: `sleepUntil:${t.name}`,
306
- success: _.Number,
307
- execute: p.sync(() => t.until instanceof Date ? t.until.getTime() : t.until)
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
- }), ue = (t, n, r, i) => p.gen(function* () {
314
- let a = yield* k, o = yield* e.CurrentAttempt, s = yield* p.serviceOption(O), c = yield* p.serviceOption(F), l = c._tag === "Some" ? c.value.interceptor(n, {
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* p.promise(() => f.startStep({
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 m = Date.now();
335
- return yield* J(l, i).pipe(p.tap((e) => d && f ? p.promise(async () => {
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() - m
384
+ durationMs: Date.now() - p
341
385
  });
342
386
  } catch {}
343
- }) : p.void), p.tapErrorCause((e) => d && f && !u.isInterruptedOnly(e) ? p.promise(async () => {
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() - m
399
+ durationMs: Date.now() - p
356
400
  });
357
401
  } catch {}
358
- }) : p.void));
359
- }), de = (e) => p.gen(function* () {
360
- let t = yield* h.get(L);
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* z([n]));
364
- }), fe = (e, t) => {
407
+ n !== void 0 && (yield* U([n]));
408
+ }), xe = (e, t) => {
365
409
  let n = e;
366
410
  return {
367
411
  ...e,
368
- commit: () => p.zipRight(de(t), n)
412
+ commit: () => m.zipRight(be(t), n)
369
413
  };
370
- }, pe = (t) => {
414
+ }, Se = (t) => {
371
415
  let { input: n, retry: r, ...i } = t;
372
- return fe(e.make({
416
+ return xe(e.make({
373
417
  ...i,
374
- execute: ue(i.name, i.execute, n, r)
418
+ execute: ye(i.name, i.execute, n, r)
375
419
  }), i.name);
376
- }, me = (e) => p.map(h.get(M), (t) => t.includes(e)), he = c.withCompensation, ge = e.idempotencyKey;
420
+ }, Ce = (e) => m.map(g.get(j), (t) => t.includes(e)), we = c.withCompensation, Te = e.idempotencyKey;
377
421
  //#endregion
378
- export { k as A, v as B, l as C, z as D, R as E, N as F, b as I, D as L, O as M, F as N, A as O, P, x as R, re as S, I as T, E as V, le as _, r as a, t as b, s as c, te as d, me as f, $ as g, ce as h, Y as i, j, M as k, ie as l, se as m, Z as n, ae as o, oe as p, X as r, a as s, Q as t, ne as u, pe as v, L as w, he as x, ge as y, y as z };
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 };
@@ -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
  *
@@ -1,2 +1,2 @@
1
- import { B as e, C as t, D as n, E as r, I as i, L as a, R as o, S as s, T as c, V as l, _ as u, a as d, b as f, c as p, d as m, f as h, g, h as _, i as v, l as y, m as b, n as x, o as S, p as C, r as w, s as T, t as E, u as D, v as O, w as k, x as A, y as j, z as M } from "./primitives-oLKOTbvJ.js";
2
- export { k as CurrentWorkflowReplayShape, i as DEFERRING_CONTROLS, E as WorkflowFlowControlProperty, x as WorkflowMessagesProperty, w as WorkflowVersionTypeId, v as WorkflowWorkerLayerTypeId, a as assertConsistentPools, o as deferringControlsOf, d as durableClock, S as durableQueue, T as durableQueueModule, p as durableRateLimiterModule, y as getWorkflowFlowControl, D as getWorkflowVersionMetadata, M as hasCancelOn, e as hasFlowControl, m as isWorkflowWorkerLayer, c as makeReplayShapeTracker, r as nondeterminismEventPayload, h as patch, C as processQueue, b as queueWorker, _ as rateLimit, n as reportNondeterminism, g as sleep, u as sleepUntil, O as step, j as stepIdempotencyKey, f as stepModule, l as validateFlowControl, A as withCompensation, s as workflow, t as workflowModule };
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 };