@voltro/runtime 0.85.0 → 0.87.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.
Files changed (184) hide show
  1. package/CHANGELOG.md +5987 -0
  2. package/README.md +43 -0
  3. package/THIRD-PARTY-NOTICES.md +864 -253
  4. package/dist/aggregate.js +42 -0
  5. package/dist/aggregateExtrema.js +80 -0
  6. package/dist/aggregateIvm.js +115 -0
  7. package/dist/analyticsMirror.js +228 -0
  8. package/dist/analyticsSink.js +32 -0
  9. package/dist/apiKeyUsage.js +33 -0
  10. package/dist/apiKeys.js +227 -0
  11. package/dist/apiKeysWiring.js +114 -0
  12. package/dist/apiRoleBucketSampler.js +64 -0
  13. package/dist/apiRoleWorkloadHistory.js +56 -0
  14. package/dist/apiRoleWorkloadMetrics.js +78 -0
  15. package/dist/apiRoleWorkloadStorage.js +95 -0
  16. package/dist/approvalBuiltins.js +10 -0
  17. package/dist/approvals.js +262 -0
  18. package/dist/authMiddlewareLayer.js +48 -0
  19. package/dist/bindEvent.js +92 -0
  20. package/dist/bootListener.js +143 -0
  21. package/dist/bootRefusal.js +7 -0
  22. package/dist/bootStoreCodec.js +71 -0
  23. package/dist/businessRules.js +67 -0
  24. package/dist/changeClaimsTable.js +12 -0
  25. package/dist/circuitBreaker.js +1 -0
  26. package/dist/columnOnUpdate.js +23 -0
  27. package/dist/composeAnalytics.js +129 -0
  28. package/dist/computedQuery.js +4 -0
  29. package/dist/computedQueryCache.js +231 -0
  30. package/dist/connectionBuiltins.js +81 -0
  31. package/dist/connectionCredentials.js +66 -0
  32. package/dist/connectionState.js +34 -0
  33. package/dist/connectionVault.js +410 -0
  34. package/dist/connectionsFacade.js +56 -0
  35. package/dist/coordinatedSchedule.js +145 -0
  36. package/dist/crdtHistory.js +138 -0
  37. package/dist/crdtMerge.js +179 -0
  38. package/dist/crdtVersionsTable.js +25 -0
  39. package/dist/credentialBound.js +12 -0
  40. package/dist/crud.js +72 -0
  41. package/dist/dataStore.js +712 -0
  42. package/dist/dataloader.js +61 -0
  43. package/dist/declaredTablesGuard.js +49 -0
  44. package/dist/defineExecutor.js +4 -0
  45. package/dist/dependencyGraph.js +32 -0
  46. package/dist/devices.js +228 -0
  47. package/dist/devicesWiring.js +168 -0
  48. package/dist/dialectReplicationAdapter.js +29 -0
  49. package/dist/dispatcher.js +522 -0
  50. package/dist/effectStore.js +71 -0
  51. package/dist/engineClients.js +24 -0
  52. package/dist/eventBus.js +260 -0
  53. package/dist/eventPublish.js +49 -0
  54. package/dist/events.js +193 -0
  55. package/dist/expectation.js +52 -0
  56. package/dist/expectationEvaluator.js +308 -0
  57. package/dist/experiment.js +96 -0
  58. package/dist/experimentEvaluator.js +245 -0
  59. package/dist/externalizedTasks.js +38 -0
  60. package/dist/fieldCipher.js +130 -0
  61. package/dist/fieldKeyRegistry.js +295 -0
  62. package/dist/fieldKeyRekey.js +286 -0
  63. package/dist/fieldKeyScan.js +136 -0
  64. package/dist/fieldKeyring.js +77 -0
  65. package/dist/fingerprint.js +21 -0
  66. package/dist/finops.js +42 -0
  67. package/dist/finopsAccountant.js +216 -0
  68. package/dist/handlerBody.js +8 -0
  69. package/dist/healthProbes.js +55 -0
  70. package/dist/hot.d.ts +56 -0
  71. package/dist/hot.js +3 -0
  72. package/dist/hotDataStore.js +148 -0
  73. package/dist/hotDataStoreSnapshot.js +98 -0
  74. package/dist/hotDataStoreTables.js +18 -0
  75. package/dist/http-range.d.ts +53 -0
  76. package/dist/http-range.js +52 -0
  77. package/dist/httpClientSsrf.js +24 -0
  78. package/dist/httpClientTracing.js +14 -0
  79. package/dist/httpCompression.js +76 -0
  80. package/dist/httpInputStream.js +82 -0
  81. package/dist/httpKeepAlive.js +12 -0
  82. package/dist/httpRouteMounts.js +48 -0
  83. package/dist/idempotencyStore.js +110 -0
  84. package/dist/idleDetector.js +21 -0
  85. package/dist/index.d.ts +1249 -517
  86. package/dist/index.js +155 -15292
  87. package/dist/insertRequiredColumns.js +19 -0
  88. package/dist/inspectWorkflow.js +1 -0
  89. package/dist/instanceMembership.js +98 -0
  90. package/dist/ivm.js +125 -0
  91. package/dist/kvDataStore.js +86 -0
  92. package/dist/kvFacade.js +38 -0
  93. package/dist/matcher.js +304 -0
  94. package/dist/memoryColumnDefaults.js +11 -0
  95. package/dist/memoryQueryJoins.js +114 -0
  96. package/dist/memorySetOperations.js +43 -0
  97. package/dist/metrics.js +175 -0
  98. package/dist/observability.js +123 -0
  99. package/dist/observedGraph.js +52 -0
  100. package/dist/originGuard.js +116 -0
  101. package/dist/outbox.js +352 -0
  102. package/dist/pluginRefRules.js +65 -0
  103. package/dist/policyGuardResolver.js +55 -0
  104. package/dist/preview.js +62 -0
  105. package/dist/projectedRows.js +12 -0
  106. package/dist/provenance.js +63 -0
  107. package/dist/rangeIndex.js +89 -0
  108. package/dist/rawReadScope.js +17 -0
  109. package/dist/reaction.js +85 -0
  110. package/dist/reactiveConfig.js +11 -0
  111. package/dist/readClassification.js +57 -0
  112. package/dist/readRecorder.js +67 -0
  113. package/dist/readScopePredicate.js +12 -0
  114. package/dist/rebac.js +104 -0
  115. package/dist/rebacInterceptor.js +81 -0
  116. package/dist/registry.js +29 -0
  117. package/dist/relevantFields.js +65 -0
  118. package/dist/replicaHealth.js +42 -0
  119. package/dist/replicaLocality.js +44 -0
  120. package/dist/replicaObservations.js +121 -0
  121. package/dist/replicaSelection.js +32 -0
  122. package/dist/replicatedDataStore.js +257 -0
  123. package/dist/requestLoader.js +7 -0
  124. package/dist/resumeCensus.js +28 -0
  125. package/dist/revalidation.js +22 -0
  126. package/dist/rowFilter.js +82 -0
  127. package/dist/rowStateBytes.js +45 -0
  128. package/dist/rpcBodyCap.js +63 -0
  129. package/dist/rpcConnections.js +83 -0
  130. package/dist/rpcDefectDiagnostics.js +15 -0
  131. package/dist/rpcServer.js +1065 -0
  132. package/dist/rpcWorkloadAdmission.js +50 -0
  133. package/dist/runAsSystem.js +23 -0
  134. package/dist/rywPositionStore.js +119 -0
  135. package/dist/schedule.js +23 -0
  136. package/dist/scheduleBackfillRequests.js +151 -0
  137. package/dist/scheduleClaimsTable.js +11 -0
  138. package/dist/scheduleRunsTable.js +37 -0
  139. package/dist/scheduler.js +840 -0
  140. package/dist/schemaRegistry.js +65 -0
  141. package/dist/secretsResolver.js +35 -0
  142. package/dist/securityHeaders.js +33 -0
  143. package/dist/servePipeline.js +374 -0
  144. package/dist/serverOnlyAudit.js +66 -0
  145. package/dist/serverTracer.js +15 -0
  146. package/dist/serviceCredentials.js +40 -0
  147. package/dist/setReadScopes.js +28 -0
  148. package/dist/singleNodeOrchestrator.js +91 -0
  149. package/dist/snapshotValueCodec.js +46 -0
  150. package/dist/sourceResolution.js +52 -0
  151. package/dist/storeMiddleware.js +882 -0
  152. package/dist/storeQuery.js +204 -0
  153. package/dist/storeRead.js +10 -0
  154. package/dist/subscriber.js +8 -0
  155. package/dist/subscriptionOutbox.js +139 -0
  156. package/dist/subscriptionRegistry.js +82 -0
  157. package/dist/subscriptionResume.js +132 -0
  158. package/dist/subscriptionSocketMetrics.js +65 -0
  159. package/dist/systemSubject.js +5 -0
  160. package/dist/tenantScope.js +11 -0
  161. package/dist/tenantSelection.js +27 -0
  162. package/dist/timeline.js +105 -0
  163. package/dist/traceContext.js +5 -0
  164. package/dist/traceSink.js +63 -0
  165. package/dist/traceSpansTable.js +29 -0
  166. package/dist/traceparent.js +23 -0
  167. package/dist/transactionSubject.js +12 -0
  168. package/dist/trustedProxy.js +155 -0
  169. package/dist/undo.js +129 -0
  170. package/dist/undoBuiltins.js +69 -0
  171. package/dist/undoCapture.js +74 -0
  172. package/dist/undoLogTable.js +16 -0
  173. package/dist/upsertBinding.js +20 -0
  174. package/dist/wakeOrchestrator.js +34 -0
  175. package/dist/wakeRegistry.js +71 -0
  176. package/dist/wakeRouter.js +63 -0
  177. package/dist/wakeSupervisor.js +62 -0
  178. package/dist/wakeupsTable.js +12 -0
  179. package/dist/webSocketGatewayRuntime.js +154 -0
  180. package/dist/webhookVerification.js +18 -0
  181. package/dist/wireColumnStrip.js +84 -0
  182. package/dist/workflowReadBuiltins.js +87 -0
  183. package/dist/workflowRuntime.js +708 -0
  184. package/package.json +19 -7
