@voltro/workflow 0.28.0 → 0.30.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/workflow",
3
- "version": "0.28.0",
3
+ "version": "0.30.0",
4
4
  "description": "Durable workflows for Voltro — the workflow() descriptor + step() / awaitSignal primitives over @effect/cluster, with a browser-safe define subpath.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -50,8 +50,8 @@
50
50
  "@effect/cluster": "^0.60.0",
51
51
  "@effect/sql": "^0.52.0",
52
52
  "@effect/workflow": "^0.19.0",
53
- "@voltro/database": "0.28.0",
54
- "@voltro/protocol": "0.28.0"
53
+ "@voltro/database": "0.30.0",
54
+ "@voltro/protocol": "0.30.0"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "effect": "^3.22.0",
@@ -1,131 +0,0 @@
1
- import { Context as e, Duration as t, Effect as n, Layer as r, Option as i } from "effect";
2
- import { ClusterCron as a, ClusterSchema as o, ClusterWorkflowEngine as s, EntityAddress as c, EntityId as l, EntityType as u, MessageStorage as d, RunnerAddress as f, Sharding as p, SingleRunner as m } from "@effect/cluster";
3
- import { SqlClient as h } from "@effect/sql";
4
- //#region src/clusterLayer.ts
5
- var g = 34e3, _ = (e) => {
6
- let t = e.runnerListenHost ?? process.env.POD_IP ?? process.env.VOLTRO_WORKFLOW_RUNNER_HOST ?? "localhost";
7
- return {
8
- host: t,
9
- port: e.runnerListenPort ?? 34e3,
10
- localhostRisk: e.runnerStorage === "sql" && (t === "localhost" || t === "127.0.0.1")
11
- };
12
- }, v = (e) => {
13
- let t = e.sqliteFamily ? "memory" : "sql", n = e.override ?? process.env.VOLTRO_WORKFLOW_RUNNER_STORAGE;
14
- if (n === void 0 || n.trim() === "") return t;
15
- let r = n.trim().toLowerCase();
16
- if (r !== "memory" && r !== "sql") throw Error(`VOLTRO_WORKFLOW_RUNNER_STORAGE must be 'memory' or 'sql', got '${n}'.`);
17
- if (r === "sql" && e.sqliteFamily) throw Error("VOLTRO_WORKFLOW_RUNNER_STORAGE='sql' is unsupported on sqlite / turso — @effect/cluster has no sqlite advisory-lock branch. Use 'memory'.");
18
- return r;
19
- }, y = (e = process.env.VOLTRO_WORKFLOW_SHARD_LOCK) => {
20
- if (e === void 0 || e.trim() === "") return "auto";
21
- let t = e.trim().toLowerCase();
22
- if (t === "auto" || t === "row" || t === "advisory") return t;
23
- throw Error(`VOLTRO_WORKFLOW_SHARD_LOCK must be 'auto', 'row', or 'advisory', got '${e}'.`);
24
- }, b = n.gen(function* () {
25
- let e = yield* (yield* h.SqlClient)`SELECT @@wsrep_on AS wsrep`.pipe(n.catchAll(() => n.succeed([])));
26
- if (e.length === 0) return !1;
27
- let t = e[0].wsrep;
28
- return t === 1 || t === "1" || t === !0 || t === "ON" || t === "on";
29
- }), x = (e) => n.gen(function* () {
30
- return e.runnerStorage === "memory" ? !1 : e.mode === "row" ? !0 : e.mode === "advisory" || e.dialectId !== "mysql" && e.dialectId !== "mariadb" ? !1 : yield* b;
31
- }), S = () => {
32
- let e = (e) => {
33
- if (e === void 0) return;
34
- let t = Number(e);
35
- return Number.isFinite(t) && t > 0 ? t : void 0;
36
- }, t = e(process.env.VOLTRO_WORKFLOW_FAILOVER_LEASE), n = e(process.env.VOLTRO_WORKFLOW_FAILOVER_HEARTBEAT), r = e(process.env.VOLTRO_WORKFLOW_POLL_INTERVAL);
37
- return {
38
- ...t === void 0 ? {} : { failoverLeaseSeconds: t },
39
- ...n === void 0 ? {} : { failoverHeartbeatSeconds: n },
40
- ...r === void 0 ? {} : { messagePollSeconds: r }
41
- };
42
- }, C = n.flatMap(p.Sharding, (e) => e.pollStorage), w = /* @__PURE__ */ new Set(), T = (e) => {
43
- let a = _({
44
- runnerStorage: e.runnerStorage,
45
- runnerListenHost: e.runnerListenHost,
46
- runnerListenPort: e.runnerListenPort
47
- }), o = a.host, c = a.port;
48
- a.localhostRisk && !w.has(o) && (w.add(o), process.stderr.write(`[voltro:workflow] runner host is '${o}' with SQL cluster storage — other pods cannot reach this runner, so a workflow cannot resume on another pod after a reschedule. Inject POD_IP via the K8s downward API (fieldRef: status.podIP) or set runnerListenHost / VOLTRO_WORKFLOW_RUNNER_HOST.
49
- `));
50
- let l = e.shardLockMode ?? y(), u = r.unwrapEffect(x({
51
- dialectId: e.dialectId,
52
- runnerStorage: e.runnerStorage,
53
- mode: l
54
- }).pipe(n.map((n) => (e.runnerStorage === "sql" && process.stderr.write(`[voltro:workflow] shard-lock coordination: ${n ? "row-based" : "advisory"} (dialect=${e.dialectId ?? "unknown"}, mode=${l}` + (n && l === "auto" ? ", wsrep/Galera cluster detected" : "") + ")\n"), m.layer({
55
- runnerStorage: e.runnerStorage,
56
- shardingConfig: {
57
- runnerAddress: i.some(f.make(o, c)),
58
- runnerListenAddress: i.some(f.make(o, c)),
59
- ...n ? { shardLockDisableAdvisory: !0 } : {},
60
- ...e.failoverLeaseSeconds === void 0 ? {} : { shardLockExpiration: t.seconds(e.failoverLeaseSeconds) },
61
- ...e.failoverHeartbeatSeconds === void 0 ? {} : { shardLockRefreshInterval: t.seconds(e.failoverHeartbeatSeconds) },
62
- ...e.messagePollSeconds === void 0 ? {} : { entityMessagePollInterval: t.seconds(e.messagePollSeconds) }
63
- }
64
- }))))).pipe(r.provide(e.sqlClientLayer)), d = e.extraShardingLayers ?? [];
65
- return (d.length === 0 ? s.layer : r.mergeAll(s.layer, ...d)).pipe(r.provideMerge(u));
66
- }, E = (e) => a.make({
67
- name: e.name,
68
- cron: e.cron,
69
- execute: n.promise(() => e.execute()),
70
- skipIfOlderThan: e.skipIfOlderThan ?? t.days(1)
71
- }), D = (e, t) => `${e}/${t}`, O = (t, n, r) => {
72
- let i = l.make(n), a = e.get(t.annotations, o.ShardGroup)(i);
73
- return new c.EntityAddress({
74
- entityType: u.EntityType.make(`Workflow/${t.name}`),
75
- entityId: i,
76
- shardId: r.getShardId(i, a)
77
- });
78
- }, k = (e) => {
79
- let t = (e) => e && typeof e == "object" && "_tag" in e ? String(e._tag) : void 0, n = t(e);
80
- if (n === "Failure" || n !== "Success") return "failed";
81
- let r = e.value, i = t(r);
82
- if (i === "Suspended") return "suspended";
83
- if (i === "Complete") {
84
- let e = r.exit;
85
- return t(e) === "Success" ? "succeeded" : "failed";
86
- }
87
- return "failed";
88
- }, A = (e) => n.gen(function* () {
89
- let t = yield* p.Sharding, r = yield* d.MessageStorage, a = O(e.workflow, e.executionId, t), o = yield* r.requestIdForPrimaryKey({
90
- address: a,
91
- tag: "run",
92
- id: ""
93
- }).pipe(n.orElseSucceed(() => i.none()));
94
- if (i.isNone(o)) return {
95
- redriven: !1,
96
- runReplyCleared: !1,
97
- activitiesReset: 0,
98
- reason: "no-journal"
99
- };
100
- let s = (yield* r.repliesForUnfiltered([o.value]).pipe(n.orElseSucceed(() => []))).filter((e) => e._tag === "WithExit").at(-1);
101
- if (s === void 0) return {
102
- redriven: !1,
103
- runReplyCleared: !1,
104
- activitiesReset: 0,
105
- reason: "no-reply"
106
- };
107
- if (k(s.exit) === "succeeded") return {
108
- redriven: !1,
109
- runReplyCleared: !1,
110
- activitiesReset: 0,
111
- reason: "succeeded"
112
- };
113
- let c = 0;
114
- for (let o of e.failedActivities) {
115
- let e = yield* r.requestIdForPrimaryKey({
116
- address: a,
117
- tag: "activity",
118
- id: D(o.name, o.attempt)
119
- }).pipe(n.orElseSucceed(() => i.none()));
120
- i.isSome(e) && (yield* t.reset(e.value)) && (c += 1);
121
- }
122
- let l = yield* t.reset(o.value);
123
- return yield* t.pollStorage, {
124
- redriven: !0,
125
- runReplyCleared: l,
126
- activitiesReset: c,
127
- reason: null
128
- };
129
- });
130
- //#endregion
131
- export { b as a, S as c, x as d, T as f, g as i, _ as l, A as n, y as o, E as r, C as s, D as t, v as u };
@@ -1,164 +0,0 @@
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 g, Schedule as _ } from "effect";
3
- //#region src/recorder.ts
4
- var v = class extends d.Tag("@voltro/WorkflowRunRecorder")() {}, y = h.unsafeMake(void 0), b = h.unsafeMake(void 0), x = h.unsafeMake(void 0), S = () => h.get(y), C = () => h.get(b), w = class extends d.Tag("@voltro/WorkflowStepInterceptor")() {}, T = (e) => {
5
- if (typeof e != "object" || !e) return;
6
- let t = e.retryAfterMillis;
7
- if (typeof t == "number" && Number.isFinite(t) && t >= 0) return f.millis(t);
8
- let n = e.retryAfter;
9
- if (typeof n == "number" && Number.isFinite(n) && n >= 0) return f.seconds(n);
10
- }, E = (e) => f.decode(e), D = (e) => typeof e == "object" && e && "_tag" in e ? String(e._tag) : void 0, O = (e) => e !== void 0 && (e.maxAttempts ?? 3) > 1, k = (e) => {
11
- if (e.retryable) return e.retryable;
12
- if (e.retryableErrors && e.retryableErrors.length > 0) {
13
- let t = new Set(e.retryableErrors);
14
- return (e) => {
15
- let n = D(e);
16
- return n !== void 0 && t.has(n);
17
- };
18
- }
19
- return () => !0;
20
- }, A = (e) => {
21
- let t = e.baseDelay ?? "200 millis", n = e.strategy ?? "exponential", r = e.maxAttempts ?? 3, i = n === "linear" ? _.linear(E(e.step ?? t)) : n === "fixed" ? _.spaced(E(t)) : _.exponential(E(t), e.factor ?? 2);
22
- if (e.maxDelay !== void 0) {
23
- let t = E(e.maxDelay);
24
- i = _.modifyDelay(i, (e, n) => f.min(n, t));
25
- }
26
- (e.jitter ?? !0) === !0 && (i = _.jittered(i));
27
- let a = _.intersect(i, _.recurs(Math.max(0, r - 1)));
28
- return e.maxElapsed !== void 0 && (a = _.upTo(E(e.maxElapsed))(a)), a;
29
- }, j = (e, t) => {
30
- let n = f.toMillis(E(e.baseDelay ?? "200 millis")), r = e.strategy ?? "exponential", i = r === "fixed" ? n : r === "linear" ? n + f.toMillis(E(e.step ?? e.baseDelay ?? "200 millis")) * (t - 1) : n * (e.factor ?? 2) ** (t - 1);
31
- return e.maxDelay !== void 0 && (i = Math.min(i, f.toMillis(E(e.maxDelay)))), (e.jitter ?? !0) === !0 && (i *= Math.random()), i;
32
- }, M = (e, t, n, r) => p.gen(function* () {
33
- let i = t.maxAttempts ?? 3, a = t.maxElapsed === void 0 ? Infinity : f.toMillis(E(t.maxElapsed)), o = yield* p.sync(() => Date.now()), s = 0;
34
- for (;;) {
35
- let c = yield* p.exit(e);
36
- if (m.isSuccess(c)) return c.value;
37
- let l = c.cause, d = u.failureOption(l), h = g.isSome(d) ? d.value : u.squash(l);
38
- s += 1;
39
- let _ = (yield* p.sync(() => Date.now())) - o;
40
- if (s >= i || !n(h) || _ >= a) return yield* p.failCause(l);
41
- r?.(h);
42
- let v = T(h), y = v === void 0 ? j(t, s) : f.toMillis(v);
43
- yield* p.sleep(f.millis(y));
44
- }
45
- }), N = (e, t, n) => {
46
- if (!O(t)) return e;
47
- let r = k(t);
48
- if (t.respectRetryAfter === !0) return M(e, t, r, n);
49
- let i = A(t), a = n ? p.tapError(e, (e) => p.sync(() => {
50
- r(e) && n(e);
51
- })) : e;
52
- return p.retry(a, {
53
- schedule: i,
54
- while: r
55
- });
56
- }, P = Symbol.for("voltro.workflow.workerLayer"), F = Symbol.for("voltro.workflow.version"), I = "__voltroWorkflowMessages", L = (e) => typeof e == "object" && !!e && e[P] === !0, R = (e) => typeof e == "object" && e && e[F] !== void 0 ? e[F] : {
57
- version: "1",
58
- compatibleWith: ["1"],
59
- patches: []
60
- }, z = ((e) => {
61
- let t = c.make(e), n = e.suspendOnFailure === !0 ? t.annotate(c.SuspendOnFailure, !0) : t, r = String(e.version ?? "1"), i = {
62
- version: r,
63
- compatibleWith: (e.compatibleWith ?? [r]).map(String),
64
- patches: [...e.patches ?? []]
65
- }, a = {
66
- signals: { ...e.messages?.signals ?? {} },
67
- updates: { ...e.messages?.updates ?? {} },
68
- queries: { ...e.messages?.queries ?? {} }
69
- };
70
- return Object.defineProperty(n, F, {
71
- value: i,
72
- enumerable: !1
73
- }), Object.defineProperty(n, I, {
74
- value: a,
75
- enumerable: !1
76
- }), n;
77
- }), B = i.make, V = i.process, H = ((...e) => {
78
- let t = i.worker(...e);
79
- return Object.defineProperty(t, P, {
80
- value: !0,
81
- enumerable: !1
82
- }), t;
83
- }), U = o.rateLimit, W = (e) => p.gen(function* () {
84
- let t = yield* y, r = yield* p.serviceOption(v), i = f.toMillis(f.decode(e.duration));
85
- if (t !== void 0 && r._tag === "Some") {
86
- let a = r.value, o = new Date(Date.now() + i).toISOString(), s = yield* x;
87
- yield* p.promise(() => a.recordEvent({
88
- runId: t,
89
- eventType: "timer-set",
90
- payload: {
91
- name: e.name,
92
- durationMs: i,
93
- scheduledWakeAt: o,
94
- ...s === void 0 ? {} : { tenantId: s }
95
- }
96
- }).catch(() => {}));
97
- let c = Date.now();
98
- yield* n.sleep(e), yield* p.promise(() => a.recordEvent({
99
- runId: t,
100
- eventType: "timer-fired",
101
- payload: {
102
- name: e.name,
103
- actualDurationMs: Date.now() - c,
104
- ...s === void 0 ? {} : { tenantId: s }
105
- }
106
- }).catch(() => {}));
107
- return;
108
- }
109
- yield* n.sleep(e);
110
- }), G = (t, n, r, i) => p.gen(function* () {
111
- let a = yield* y, o = yield* e.CurrentAttempt, s = yield* p.serviceOption(v), c = yield* p.serviceOption(w), l = c._tag === "Some" ? c.value.interceptor(n, {
112
- runId: a ?? "unrecorded",
113
- stepName: t,
114
- attempt: o
115
- }) : n, d, f;
116
- if (a !== void 0 && s._tag === "Some") {
117
- f = s.value;
118
- let e = i === void 0 ? void 0 : (({ retryable: e, ...t }) => t)(i);
119
- try {
120
- d = yield* p.promise(() => f.startStep({
121
- runId: a,
122
- stepName: t,
123
- attempt: o,
124
- stepInput: r,
125
- ...e === void 0 ? {} : { retryPolicy: e }
126
- }));
127
- } catch {
128
- d = void 0;
129
- }
130
- }
131
- let m = Date.now();
132
- return yield* N(l, i).pipe(p.tap((e) => d && f ? p.promise(async () => {
133
- try {
134
- await f.endStepSuccess({
135
- stepRecordId: d,
136
- output: e,
137
- durationMs: Date.now() - m
138
- });
139
- } catch {}
140
- }) : p.void), p.tapErrorCause((e) => d && f && !u.isInterruptedOnly(e) ? p.promise(async () => {
141
- try {
142
- 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;
143
- await f.endStepFailure({
144
- stepRecordId: d,
145
- errorTag: o,
146
- errorMessage: a,
147
- errorCause: {
148
- pretty: u.pretty(e),
149
- failures: n,
150
- defects: r
151
- },
152
- durationMs: Date.now() - m
153
- });
154
- } catch {}
155
- }) : p.void));
156
- }), K = (t) => {
157
- let { input: n, retry: r, ...i } = t;
158
- return e.make({
159
- ...i,
160
- execute: G(i.name, i.execute, n, r)
161
- });
162
- }, q = c.withCompensation, J = e.idempotencyKey;
163
- //#endregion
164
- export { v as C, S as E, x as S, C as T, q as _, B as a, b, R as c, H as d, U as f, t as g, J as h, r as i, L as l, K as m, F as n, a as o, W as p, P as r, s, I as t, V as u, z as v, w, y as x, l as y };