@voltro/workflow 0.83.0 → 0.84.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 +717 -0
- package/dist/{cluster-NnChVZoK.js → cluster-i3frC3Da.js} +174 -132
- package/dist/cluster.d.ts +13 -13
- package/dist/cluster.js +1 -1
- package/dist/clusterTestSuite.js +2 -2
- package/dist/index.d.ts +109 -11
- package/dist/index.js +3 -3
- package/dist/{src--sCbH8Fs.js → src-DP-lKqaG.js} +811 -805
- package/package.json +4 -4
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { _ as e, a as t, b as n, c as r, l as i, m as a, n as o, p as s, s as c, t as l, x as u } from "./clusterDiagnostics-CGUtnJkX.js";
|
|
2
|
-
import { Context as d, Duration as f, Effect as p, Layer as m, Option as h, Runtime as g,
|
|
3
|
-
import { eq as
|
|
4
|
-
import { SqlClient as
|
|
5
|
-
import { ClusterCron as ee, ClusterSchema as te, ClusterWorkflowEngine as
|
|
6
|
-
import { applyUndeclaredCharsetWork as
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
2
|
+
import { Context as d, Duration as f, Effect as p, Layer as m, Option as h, Runtime as g, Schedule as _, Schema as v } from "effect";
|
|
3
|
+
import { eq as y } from "@voltro/database";
|
|
4
|
+
import { SqlClient as b } from "@effect/sql";
|
|
5
|
+
import { ClusterCron as ee, ClusterSchema as te, ClusterWorkflowEngine as x, EntityAddress as ne, EntityId as re, EntityType as ie, MessageStorage as S, RunnerAddress as C, RunnerHealth as ae, RunnerStorage as w, Runners as oe, ShardId as se, Sharding as T, ShardingConfig as E, SqlMessageStorage as D, SqlRunnerStorage as O } from "@effect/cluster";
|
|
6
|
+
import { applyUndeclaredCharsetWork as ce, fnv1a64 as k, readUndeclaredCharsetColumns as le, resolveMigrationLockSchema as A, sqlPoolCapacity as ue, undeclaredCharsetWork as de } from "@voltro/database/sql";
|
|
7
|
+
import { PersistenceError as fe } from "@effect/cluster/ClusterError";
|
|
8
|
+
import { realpath as pe } from "node:fs/promises";
|
|
9
|
+
import { createLogger as me } from "@voltro/logger";
|
|
9
10
|
//#region src/workflowResumeDispatch.ts
|
|
10
|
-
var
|
|
11
|
+
var he = (e) => p.gen(function* () {
|
|
11
12
|
let t = yield* p.runtime(), r = yield* p.promise(() => e.transaction((n) => u(n, e.requestId, (e) => g.runPromise(t)(s(e)))));
|
|
12
13
|
if (r.kind !== "bound") return r;
|
|
13
14
|
let i = yield* p.promise(() => e.transaction((t) => n(t, e.requestId)));
|
|
@@ -19,15 +20,15 @@ var ge = (e) => p.gen(function* () {
|
|
|
19
20
|
executionId: o.executionId,
|
|
20
21
|
...o.resumeJournal
|
|
21
22
|
});
|
|
22
|
-
return yield* (yield*
|
|
23
|
+
return yield* (yield* T.Sharding).pollStorage, {
|
|
23
24
|
kind: "attempted",
|
|
24
25
|
request: o,
|
|
25
26
|
journal: c
|
|
26
27
|
};
|
|
27
|
-
}),
|
|
28
|
-
let e = yield*
|
|
29
|
-
return
|
|
30
|
-
}),
|
|
28
|
+
}), j = class extends d.Tag("@voltro/workflow/WorkflowResumeDispatcher")() {}, ge = p.gen(function* () {
|
|
29
|
+
let e = yield* b.SqlClient, t = yield* T.Sharding;
|
|
30
|
+
return j.of({ dispatch: (n) => he(n).pipe(p.provideService(b.SqlClient, e), p.provideService(T.Sharding, t)) });
|
|
31
|
+
}), _e = async (e) => {
|
|
31
32
|
if (e.workflow.name !== e.command.workflowName || e.control.workflowName !== e.command.workflowName) throw Error("Restart preparation requires the matching workflow");
|
|
32
33
|
let n = await e.transaction(async (n) => {
|
|
33
34
|
let r = await t(n, e.command);
|
|
@@ -37,7 +38,7 @@ var ge = (e) => p.gen(function* () {
|
|
|
37
38
|
};
|
|
38
39
|
let [i] = await n.query({
|
|
39
40
|
table: "_voltro_workflow_runs",
|
|
40
|
-
predicate:
|
|
41
|
+
predicate: y("id", e.command.runId),
|
|
41
42
|
lock: "update",
|
|
42
43
|
order: [],
|
|
43
44
|
take: 1,
|
|
@@ -46,24 +47,24 @@ var ge = (e) => p.gen(function* () {
|
|
|
46
47
|
});
|
|
47
48
|
return {
|
|
48
49
|
kind: "captured",
|
|
49
|
-
generation:
|
|
50
|
+
generation: v.decodeUnknownSync(v.NonEmptyString)(i?.runnerGeneration)
|
|
50
51
|
};
|
|
51
52
|
});
|
|
52
53
|
return n.kind === "accepted" ? {
|
|
53
54
|
kind: "reserved",
|
|
54
55
|
request: n.request,
|
|
55
56
|
reused: !0
|
|
56
|
-
} :
|
|
57
|
+
} : ye({
|
|
57
58
|
...e,
|
|
58
59
|
command: {
|
|
59
60
|
...e.command,
|
|
60
61
|
generation: n.generation
|
|
61
62
|
}
|
|
62
63
|
});
|
|
63
|
-
},
|
|
64
|
-
requestId:
|
|
65
|
-
restartToken:
|
|
66
|
-
}),
|
|
64
|
+
}, ve = v.Struct({
|
|
65
|
+
requestId: v.NonEmptyString,
|
|
66
|
+
restartToken: v.NullOr(v.NonEmptyString)
|
|
67
|
+
}), ye = async (e) => {
|
|
67
68
|
let { command: t } = e;
|
|
68
69
|
if (e.workflow.name !== t.workflowName || e.control.workflowName !== t.workflowName) throw Error("Restart preparation requires the matching workflow");
|
|
69
70
|
let n = await e.transaction(async (n) => {
|
|
@@ -74,7 +75,7 @@ var ge = (e) => p.gen(function* () {
|
|
|
74
75
|
};
|
|
75
76
|
let [i] = await n.query({
|
|
76
77
|
table: "_voltro_workflow_runs",
|
|
77
|
-
predicate:
|
|
78
|
+
predicate: y("id", t.runId),
|
|
78
79
|
lock: "update",
|
|
79
80
|
order: [],
|
|
80
81
|
take: 1,
|
|
@@ -82,11 +83,11 @@ var ge = (e) => p.gen(function* () {
|
|
|
82
83
|
projection: void 0
|
|
83
84
|
});
|
|
84
85
|
if (i === void 0 || i.tag !== t.workflowName || i.runnerGeneration !== t.generation || i.status !== "failed" || i.discardedAt != null || typeof i.executionId != "string" || i.executionId.length === 0) throw Error("Restart preparation requires its failed resident generation");
|
|
85
|
-
if ((
|
|
86
|
+
if ((v.decodeUnknownSync(v.NullOr(v.Struct({ tenantId: v.NullOr(v.NonEmptyString) })))(i.subject ?? null)?.tenantId ?? null) !== t.tenantId) throw Error("Restart preparation belongs to another tenant");
|
|
86
87
|
return {
|
|
87
88
|
kind: "observe",
|
|
88
89
|
executionId: i.executionId,
|
|
89
|
-
delivery:
|
|
90
|
+
delivery: v.decodeUnknownSync(ve)(i.engineDelivery),
|
|
90
91
|
activities: await e.readActivities(n)
|
|
91
92
|
};
|
|
92
93
|
});
|
|
@@ -111,7 +112,7 @@ var ge = (e) => p.gen(function* () {
|
|
|
111
112
|
if (i.kind === "unavailable") return i;
|
|
112
113
|
let [s] = await a.query({
|
|
113
114
|
table: "_voltro_workflow_runs",
|
|
114
|
-
predicate:
|
|
115
|
+
predicate: y("id", t.runId),
|
|
115
116
|
lock: "update",
|
|
116
117
|
order: [],
|
|
117
118
|
take: 1,
|
|
@@ -129,16 +130,16 @@ var ge = (e) => p.gen(function* () {
|
|
|
129
130
|
now: e.now
|
|
130
131
|
});
|
|
131
132
|
});
|
|
132
|
-
},
|
|
133
|
+
}, M = (e, t) => `${e}/${t}`, N = (e, t, n) => {
|
|
133
134
|
let r = re.make(t), i = d.get(e.annotations, te.ShardGroup)(r);
|
|
134
135
|
return new ne.EntityAddress({
|
|
135
136
|
entityType: ie.EntityType.make(`Workflow/${e.name}`),
|
|
136
137
|
entityId: r,
|
|
137
138
|
shardId: n.getShardId(r, i)
|
|
138
139
|
});
|
|
139
|
-
},
|
|
140
|
-
let n = yield*
|
|
141
|
-
address:
|
|
140
|
+
}, be = (e, t) => p.gen(function* () {
|
|
141
|
+
let n = yield* T.Sharding, r = yield* S.MessageStorage, i = yield* r.requestIdForPrimaryKey({
|
|
142
|
+
address: N(e, t, n),
|
|
142
143
|
tag: "run",
|
|
143
144
|
id: ""
|
|
144
145
|
});
|
|
@@ -153,23 +154,23 @@ var ge = (e) => p.gen(function* () {
|
|
|
153
154
|
replyId: String(o.id),
|
|
154
155
|
exit: o.exit
|
|
155
156
|
};
|
|
156
|
-
}),
|
|
157
|
-
success:
|
|
158
|
-
failure:
|
|
159
|
-
defect:
|
|
160
|
-
})),
|
|
161
|
-
let t = (e) => e && typeof e == "object" && "_tag" in e && typeof e._tag == "string" ? e._tag : void 0, n =
|
|
157
|
+
}), P = v.decodeUnknownOption(v.Exit({
|
|
158
|
+
success: v.Unknown,
|
|
159
|
+
failure: v.Unknown,
|
|
160
|
+
defect: v.Unknown
|
|
161
|
+
})), F = (e) => {
|
|
162
|
+
let t = (e) => e && typeof e == "object" && "_tag" in e && typeof e._tag == "string" ? e._tag : void 0, n = P(e);
|
|
162
163
|
if (h.isNone(n)) return "unrecognized-reply";
|
|
163
164
|
if (n.value._tag === "Failure") return "failed";
|
|
164
165
|
let r = n.value.value, i = t(r);
|
|
165
166
|
if (i === "Suspended") return "suspended";
|
|
166
167
|
if (i === "Complete") {
|
|
167
|
-
let e = r.exit, t =
|
|
168
|
+
let e = r.exit, t = P(e);
|
|
168
169
|
return h.isNone(t) ? "unrecognized-reply" : t.value._tag === "Success" ? "succeeded" : "failed";
|
|
169
170
|
}
|
|
170
171
|
return "unrecognized-reply";
|
|
171
|
-
},
|
|
172
|
-
let r = yield*
|
|
172
|
+
}, xe = (e) => I(e.workflow, e.executionId, e.failedActivities), Se = (e) => I(e.workflow, e.executionId, e.resetActivities), I = (e, t, n) => p.gen(function* () {
|
|
173
|
+
let r = yield* T.Sharding, i = yield* S.MessageStorage, a = N(e, t, r), o = yield* i.requestIdForPrimaryKey({
|
|
173
174
|
address: a,
|
|
174
175
|
tag: "run",
|
|
175
176
|
id: ""
|
|
@@ -187,7 +188,7 @@ var ge = (e) => p.gen(function* () {
|
|
|
187
188
|
activitiesReset: 0,
|
|
188
189
|
reason: "no-reply"
|
|
189
190
|
};
|
|
190
|
-
let c =
|
|
191
|
+
let c = F(s.exit);
|
|
191
192
|
if (c !== "failed") return {
|
|
192
193
|
redriven: !1,
|
|
193
194
|
runReplyCleared: !1,
|
|
@@ -199,7 +200,7 @@ var ge = (e) => p.gen(function* () {
|
|
|
199
200
|
let t = yield* i.requestIdForPrimaryKey({
|
|
200
201
|
address: a,
|
|
201
202
|
tag: "activity",
|
|
202
|
-
id:
|
|
203
|
+
id: M(e.name, e.attempt)
|
|
203
204
|
}).pipe(p.orDie);
|
|
204
205
|
if (h.isNone(t)) return {
|
|
205
206
|
redriven: !1,
|
|
@@ -231,12 +232,12 @@ var ge = (e) => p.gen(function* () {
|
|
|
231
232
|
activitiesReset: u,
|
|
232
233
|
reason: "run-reset-unconfirmed"
|
|
233
234
|
};
|
|
234
|
-
}), Ce =
|
|
235
|
-
key:
|
|
236
|
-
value:
|
|
235
|
+
}), Ce = v.parseJson(v.Record({
|
|
236
|
+
key: v.String,
|
|
237
|
+
value: v.String
|
|
237
238
|
})), we = (e) => p.gen(function* () {
|
|
238
239
|
if (!/^[1-9]\d*$/.test(e.delivery.requestId) || e.executionId.length === 0 || e.activities.some((e) => e.name.length === 0 || !Number.isSafeInteger(e.attempt) || e.attempt < 1)) return yield* p.die(/* @__PURE__ */ Error("Restart observation requires an exact delivery and valid activity attempts"));
|
|
239
|
-
let t = (yield*
|
|
240
|
+
let t = (yield* b.SqlClient).withoutTransforms(), n = yield* S.MessageStorage, r = yield* T.Sharding, i = N(e.workflow, e.executionId, r), a = (n, r) => p.gen(function* () {
|
|
240
241
|
let [i] = yield* t`SELECT m.entity_type, m.entity_id, m.tag, m.headers, r.id AS reply_id, r.payload
|
|
241
242
|
FROM cluster_messages m
|
|
242
243
|
LEFT JOIN cluster_replies r ON r.id = m.last_reply_id AND r.request_id = m.id AND r.kind = 0
|
|
@@ -247,7 +248,7 @@ var ge = (e) => p.gen(function* () {
|
|
|
247
248
|
kind: "unavailable",
|
|
248
249
|
reason: "no-journal"
|
|
249
250
|
};
|
|
250
|
-
if (((yield* p.sync(() =>
|
|
251
|
+
if (((yield* p.sync(() => v.decodeUnknownSync(Ce)(o.headers)))["voltro-workflow-restart"] ?? null) !== e.delivery.restartToken) return {
|
|
251
252
|
kind: "unavailable",
|
|
252
253
|
reason: "delivery-changed"
|
|
253
254
|
};
|
|
@@ -255,7 +256,7 @@ var ge = (e) => p.gen(function* () {
|
|
|
255
256
|
kind: "unavailable",
|
|
256
257
|
reason: "no-reply"
|
|
257
258
|
};
|
|
258
|
-
let s =
|
|
259
|
+
let s = F(yield* p.sync(() => JSON.parse(o.payload)));
|
|
259
260
|
if (s !== "failed") return {
|
|
260
261
|
kind: "unavailable",
|
|
261
262
|
reason: s
|
|
@@ -265,7 +266,7 @@ var ge = (e) => p.gen(function* () {
|
|
|
265
266
|
let e = yield* n.requestIdForPrimaryKey({
|
|
266
267
|
address: i,
|
|
267
268
|
tag: "activity",
|
|
268
|
-
id:
|
|
269
|
+
id: M(t.name, t.attempt)
|
|
269
270
|
});
|
|
270
271
|
if (h.isNone(e)) return {
|
|
271
272
|
kind: "unavailable",
|
|
@@ -285,59 +286,59 @@ var ge = (e) => p.gen(function* () {
|
|
|
285
286
|
replyId: t
|
|
286
287
|
}))
|
|
287
288
|
};
|
|
288
|
-
}).pipe(p.provideService(
|
|
289
|
+
}).pipe(p.provideService(b.SafeIntegers, !0)), Te = (e) => p.gen(function* () {
|
|
289
290
|
let t = yield* i(e);
|
|
290
|
-
return t.kind === "waiting-capacity" ? t : (yield* (yield*
|
|
291
|
+
return t.kind === "waiting-capacity" ? t : (yield* (yield* T.Sharding).pollStorage, {
|
|
291
292
|
kind: "attempted",
|
|
292
293
|
journal: t
|
|
293
294
|
});
|
|
294
|
-
}),
|
|
295
|
-
let e = yield*
|
|
296
|
-
return
|
|
295
|
+
}), L = class extends d.Tag("@voltro/workflow/WorkflowRestartDispatcher")() {}, Ee = p.gen(function* () {
|
|
296
|
+
let e = yield* b.SqlClient, t = yield* T.Sharding, n = yield* S.MessageStorage, r = (r) => we(r).pipe(p.provideService(b.SqlClient, e), p.provideService(T.Sharding, t), p.provideService(S.MessageStorage, n));
|
|
297
|
+
return L.of({
|
|
297
298
|
prepare: (e) => p.tryPromise({
|
|
298
|
-
try: (t) =>
|
|
299
|
+
try: (t) => _e({
|
|
299
300
|
...e,
|
|
300
301
|
observe: (e) => p.runPromise(r(e), { signal: t })
|
|
301
302
|
}),
|
|
302
303
|
catch: (e) => e
|
|
303
304
|
}),
|
|
304
305
|
observe: r,
|
|
305
|
-
dispatch: (n) => Te(n).pipe(p.provideService(
|
|
306
|
+
dispatch: (n) => Te(n).pipe(p.provideService(b.SqlClient, e), p.provideService(T.Sharding, t))
|
|
306
307
|
});
|
|
307
|
-
}),
|
|
308
|
-
let e =
|
|
309
|
-
return e === void 0 ?
|
|
310
|
-
},
|
|
311
|
-
let e = `voltro_cluster_migration_${
|
|
308
|
+
}), R = 6322741009312601n, z = () => {
|
|
309
|
+
let e = A();
|
|
310
|
+
return e === void 0 ? R : k(`voltro_cluster_migration_lock:${e}`) & 9223372036854775807n;
|
|
311
|
+
}, B = () => {
|
|
312
|
+
let e = `voltro_cluster_migration_${R.toString(36)}`, t = A();
|
|
312
313
|
if (t === void 0) return e;
|
|
313
314
|
let n = `${e}_${t}`;
|
|
314
|
-
return /^[A-Za-z0-9_]+$/.test(t) && n.length <= 64 ? n : `${e}_h${
|
|
315
|
-
},
|
|
315
|
+
return /^[A-Za-z0-9_]+$/.test(t) && n.length <= 64 ? n : `${e}_h${k(`voltro_cluster_migration_lock:${t}`).toString(16)}`;
|
|
316
|
+
}, V = () => {
|
|
316
317
|
let e = Number(process.env.VOLTRO_CLUSTER_MIGRATION_LOCK_TIMEOUT_MS);
|
|
317
318
|
return Number.isFinite(e) && e > 0 ? e : 6e4;
|
|
318
|
-
}, De = 250,
|
|
319
|
+
}, De = 250, H = (e) => e[0]?.[0], Oe = (e, t) => e.onDialectOrElse({
|
|
319
320
|
sqlite: () => p.void,
|
|
320
|
-
mysql: () => t.executeValues(`SELECT GET_LOCK('${
|
|
321
|
-
mssql: () => t.executeValues(`DECLARE @res int; EXEC @res = sp_getapplock @Resource = '${
|
|
321
|
+
mysql: () => t.executeValues(`SELECT GET_LOCK('${B()}', ${Math.ceil(V() / 1e3)})`, []).pipe(p.flatMap((e) => H(e) === 1 || H(e) === "1" ? p.void : p.die(/* @__PURE__ */ Error(`could not acquire cluster migration lock '${B()}' (GET_LOCK timed out)`)))),
|
|
322
|
+
mssql: () => t.executeValues(`DECLARE @res int; EXEC @res = sp_getapplock @Resource = '${B()}', @LockMode = 'Exclusive', @LockOwner = 'Session', @LockTimeout = ${V()}; SELECT @res`, []).pipe(p.flatMap((e) => Number(H(e) ?? -999) >= 0 ? p.void : p.die(/* @__PURE__ */ Error(`could not acquire cluster migration lock '${B()}' (sp_getapplock ${String(H(e))})`)))),
|
|
322
323
|
orElse: () => {
|
|
323
|
-
let e =
|
|
324
|
-
let t =
|
|
325
|
-
return t === !0 || t === "t" || t === 1 ? p.void : Date.now() - n >=
|
|
324
|
+
let e = z().toString(), n = Date.now(), r = t.executeValues(`SELECT pg_try_advisory_lock(${e}::bigint)`, []).pipe(p.flatMap((e) => {
|
|
325
|
+
let t = H(e);
|
|
326
|
+
return t === !0 || t === "t" || t === 1 ? p.void : Date.now() - n >= V() ? p.die(/* @__PURE__ */ Error(`could not acquire the cluster migration advisory lock within ${Math.round(V() / 1e3)}s. Another runner is migrating the cluster schema, or a prior boot wedged while holding it. (Raise VOLTRO_CLUSTER_MIGRATION_LOCK_TIMEOUT_MS, or inspect pg_locks for the holder.)`)) : p.sleep(`${De} millis`).pipe(p.zipRight(r));
|
|
326
327
|
}));
|
|
327
328
|
return r;
|
|
328
329
|
}
|
|
329
|
-
}), z = (e, t) => e.onDialectOrElse({
|
|
330
|
-
sqlite: () => p.void,
|
|
331
|
-
mysql: () => p.asVoid(t.executeValues(`SELECT RELEASE_LOCK('${I()}')`, [])),
|
|
332
|
-
mssql: () => p.asVoid(t.executeValues(`EXEC sp_releaseapplock @Resource = '${I()}', @LockOwner = 'Session'`, [])),
|
|
333
|
-
orElse: () => p.asVoid(t.executeValues(`SELECT pg_advisory_unlock(${F().toString()}::bigint)`, []))
|
|
334
330
|
}), ke = (e, t) => e.onDialectOrElse({
|
|
331
|
+
sqlite: () => p.void,
|
|
332
|
+
mysql: () => p.asVoid(t.executeValues(`SELECT RELEASE_LOCK('${B()}')`, [])),
|
|
333
|
+
mssql: () => p.asVoid(t.executeValues(`EXEC sp_releaseapplock @Resource = '${B()}', @LockOwner = 'Session'`, [])),
|
|
334
|
+
orElse: () => p.asVoid(t.executeValues(`SELECT pg_advisory_unlock(${z().toString()}::bigint)`, []))
|
|
335
|
+
}), Ae = (e, t) => e.onDialectOrElse({
|
|
335
336
|
sqlite: () => !0,
|
|
336
337
|
orElse: () => !1
|
|
337
338
|
}) ? t : p.scoped(p.gen(function* () {
|
|
338
339
|
let n = yield* e.reserve;
|
|
339
|
-
return yield* p.acquireRelease(Oe(e, n), () =>
|
|
340
|
-
})),
|
|
340
|
+
return yield* p.acquireRelease(Oe(e, n), () => ke(e, n).pipe(p.orDie)), yield* t;
|
|
341
|
+
})), je = (e) => e === "sql" ? ["cluster_messages", "cluster_runners"] : ["cluster_messages"], Me = (e, t) => e.onDialectOrElse({
|
|
341
342
|
pg: () => e`SELECT (to_regclass(${t}) IS NOT NULL) AS present`,
|
|
342
343
|
mysql: () => e`SELECT (COUNT(*) > 0) AS present FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = ${t}`,
|
|
343
344
|
mssql: () => e`SELECT CASE WHEN OBJECT_ID(${t}, 'U') IS NULL THEN 0 ELSE 1 END AS present`,
|
|
@@ -345,63 +346,99 @@ var ge = (e) => p.gen(function* () {
|
|
|
345
346
|
}).pipe(p.map((e) => {
|
|
346
347
|
let t = e[0]?.present;
|
|
347
348
|
return t === !0 || t === 1 || t === "1" || t === "t" || typeof t == "bigint" && t === 1n;
|
|
348
|
-
}), p.catchAll(() => p.succeed(!1))),
|
|
349
|
+
}), p.catchAll(() => p.succeed(!1))), Ne = (e, t) => p.forEach(je(t), (t) => Me(e, t), { concurrency: 1 }).pipe(p.map((e) => e.every(Boolean))), U = (e, t) => p.scoped(p.asVoid(m.build(t.pipe(m.provide(E.layerDefaults), m.provide(m.succeed(b.SqlClient, e)))))), W = (e, t) => U(e, D.layer).pipe(p.zipRight(t === "sql" ? U(e, O.layer) : p.void)), G = (e) => p.map(e.unsafe("SELECT table_name AS t FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name LIKE 'cluster\\_%'"), (e) => new Set(e.map((e) => String(e.t ?? e.T)))), Pe = (e, t, n) => n === void 0 || !e.onDialectOrElse({
|
|
349
350
|
mysql: () => !0,
|
|
350
351
|
orElse: () => !1
|
|
351
|
-
}) ?
|
|
352
|
-
let r = yield*
|
|
353
|
-
yield*
|
|
354
|
-
let i = new Set([...yield*
|
|
355
|
-
i.size !== 0 && (yield*
|
|
356
|
-
}),
|
|
357
|
-
let t = yield*
|
|
358
|
-
return r !== void 0 && r < n ? yield* p.dieMessage(`the workflow runtime needs ${n} database connections at once and the pool has ${r}: ` + (e.runnerStorage === "sql" ? "on a first boot it holds its migration lock and its runner lock while the migration runs on a third, and once running it keeps the runner lock and needs one more to work. " : "on a first boot it holds its migration lock while the migration runs on a second. ") + `Set DB_MAX_CONNECTIONS (or PG_MAX_CONNECTIONS) to at least ${n}` + (e.runnerStorage === "sql" ? ", or VOLTRO_WORKFLOW_RUNNER_STORAGE=memory for a single-process runtime that needs 2 (no cross-pod shard handoff)." : ".")) : ((yield*
|
|
359
|
-
})),
|
|
352
|
+
}) ? W(e, t) : p.gen(function* () {
|
|
353
|
+
let r = yield* G(e);
|
|
354
|
+
yield* W(e, t);
|
|
355
|
+
let i = new Set([...yield* G(e)].filter((e) => !r.has(e)));
|
|
356
|
+
i.size !== 0 && (yield* ce(e, de(yield* le(e), /* @__PURE__ */ new Set(), n, i)));
|
|
357
|
+
}), Fe = (e) => e === "sql" ? 3 : 2, K = (e) => m.effect(b.SqlClient, p.gen(function* () {
|
|
358
|
+
let t = yield* b.SqlClient, n = Fe(e.runnerStorage), r = ue(t);
|
|
359
|
+
return r !== void 0 && r < n ? yield* p.dieMessage(`the workflow runtime needs ${n} database connections at once and the pool has ${r}: ` + (e.runnerStorage === "sql" ? "on a first boot it holds its migration lock and its runner lock while the migration runs on a third, and once running it keeps the runner lock and needs one more to work. " : "on a first boot it holds its migration lock while the migration runs on a second. ") + `Set DB_MAX_CONNECTIONS (or PG_MAX_CONNECTIONS) to at least ${n}` + (e.runnerStorage === "sql" ? ", or VOLTRO_WORKFLOW_RUNNER_STORAGE=memory for a single-process runtime that needs 2 (no cross-pod shard handoff)." : ".")) : ((yield* Ne(t, e.runnerStorage)) || (yield* Ae(t, Pe(t, e.runnerStorage, e.charset))), t);
|
|
360
|
+
})), Ie = (e, t) => p.gen(function* () {
|
|
361
|
+
let n = f.decode(t.interval), r = f.toMillis(t.giveUpAfter), i, a, o = !1, s = !1;
|
|
362
|
+
yield* t.probe.pipe(p.timeout(n), p.isSuccess, p.flatMap((e) => p.sync(() => {
|
|
363
|
+
let n = Date.now();
|
|
364
|
+
if (e) {
|
|
365
|
+
if (i = void 0, !o || (a ??= n, n - a < r)) return;
|
|
366
|
+
t.log.info("workflow runner: its storage answers again — taking part in shard assignment again"), a = void 0, o = !1, s = !1;
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
a = void 0, i ??= n, !o && n - i >= r && (o = !0, t.log.warn(`workflow runner: its connection pool has not served a query for ${Math.round((n - i) / 1e3)}s — released its shards and stopped its heartbeat, so another runner takes them`, { fix: "look for what holds this process's database connections (`voltro_db_operations_in_flight` against DB_MAX_CONNECTIONS)" }));
|
|
370
|
+
}))).pipe(p.repeat(_.spaced(n)), p.forkScoped);
|
|
371
|
+
let c = () => new fe({ cause: /* @__PURE__ */ Error("workflow runner gave its shards up: its connection pool does not serve queries") });
|
|
372
|
+
return w.RunnerStorage.of({
|
|
373
|
+
...e,
|
|
374
|
+
register: (t, n) => p.suspend(() => o ? p.fail(c()) : e.register(t, n)),
|
|
375
|
+
acquire: (t, n) => p.suspend(() => o ? p.succeed([]) : e.acquire(t, n)),
|
|
376
|
+
refresh: (t, n) => p.suspend(() => {
|
|
377
|
+
if (!o) return e.refresh(t, n);
|
|
378
|
+
let r = s ? p.void : e.releaseAll(t).pipe(p.tap(() => p.sync(() => {
|
|
379
|
+
s = !0;
|
|
380
|
+
})), p.ignore);
|
|
381
|
+
return p.zipRight(r, p.fail(c()));
|
|
382
|
+
})
|
|
383
|
+
});
|
|
384
|
+
}), Le = (e) => p.map(p.makeSemaphore(1), (t) => ({
|
|
385
|
+
...e,
|
|
386
|
+
release: (n, r) => t.withPermits(1)(p.suspend(() => e.release(n, r)))
|
|
387
|
+
})), Re = m.unwrapEffect(p.map(b.SqlClient, (e) => e.onDialectOrElse({
|
|
388
|
+
sqlite: () => m.effect(w.RunnerStorage, p.flatMap(w.RunnerStorage, Le)).pipe(m.provide(O.layer)),
|
|
389
|
+
orElse: () => O.layer
|
|
390
|
+
}))), ze = (e) => m.scoped(w.RunnerStorage, p.gen(function* () {
|
|
391
|
+
let t = yield* b.SqlClient;
|
|
392
|
+
return yield* Ie(yield* w.RunnerStorage, {
|
|
393
|
+
...e,
|
|
394
|
+
probe: t`SELECT 1`
|
|
395
|
+
});
|
|
396
|
+
})).pipe(m.provide(m.orDie(Re))), Be = (e) => T.layer.pipe(m.provideMerge(oe.layerNoop), m.provideMerge(D.layer), m.provide([e.runnerStorage === "memory" ? w.layerMemory : ze(e.guard), ae.layerNoop]), m.provide(E.layerFromEnv(e.shardingConfig))), Ve = (e) => p.gen(function* () {
|
|
360
397
|
let t = yield* p.tryPromise({
|
|
361
|
-
try: () =>
|
|
362
|
-
catch:
|
|
398
|
+
try: () => pe(e),
|
|
399
|
+
catch: q
|
|
363
400
|
}), n = yield* p.acquireRelease(p.tryPromise({
|
|
364
401
|
try: async () => {
|
|
365
402
|
let { DatabaseSync: e } = await import("node:sqlite");
|
|
366
403
|
return new e(`${t}.voltro-workflow-lock`);
|
|
367
404
|
},
|
|
368
|
-
catch:
|
|
405
|
+
catch: q
|
|
369
406
|
}), (e) => p.sync(() => e.close()));
|
|
370
407
|
yield* p.try({
|
|
371
408
|
try: () => {
|
|
372
409
|
n.exec("PRAGMA busy_timeout=0"), n.exec("CREATE TABLE IF NOT EXISTS workflow_mutex(id INTEGER PRIMARY KEY)"), n.exec("BEGIN IMMEDIATE");
|
|
373
410
|
},
|
|
374
411
|
catch: (e) => {
|
|
375
|
-
let n =
|
|
412
|
+
let n = q(e), r = n.errcode;
|
|
376
413
|
return r !== void 0 && ((r & 255) == 5 || (r & 255) == 6) ? Error("SQLite workflow journal is already owned by another engine. Stop that engine before starting another, or use a SQL dialect with shared runner coordination for replicas.", { cause: n }) : Error(`Could not acquire SQLite workflow ownership for ${t}`, { cause: n });
|
|
377
414
|
}
|
|
378
415
|
});
|
|
379
|
-
}),
|
|
416
|
+
}), q = (e) => e instanceof Error ? e : Error(String(e)), J = (e) => {
|
|
380
417
|
if (e instanceof Date) return Number.isNaN(e.getTime()) ? null : e;
|
|
381
418
|
if (typeof e == "string" || typeof e == "number") {
|
|
382
419
|
let t = typeof e == "string" && /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(e) ? `${e.replace(" ", "T")}Z` : e, n = new Date(t);
|
|
383
420
|
return Number.isNaN(n.getTime()) ? null : n;
|
|
384
421
|
}
|
|
385
422
|
return null;
|
|
386
|
-
},
|
|
423
|
+
}, Y = (e) => e === !0 || e === 1 || e === "1" || e instanceof Uint8Array && e[0] === 1, He = (e, t) => {
|
|
387
424
|
let n = e.lastIndexOf(":");
|
|
388
425
|
if (n < 0) return;
|
|
389
426
|
let r = e.slice(0, n), i = Number(e.slice(n + 1)), a = t.indexOf(r);
|
|
390
427
|
if (!(a < 0 || !Number.isInteger(i))) return (a + 1) * 1e6 + i;
|
|
391
|
-
},
|
|
392
|
-
let t = yield*
|
|
428
|
+
}, X = (e) => p.gen(function* () {
|
|
429
|
+
let t = yield* b.SqlClient, n = e.shardGroups ?? ["default"], r = e.dialectId ?? "", i = r.startsWith("postgres") || r === "pg", a = (e) => e.pipe(p.catchAll(() => p.succeed([]))), o = t.onDialectOrElse({
|
|
393
430
|
pg: () => t.literal("NOW()"),
|
|
394
431
|
mysql: () => t.literal("NOW()"),
|
|
395
432
|
mssql: () => t.literal("GETDATE()"),
|
|
396
433
|
orElse: () => t.literal("CURRENT_TIMESTAMP")
|
|
397
434
|
}), s = (e, t) => {
|
|
398
|
-
let n =
|
|
435
|
+
let n = J(e.last_read), r = J(e.server_now), i = n !== null && r !== null ? Math.max(0, r.getTime() - n.getTime()) : null;
|
|
399
436
|
return {
|
|
400
437
|
tag: typeof e.tag == "string" ? e.tag : null,
|
|
401
438
|
entityType: String(e.entity_type ?? ""),
|
|
402
439
|
entityId: String(e.entity_id ?? ""),
|
|
403
440
|
shardId: String(e.shard_id ?? ""),
|
|
404
|
-
processed:
|
|
441
|
+
processed: Y(e.processed),
|
|
405
442
|
lastRead: i === null ? null : new Date(t - i)
|
|
406
443
|
};
|
|
407
444
|
}, c = (e) => t.onDialectOrElse({
|
|
@@ -446,12 +483,12 @@ var ge = (e) => p.gen(function* () {
|
|
|
446
483
|
mode: "row",
|
|
447
484
|
holder: r,
|
|
448
485
|
own: r === e.ownAddress || (e.hasShard?.(i) ?? !1),
|
|
449
|
-
since:
|
|
486
|
+
since: J(n.acquired_at)
|
|
450
487
|
};
|
|
451
488
|
}))), h = (r) => {
|
|
452
489
|
let i = /* @__PURE__ */ new Map();
|
|
453
490
|
for (let e of r) {
|
|
454
|
-
let t =
|
|
491
|
+
let t = He(e, n);
|
|
455
492
|
t !== void 0 && i.set(t, e);
|
|
456
493
|
}
|
|
457
494
|
return i.size === 0 ? p.succeed([]) : p.gen(function* () {
|
|
@@ -469,14 +506,14 @@ var ge = (e) => p.gen(function* () {
|
|
|
469
506
|
mode: "advisory",
|
|
470
507
|
holder: s ? `this runner (pid ${a})` : `pid ${a} from ${o}`,
|
|
471
508
|
own: s,
|
|
472
|
-
since:
|
|
509
|
+
since: J(n.state_change),
|
|
473
510
|
detail: {
|
|
474
511
|
pid: a,
|
|
475
512
|
clientAddr: n.client_addr ?? null,
|
|
476
513
|
applicationName: n.application_name ?? null,
|
|
477
514
|
state: n.state ?? null,
|
|
478
515
|
waitEventType: n.wait_event_type ?? null,
|
|
479
|
-
backendStart:
|
|
516
|
+
backendStart: J(n.backend_start)?.toISOString() ?? null
|
|
480
517
|
}
|
|
481
518
|
}];
|
|
482
519
|
});
|
|
@@ -492,8 +529,8 @@ var ge = (e) => p.gen(function* () {
|
|
|
492
529
|
let t = e;
|
|
493
530
|
return {
|
|
494
531
|
address: String(t.address ?? ""),
|
|
495
|
-
lastHeartbeat:
|
|
496
|
-
healthy:
|
|
532
|
+
lastHeartbeat: J(t.last_heartbeat),
|
|
533
|
+
healthy: Y(t.healthy)
|
|
497
534
|
};
|
|
498
535
|
}))) : p.succeed([]),
|
|
499
536
|
discardMessages: (e) => {
|
|
@@ -509,38 +546,38 @@ var ge = (e) => p.gen(function* () {
|
|
|
509
546
|
}).pipe(t.withTransaction, p.catchAll(() => p.succeed(0)));
|
|
510
547
|
}
|
|
511
548
|
};
|
|
512
|
-
}),
|
|
549
|
+
}), Z = me({ scope: "voltro:workflow" }), Ue = 34e3, Q = (e) => {
|
|
513
550
|
let t = e.runnerListenHost ?? process.env.POD_IP ?? process.env.VOLTRO_WORKFLOW_RUNNER_HOST ?? "localhost";
|
|
514
551
|
return {
|
|
515
552
|
host: t,
|
|
516
553
|
port: e.runnerListenPort ?? 34e3,
|
|
517
554
|
localhostRisk: e.runnerStorage === "sql" && (t === "localhost" || t === "127.0.0.1")
|
|
518
555
|
};
|
|
519
|
-
},
|
|
556
|
+
}, We = (e) => {
|
|
520
557
|
let t = e.sqlRunnerSupported ?? !e.sqliteFamily, n = t ? "sql" : "memory", r = e.override ?? process.env.VOLTRO_WORKFLOW_RUNNER_STORAGE;
|
|
521
558
|
if (r === void 0 || r.trim() === "") return n;
|
|
522
559
|
let i = r.trim().toLowerCase();
|
|
523
560
|
if (i !== "memory" && i !== "sql") throw Error(`VOLTRO_WORKFLOW_RUNNER_STORAGE must be 'memory' or 'sql', got '${r}'.`);
|
|
524
561
|
if (i === "sql" && !t) throw Error("VOLTRO_WORKFLOW_RUNNER_STORAGE='sql' is unsupported for this database connection. Use 'memory' for a local or embedded SQLite-family connection.");
|
|
525
562
|
return i;
|
|
526
|
-
},
|
|
563
|
+
}, $ = (e = process.env.VOLTRO_WORKFLOW_SHARD_LOCK) => {
|
|
527
564
|
if (e === void 0 || e.trim() === "") return "auto";
|
|
528
565
|
let t = e.trim().toLowerCase();
|
|
529
566
|
if (t === "auto" || t === "row" || t === "advisory") return t;
|
|
530
567
|
throw Error(`VOLTRO_WORKFLOW_SHARD_LOCK must be 'auto', 'row', or 'advisory', got '${e}'.`);
|
|
531
|
-
},
|
|
532
|
-
let e = yield* (yield*
|
|
568
|
+
}, Ge = p.gen(function* () {
|
|
569
|
+
let e = yield* (yield* b.SqlClient)`SELECT @@wsrep_on AS wsrep`.pipe(p.catchAll(() => p.succeed([])));
|
|
533
570
|
if (e.length === 0) return !1;
|
|
534
571
|
let t = e[0].wsrep;
|
|
535
572
|
return t === 1 || t === "1" || t === !0 || t === "ON" || t === "on";
|
|
536
|
-
}),
|
|
537
|
-
return e.runnerStorage === "memory" ? !1 : e.dialectId === "sqlite" || e.dialectId === "turso" || e.mode === "row" ? !0 : e.mode === "advisory" || e.dialectId !== "mysql" && e.dialectId !== "mariadb" ? !1 : yield*
|
|
538
|
-
}),
|
|
573
|
+
}), Ke = (e) => p.gen(function* () {
|
|
574
|
+
return e.runnerStorage === "memory" ? !1 : e.dialectId === "sqlite" || e.dialectId === "turso" || e.mode === "row" ? !0 : e.mode === "advisory" || e.dialectId !== "mysql" && e.dialectId !== "mariadb" ? !1 : yield* Ge;
|
|
575
|
+
}), qe = (e) => ({
|
|
539
576
|
coordination: e.disableAdvisory ? "row-based" : "advisory",
|
|
540
577
|
dialect: e.dialectId ?? "unknown",
|
|
541
578
|
mode: e.mode,
|
|
542
579
|
...e.disableAdvisory && e.mode === "auto" ? { reason: e.dialectId === "sqlite" || e.dialectId === "turso" ? "SQLite-family connection uses row locks" : "wsrep/Galera cluster detected" } : {}
|
|
543
|
-
}),
|
|
580
|
+
}), Je = () => {
|
|
544
581
|
let e = (e) => {
|
|
545
582
|
if (e === void 0) return;
|
|
546
583
|
let t = Number(e);
|
|
@@ -551,54 +588,59 @@ var ge = (e) => p.gen(function* () {
|
|
|
551
588
|
...n === void 0 ? {} : { failoverHeartbeatSeconds: n },
|
|
552
589
|
...r === void 0 ? {} : { messagePollSeconds: r }
|
|
553
590
|
};
|
|
554
|
-
},
|
|
555
|
-
let n =
|
|
591
|
+
}, Ye = p.flatMap(T.Sharding, (e) => e.pollStorage), Xe = /* @__PURE__ */ new Set(), Ze = (t) => {
|
|
592
|
+
let n = Q({
|
|
556
593
|
runnerStorage: t.runnerStorage,
|
|
557
594
|
runnerListenHost: t.runnerListenHost,
|
|
558
595
|
runnerListenPort: t.runnerListenPort
|
|
559
596
|
}), r = n.host, i = n.port;
|
|
560
|
-
n.localhostRisk &&
|
|
561
|
-
let a = t.dialectId === "sqlite" ? m.scoped(
|
|
562
|
-
let e = yield*
|
|
563
|
-
return t === void 0 ? yield* p.die(/* @__PURE__ */ Error("SQLite workflow connection has no main database")) : (t.file !== "" && (yield*
|
|
564
|
-
}).pipe(p.orDie)).pipe(m.provide(t.sqlClientLayer)) : t.sqlClientLayer, s =
|
|
597
|
+
n.localhostRisk && !Xe.has(r) && (Xe.add(r), Z.warn("runner host is not routable from other pods with SQL cluster storage — 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.", { runnerHost: r }));
|
|
598
|
+
let a = t.dialectId === "sqlite" ? m.scoped(b.SqlClient, p.gen(function* () {
|
|
599
|
+
let e = yield* b.SqlClient, t = (yield* e.unsafe("PRAGMA database_list")).find((e) => e.name === "main");
|
|
600
|
+
return t === void 0 ? yield* p.die(/* @__PURE__ */ Error("SQLite workflow connection has no main database")) : (t.file !== "" && (yield* Ve(t.file)), e);
|
|
601
|
+
}).pipe(p.orDie)).pipe(m.provide(t.sqlClientLayer)) : t.sqlClientLayer, s = K({
|
|
565
602
|
runnerStorage: t.runnerStorage,
|
|
566
603
|
charset: t.charset
|
|
567
|
-
}).pipe(m.provide(a)), c = t.shardLockMode ??
|
|
604
|
+
}).pipe(m.provide(a)), c = t.shardLockMode ?? $(), l = m.unwrapEffect(Ke({
|
|
568
605
|
dialectId: t.dialectId,
|
|
569
606
|
runnerStorage: t.runnerStorage,
|
|
570
607
|
mode: c
|
|
571
608
|
}).pipe(p.map((e) => {
|
|
572
|
-
t.runnerStorage === "sql" &&
|
|
609
|
+
t.runnerStorage === "sql" && Z.info("shard-lock coordination", qe({
|
|
573
610
|
disableAdvisory: e,
|
|
574
611
|
dialectId: t.dialectId,
|
|
575
612
|
mode: c
|
|
576
613
|
}));
|
|
577
|
-
let n = m.effect(o, p.flatMap(
|
|
578
|
-
ownAddress:
|
|
614
|
+
let n = m.effect(o, p.flatMap(T.Sharding, (n) => X({
|
|
615
|
+
ownAddress: C.make(r, i).toString(),
|
|
579
616
|
shardLockMode: t.runnerStorage === "sql" ? e ? "row" : "advisory" : "none",
|
|
580
617
|
runnerStorage: t.runnerStorage,
|
|
581
618
|
dialectId: t.dialectId,
|
|
582
|
-
hasShard: (e) => n.hasShardId(
|
|
583
|
-
}))), a =
|
|
619
|
+
hasShard: (e) => n.hasShardId(se.ShardId.fromString(e))
|
|
620
|
+
}))), a = {
|
|
621
|
+
runnerAddress: h.some(C.make(r, i)),
|
|
622
|
+
runnerListenAddress: h.some(C.make(r, i)),
|
|
623
|
+
...e ? { shardLockDisableAdvisory: !0 } : {},
|
|
624
|
+
...t.failoverLeaseSeconds === void 0 ? {} : { shardLockExpiration: f.seconds(t.failoverLeaseSeconds) },
|
|
625
|
+
...t.failoverHeartbeatSeconds === void 0 ? {} : { shardLockRefreshInterval: f.seconds(t.failoverHeartbeatSeconds) },
|
|
626
|
+
...t.messagePollSeconds === void 0 ? {} : { entityMessagePollInterval: f.seconds(t.messagePollSeconds) }
|
|
627
|
+
}, s = f.seconds(t.failoverHeartbeatSeconds ?? 10), l = Be({
|
|
584
628
|
runnerStorage: t.runnerStorage,
|
|
585
|
-
shardingConfig:
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
...t.failoverHeartbeatSeconds === void 0 ? {} : { shardLockRefreshInterval: f.seconds(t.failoverHeartbeatSeconds) },
|
|
591
|
-
...t.messagePollSeconds === void 0 ? {} : { entityMessagePollInterval: f.seconds(t.messagePollSeconds) }
|
|
629
|
+
shardingConfig: a,
|
|
630
|
+
guard: {
|
|
631
|
+
interval: s,
|
|
632
|
+
giveUpAfter: f.times(s, 3),
|
|
633
|
+
log: Z
|
|
592
634
|
}
|
|
593
635
|
});
|
|
594
|
-
return m.mergeAll(n, m.effect(
|
|
595
|
-
}))).pipe(m.provide(s)), u = t.extraShardingLayers ?? [], g = u.length === 0 ?
|
|
636
|
+
return m.mergeAll(n, m.effect(j, ge), m.effect(L, Ee)).pipe(m.provideMerge(l));
|
|
637
|
+
}))).pipe(m.provide(s)), u = t.extraShardingLayers ?? [], g = u.length === 0 ? x.layer : m.mergeAll(x.layer, ...u), _ = l.pipe(m.map((t) => d.add(t, T.Sharding, e(d.get(t, T.Sharding)))));
|
|
596
638
|
return g.pipe(m.provideMerge(_));
|
|
597
|
-
},
|
|
639
|
+
}, Qe = (e) => ee.make({
|
|
598
640
|
name: e.name,
|
|
599
641
|
cron: e.cron,
|
|
600
642
|
execute: p.promise(() => e.execute()),
|
|
601
643
|
skipIfOlderThan: e.skipIfOlderThan ?? f.days(1)
|
|
602
644
|
});
|
|
603
645
|
//#endregion
|
|
604
|
-
export {
|
|
646
|
+
export { Se as _, Ye as a, We as c, X as d, K as f, xe as g, be as h, $ as i, Ke as l, M as m, Ue as n, Je as o, L as p, Ge as r, Q as s, Qe as t, Ze as u, j as v };
|