@@ -0,0 +1,50 @@
1
+ import { Context as e, Option as t } from "effect";
2
+ import { EventRpcWorkload as n } from "@voltro/protocol";
3
+ import { RpcGroup as r, RpcSchema as i } from "effect/rpc";
4
+ //#region src/rpcWorkloadAdmission.ts
5
+ var a = (e) => {
6
+ if (e === void 0) return;
7
+ if (!e || typeof e != "object" || Array.isArray(e) || ![Object.prototype, null].includes(Object.getPrototypeOf(e))) throw Error("Invalid RPC workload admission");
8
+ let t = /* @__PURE__ */ new Map();
9
+ for (let n of Reflect.ownKeys(e)) {
10
+ let r = Object.getOwnPropertyDescriptor(e, n);
11
+ if (typeof n != "string" || ![
12
+ "rpc",
13
+ "subscriptions",
14
+ "events",
15
+ "routes"
16
+ ].includes(n) || !r.enumerable || !("value" in r)) throw Error("RPC workload admission requires own data");
17
+ t.set(n, r.value);
18
+ }
19
+ let n = t.get("rpc"), r = t.get("subscriptions"), i = t.get("routes");
20
+ if (typeof n != "boolean" || typeof r != "boolean") throw Error("RPC workload admission requires explicit modes");
21
+ let a = t.get("events");
22
+ if (a !== void 0 && typeof a != "boolean") throw Error("Invalid event workload admission");
23
+ let o;
24
+ if (i !== void 0) {
25
+ if (!Array.isArray(i) || i.length > 4096 || Reflect.ownKeys(i).length !== i.length + 1) throw Error("Invalid RPC workload route filters");
26
+ let e = [];
27
+ for (let t = 0; t < i.length; t++) {
28
+ let n = Object.getOwnPropertyDescriptor(i, String(t));
29
+ if (!n?.enumerable || !("value" in n) || typeof n.value != "string" || n.value.length > 191 || !/^(?:[A-Za-z][A-Za-z0-9_-]*\.)*[A-Za-z][A-Za-z0-9_-]*(?:\.\*)?$/.test(n.value)) throw Error("Invalid RPC workload tag filter");
30
+ e.push(n.value);
31
+ }
32
+ if (new Set(e).size !== e.length) throw Error("Duplicate RPC workload tag filter");
33
+ o = Object.freeze(e);
34
+ }
35
+ return Object.freeze({
36
+ rpc: n,
37
+ subscriptions: r,
38
+ ...a === void 0 ? {} : { events: a },
39
+ ...o === void 0 ? {} : { routes: o }
40
+ });
41
+ }, o = (a, o, s) => {
42
+ if (!o) return a;
43
+ let c = [...a.requests.values()].filter((r) => {
44
+ let a = r, c = t.isSome(e.getOption(a.annotations, n)), l = o.events ?? o.subscriptions;
45
+ return (c ? l && (s === "websocket" || o.rpc) : s === "http" ? o.rpc : i.isStreamSchema(a.successSchema) ? o.subscriptions : o.rpc) && (o.routes === void 0 || o.routes.some((e) => e.endsWith(".*") ? r._tag.startsWith(e.slice(0, -1)) : r._tag === e));
46
+ });
47
+ return r.make(...c).annotateMerge(a.annotations);
48
+ };
49
+ //#endregion
50
+ export { a as captureRpcWorkloadAdmission, o as selectRpcWorkloadGroup };
@@ -0,0 +1,23 @@
1
+ import { NO_ROW_FILTER as e } from "./rowFilter.js";
2
+ import { wrapStoreWithMixinBehaviour as t } from "./storeMiddleware.js";
3
+ import { systemSubject as n } from "@voltro/protocol";
4
+ //#region src/runAsSystem.ts
5
+ var r, i = (e) => {
6
+ r = e;
7
+ }, a = () => {
8
+ r = void 0;
9
+ }, o = () => r, s = async (i, a = {}) => {
10
+ let o = a.scopes === void 0 ? n(a.id ?? "system") : n(a.id ?? "system", a.scopes), s = a.store ?? r?.dataStore, c = a.schemaRegistry ?? r?.schemaRegistry, l = a.dialect ?? r?.dialect;
11
+ if (s === void 0 || c === void 0) throw Error("runAsSystem: no data store available — register one at boot via setSystemStoreHandle({ dataStore, schemaRegistry }) or pass { store, schemaRegistry } in the options.");
12
+ return i({
13
+ subject: o,
14
+ store: t(s, {
15
+ subject: o,
16
+ schemaRegistry: c,
17
+ ...l === void 0 ? {} : { dialect: l },
18
+ rowFilter: e
19
+ })
20
+ });
21
+ };
22
+ //#endregion
23
+ export { a as clearSystemStoreHandle, o as getSystemStoreHandle, s as runAsSystem, i as setSystemStoreHandle };
@@ -0,0 +1,119 @@
1
+ import { Effect as e } from "effect";
2
+ import { createLogger as t } from "@voltro/logger";
3
+ //#region src/rywPositionStore.ts
4
+ var n = t({ scope: "voltro:ryw" }), r = (e = {}) => {
5
+ let t = e.now ?? Date.now, n = /* @__PURE__ */ new Map();
6
+ return {
7
+ get(e) {
8
+ let r = n.get(e);
9
+ if (r !== void 0) {
10
+ if (r.expiresAt <= t()) n.delete(e);
11
+ else return r;
12
+ }
13
+ },
14
+ set(e, t) {
15
+ n.set(e, t);
16
+ },
17
+ delete(e) {
18
+ n.delete(e);
19
+ },
20
+ size() {
21
+ return n.size;
22
+ }
23
+ };
24
+ }, i = (e, t) => e === null ? t === void 0 ? "anon:bg" : `anon:${t}` : `subject:${e}`, a = (t) => e.gen(function* () {
25
+ let r = yield* e.context(), i = e.runPromiseWith(r), a = t.now ?? Date.now, s = t.keyPrefix.trim();
26
+ if (s.length === 0) throw Error("RYW keyPrefix must name an application/environment namespace");
27
+ let c = t.channel ?? `${s}:bus`;
28
+ if (c.trim().length === 0) throw Error("RYW channel must not be empty");
29
+ let l = t.onError ?? ((e, t) => {
30
+ n.warn("redis operation failed", { op: t });
31
+ }), u = (e, t) => {
32
+ try {
33
+ l(e, t);
34
+ } catch {
35
+ n.warn("redis error observer failed", { op: t });
36
+ }
37
+ }, d = `${process.pid}:${Math.random()}:${a()}`, f = /* @__PURE__ */ new Map(), p = !1, m = Promise.resolve(), h, g = yield* t.pubsub.subscribe(c, (e) => {
38
+ if (!p) try {
39
+ let t = o(e);
40
+ if (t.senderId === d) return;
41
+ t.op === "delete" ? f.delete(t.subjectKey) : t.expiresAt > a() ? f.set(t.subjectKey, {
42
+ position: t.position,
43
+ expiresAt: t.expiresAt
44
+ }) : f.delete(t.subjectKey);
45
+ } catch (e) {
46
+ u(e, "parse");
47
+ }
48
+ }).pipe(e.onError(() => t.pubsub.close)), _ = (e, n) => {
49
+ m = m.then(async () => {
50
+ try {
51
+ await n(), await i(t.pubsub.publish(c, JSON.stringify(e)));
52
+ } catch (t) {
53
+ u(t, e.op);
54
+ }
55
+ });
56
+ }, v = () => {
57
+ if (p) throw Error("RYW consumer is closed");
58
+ };
59
+ return {
60
+ get(e) {
61
+ let t = f.get(e);
62
+ if (t !== void 0) {
63
+ if (t.expiresAt <= a()) f.delete(e);
64
+ else return t;
65
+ }
66
+ },
67
+ set(e, n) {
68
+ v();
69
+ let r = { ...n };
70
+ f.set(e, r), _({
71
+ senderId: d,
72
+ op: "set",
73
+ subjectKey: e,
74
+ ...r
75
+ }, () => {
76
+ let n = r.expiresAt - a();
77
+ return n > 0 ? t.client.psetex(`${s}:${e}`, n, JSON.stringify(r)) : t.client.del([`${s}:${e}`]);
78
+ });
79
+ },
80
+ delete(e) {
81
+ v(), f.delete(e), _({
82
+ senderId: d,
83
+ op: "delete",
84
+ subjectKey: e
85
+ }, () => t.client.del([`${s}:${e}`]));
86
+ },
87
+ size: () => f.size,
88
+ close: () => h ??= (async () => {
89
+ p = !0, await m;
90
+ try {
91
+ await i(g);
92
+ } finally {
93
+ try {
94
+ await i(t.pubsub.close);
95
+ } finally {
96
+ f.clear();
97
+ }
98
+ }
99
+ })()
100
+ };
101
+ }), o = (e) => {
102
+ let t = JSON.parse(e);
103
+ if (typeof t != "object" || !t || !("senderId" in t) || typeof t.senderId != "string" || !("subjectKey" in t) || typeof t.subjectKey != "string" || !("op" in t) || t.op !== "set" && t.op !== "delete") throw Error("Invalid RYW message");
104
+ if (t.op === "delete") return {
105
+ senderId: t.senderId,
106
+ subjectKey: t.subjectKey,
107
+ op: "delete"
108
+ };
109
+ if (!("position" in t) || typeof t.position != "string" || !("expiresAt" in t) || typeof t.expiresAt != "number" || !Number.isFinite(t.expiresAt)) throw Error("Invalid RYW position");
110
+ return {
111
+ senderId: t.senderId,
112
+ subjectKey: t.subjectKey,
113
+ op: "set",
114
+ position: t.position,
115
+ expiresAt: t.expiresAt
116
+ };
117
+ };
118
+ //#endregion
119
+ export { i as buildSubjectKey, r as memoryRywPositionStore, a as redisRywPositionStore };
@@ -0,0 +1,23 @@
1
+ import { Cron as e, Result as t } from "effect";
2
+ //#region src/schedule.ts
3
+ var n = Symbol.for("@voltro/ScheduleDefinition"), r = (e) => typeof e == "object" && !!e && n in e, i = 18e5, a = (r) => {
4
+ let i = e.parse(r.cron, r.timezone);
5
+ if (t.isFailure(i)) throw Error(`defineSchedule("${r.name}"): invalid cron expression "${r.cron}" (tz="${r.timezone}") — ${String(i.failure)}`);
6
+ if (!r.timezone || r.timezone.trim() === "") throw Error(`defineSchedule("${r.name}"): timezone is required (e.g. "UTC", "Europe/Berlin").\nThe field is non-optional in \`ScheduleDefinition\`, so an ABSENT one is a tsc error and never reaches here. Arriving at this message means the value was present and EMPTY — almost always \`process.env.TZ ?? ''\` or a config lookup that missed. A reporter lost a crashloop to it, so the distinction is worth stating: this is not a check the type could have made for you.`);
7
+ if (r.trigger === "external" && r.cron.trim().split(/\s+/).length === 6) throw Error(`defineSchedule("${r.name}"): a six-field (seconds) cron "${r.cron}" cannot be driven by an external scheduler — k8s CronJob, EventBridge and Cloud Scheduler are all minute-resolution.
8
+ Use a five-field expression, or leave this schedule on trigger: 'self' (the in-app timer has no such limit).`);
9
+ if (r.handler === void 0 && r.workflow === void 0) throw Error(`defineSchedule("${r.name}"): define either handler or workflow`);
10
+ return {
11
+ ...r,
12
+ onOverlap: r.onOverlap ?? "skip",
13
+ backfill: r.backfill ?? "skip",
14
+ maxRuntimeMs: r.maxRuntimeMs ?? 18e5,
15
+ [n]: !0
16
+ };
17
+ }, o = (t) => e.parseUnsafe(t.cron, t.timezone), s = (t, n = /* @__PURE__ */ new Date()) => e.next(o(t), n), c = (t, n, r, i) => {
18
+ let a = o(t), s = [], c = e.next(a, n);
19
+ for (; c.getTime() <= r.getTime() && s.length <= i;) s.push(c), c = e.next(a, c);
20
+ return s;
21
+ };
22
+ //#endregion
23
+ export { i as DEFAULT_MAX_RUNTIME_MS, o as compileCron, c as cronOccurrences, a as defineSchedule, r as isScheduleDefinition, s as nextFiring };
@@ -0,0 +1,151 @@
1
+ import { Schema as e } from "effect";
2
+ import { and as t, eq as n, id as r, integer as i, isNull as a, json as o, lt as s, table as c, text as l, timestamp as u } from "@voltro/database";
3
+ import { ScheduleBackfillRequestConflict as d } from "@voltro/protocol/inspect";
4
+ //#region src/scheduleBackfillRequests.ts
5
+ var f = 5e3, p = 3e4, m = "_voltro_schedule_backfill_requests", h = c(m, {
6
+ id: r({ prefix: "schbackfill" }),
7
+ name: l(),
8
+ from: u(),
9
+ to: u(),
10
+ occurrences: o(),
11
+ status: l().oneOf([
12
+ "queued",
13
+ "running",
14
+ "completed",
15
+ "interrupted"
16
+ ]),
17
+ owner: l().nullable(),
18
+ createdAt: u(),
19
+ updatedAt: u(),
20
+ cursor: i(),
21
+ activeIndex: i().nullable(),
22
+ results: o(),
23
+ error: l().nullable()
24
+ }), g = e.String.pipe(e.check(e.isMinLength(1)), e.check(e.isMaxLength(256))), _ = e.Struct({
25
+ runId: g,
26
+ status: e.Literals([
27
+ "succeeded",
28
+ "failed",
29
+ "skipped",
30
+ "missed"
31
+ ])
32
+ }), v = e.Struct({
33
+ id: g,
34
+ name: g,
35
+ from: e.Date,
36
+ to: e.Date,
37
+ occurrences: e.Array(e.Number.pipe(e.check(e.isFinite()))),
38
+ status: e.Literals([
39
+ "queued",
40
+ "running",
41
+ "completed",
42
+ "interrupted"
43
+ ]),
44
+ owner: e.NullOr(g),
45
+ createdAt: e.Date,
46
+ updatedAt: e.Date,
47
+ cursor: e.Number.pipe(e.check(e.isInt()), e.check(e.isGreaterThanOrEqualTo(0))),
48
+ activeIndex: e.NullOr(e.Number.pipe(e.check(e.isInt()), e.check(e.isGreaterThanOrEqualTo(0)))),
49
+ results: e.Array(_),
50
+ error: e.NullOr(e.String)
51
+ }), y = async (t, r) => {
52
+ let i = await t.query({
53
+ table: m,
54
+ predicate: n("id", r),
55
+ order: [],
56
+ take: 1,
57
+ skip: void 0,
58
+ projection: void 0
59
+ });
60
+ return i[0] === void 0 ? void 0 : e.decodeUnknownSync(v)(i[0]);
61
+ }, b = async (t, n) => {
62
+ let r = n.from.getTime(), i = n.to.getTime();
63
+ if (!Number.isFinite(r) || !Number.isFinite(i) || r >= i) throw Error("Invalid backfill range");
64
+ if (n.occurrences.length > 1e4) throw Error("Backfill occurrence limit exceeded");
65
+ let a = r, o = n.occurrences.map((e) => {
66
+ let t = e.getTime();
67
+ if (!Number.isFinite(t) || t <= a || t > i) throw Error("Backfill occurrences must be ordered within (from, to]");
68
+ return a = t, t;
69
+ }), s = e.decodeUnknownSync(v)({
70
+ ...n,
71
+ occurrences: o,
72
+ status: "queued",
73
+ owner: null,
74
+ createdAt: /* @__PURE__ */ new Date(),
75
+ updatedAt: /* @__PURE__ */ new Date(),
76
+ cursor: 0,
77
+ activeIndex: null,
78
+ results: [],
79
+ error: null
80
+ }), c = await t.insertIgnoreWithOutcome(m, s, { conflictColumns: ["id"] }), l = e.decodeUnknownSync(v)(c.row);
81
+ if (l.name !== s.name || l.from.getTime() !== r || l.to.getTime() !== i) throw new d("Backfill request ID already belongs to a different schedule or range");
82
+ return {
83
+ request: l,
84
+ created: c.outcome === "inserted"
85
+ };
86
+ }, x = async (r, i, a) => (e.decodeUnknownSync(g)(a), r.transactional(async (e) => await e.updateMany(m, {
87
+ status: "running",
88
+ owner: a,
89
+ updatedAt: /* @__PURE__ */ new Date()
90
+ }, { where: t(n("id", i), n("status", "queued")) }) === 1)), S = async (r, i, o, s) => {
91
+ if (!await x(r, i, o)) return y(r, i);
92
+ let c = await y(r, i);
93
+ if (!c) throw Error("Accepted backfill request disappeared");
94
+ let l = c, u = () => t(n("id", i), n("status", "running"), n("owner", o)), d, p, h = !1, g, v = () => {
95
+ d = setTimeout(() => {
96
+ p = r.transactional(async (e) => {
97
+ if (await e.updateMany("_voltro_schedule_backfill_requests", { updatedAt: /* @__PURE__ */ new Date() }, { where: u() }) !== 1) throw Error("Backfill ownership was lost");
98
+ }).then(() => {
99
+ h || v();
100
+ }, (e) => {
101
+ g = e;
102
+ });
103
+ }, f), d.unref?.();
104
+ };
105
+ v();
106
+ try {
107
+ for (let o = l.cursor; o < l.occurrences.length; o++) {
108
+ if (g !== void 0) throw g;
109
+ if (await r.transactional(async (e) => e.updateMany("_voltro_schedule_backfill_requests", {
110
+ activeIndex: o,
111
+ updatedAt: /* @__PURE__ */ new Date()
112
+ }, { where: t(u(), n("cursor", o), a("activeIndex")) })) !== 1) return y(r, i);
113
+ let c = e.decodeUnknownSync(_)(await s(new Date(l.occurrences[o]))), d = [...l.results, c];
114
+ if (await r.transactional(async (e) => e.updateMany("_voltro_schedule_backfill_requests", {
115
+ cursor: o + 1,
116
+ activeIndex: null,
117
+ results: d,
118
+ updatedAt: /* @__PURE__ */ new Date()
119
+ }, { where: t(u(), n("cursor", o), n("activeIndex", o)) })) !== 1) return y(r, i);
120
+ l = {
121
+ ...l,
122
+ cursor: o + 1,
123
+ activeIndex: null,
124
+ results: d
125
+ };
126
+ }
127
+ await r.transactional(async (e) => e.updateMany(m, {
128
+ status: "completed",
129
+ updatedAt: /* @__PURE__ */ new Date()
130
+ }, { where: t(u(), n("cursor", l.occurrences.length), a("activeIndex")) }));
131
+ } catch (e) {
132
+ await r.transactional(async (t) => t.updateMany(m, {
133
+ status: "interrupted",
134
+ error: e instanceof Error ? e.message : String(e),
135
+ updatedAt: /* @__PURE__ */ new Date()
136
+ }, { where: u() }));
137
+ } finally {
138
+ h = !0, clearTimeout(d), await p;
139
+ }
140
+ return y(r, i);
141
+ }, C = (e, r = /* @__PURE__ */ new Date()) => e.transactional((e) => e.updateMany(m, {
142
+ status: "interrupted",
143
+ error: "Backfill owner stopped reporting; check the active run before starting another range",
144
+ updatedAt: r
145
+ }, { where: t(n("status", "running"), s("updatedAt", /* @__PURE__ */ new Date(r.getTime() - p))) })), w = (e, r) => e.transactional((e) => e.updateMany(m, {
146
+ status: "interrupted",
147
+ error: "Scheduler stopped before the backfill settled",
148
+ updatedAt: /* @__PURE__ */ new Date()
149
+ }, { where: t(n("status", "running"), n("owner", r)) }));
150
+ //#endregion
151
+ export { f as SCHEDULE_BACKFILL_HEARTBEAT_MS, m as SCHEDULE_BACKFILL_REQUESTS_TABLE, p as SCHEDULE_BACKFILL_STALE_MS, b as acceptScheduleBackfillRequest, x as claimScheduleBackfillRequest, S as executeScheduleBackfillRequest, w as interruptOwnedScheduleBackfills, C as interruptStaleScheduleBackfills, y as readScheduleBackfillRequest, h as scheduleBackfillRequestsTable };
@@ -0,0 +1,11 @@
1
+ import { id as e, table as t, text as n, timestamp as r } from "@voltro/database";
2
+ //#region src/scheduleClaimsTable.ts
3
+ var i = t("_voltro_schedule_claims", {
4
+ id: e({ prefix: "schclaim" }),
5
+ scheduleName: n(),
6
+ bucket: n(),
7
+ claimedBy: n(),
8
+ claimedAt: r()
9
+ }).index(["scheduleName", "claimedAt"]).index(["claimedAt"]);
10
+ //#endregion
11
+ export { i as _voltroScheduleClaimsTable };
@@ -0,0 +1,37 @@
1
+ import { id as e, integer as t, json as n, table as r, text as i, timestamp as a } from "@voltro/database";
2
+ //#region src/scheduleRunsTable.ts
3
+ var o = r("_voltro_schedule_runs", {
4
+ id: e({ prefix: "schrun" }),
5
+ name: i(),
6
+ status: i().oneOf([
7
+ "running",
8
+ "queued",
9
+ "succeeded",
10
+ "failed",
11
+ "skipped",
12
+ "missed"
13
+ ]).default("running"),
14
+ trigger: i().oneOf([
15
+ "self",
16
+ "external",
17
+ "manual"
18
+ ]).default("self"),
19
+ scheduledAt: a(),
20
+ firedAt: a().nullable(),
21
+ completedAt: a().nullable(),
22
+ durationMs: t().nullable(),
23
+ errorTag: i().nullable(),
24
+ errorMessage: i().nullable(),
25
+ coordinationOutcome: i().oneOf([
26
+ "single",
27
+ "wonLock",
28
+ "lostLock",
29
+ "external",
30
+ "cluster"
31
+ ]).nullable(),
32
+ replicaId: i().nullable(),
33
+ heartbeatAt: a().nullable(),
34
+ result: n().nullable()
35
+ }).index(["name", "scheduledAt"]).index(["status"]);
36
+ //#endregion
37
+ export { o as _voltroScheduleRunsTable };