@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,42 @@
1
+ import { classifyShape as e } from "./ivm.js";
2
+ import { Context as t, Effect as n, Schema as r } from "effect";
3
+ //#region src/aggregate.ts
4
+ var i = (t) => {
5
+ let n = typeof t.refresh == "string" ? { interval: t.refresh } : t.refresh, r = t.strategy ?? "replace";
6
+ if (r === "merge" && (t.mergeKey === void 0 || t.mergeKey.length === 0)) throw Error(`defineAggregate("${t.name}"): strategy: 'merge' requires mergeKey — the property on each row to upsert by`);
7
+ if (t.incremental !== void 0) {
8
+ let n = e({
9
+ aggregations: [{
10
+ op: t.incremental.op,
11
+ ...t.incremental.column === void 0 ? {} : { column: t.incremental.column }
12
+ }],
13
+ ...t.incremental.groupBy === void 0 ? {} : { groupBy: t.incremental.groupBy }
14
+ });
15
+ if (!n.maintainable) throw Error(`defineAggregate("${t.name}"): incremental shape is not maintainable — ${n.reason}. Drop \`incremental\` to use the scheduled full-recompute path.`);
16
+ if (t.incremental.source.length === 0) throw Error(`defineAggregate("${t.name}"): incremental.source (the CDC table) is required`);
17
+ }
18
+ return {
19
+ _voltroAggregate: !0,
20
+ name: t.name,
21
+ refresh: n,
22
+ output: t.output,
23
+ indexes: t.indexes ?? [],
24
+ bootRefresh: t.bootRefresh ?? "persistent",
25
+ strategy: r,
26
+ ...t.mergeKey === void 0 ? {} : { mergeKey: t.mergeKey },
27
+ build: t.build,
28
+ ...t.incremental === void 0 ? {} : { incremental: t.incremental }
29
+ };
30
+ }, a = (e) => typeof e == "object" && !!e && e._voltroAggregate === !0, o = class extends r.TaggedError()("AggregateStale", {
31
+ name: r.String,
32
+ ageMs: r.Number,
33
+ maxAgeMs: r.Number
34
+ }) {}, s = class extends r.TaggedError()("AggregateNotFound", { name: r.String }) {}, c = class extends r.TaggedError()("AggregateError", {
35
+ name: r.String,
36
+ operation: r.String,
37
+ cause: r.String
38
+ }) {}, l = class extends t.Service()("@voltro/AggregateRegistry") {}, u = (e) => n.gen(function* () {
39
+ return (yield* l).getHandle(e);
40
+ });
41
+ //#endregion
42
+ export { c as AggregateError, s as AggregateNotFound, l as AggregateRegistry, o as AggregateStale, i as defineAggregate, a as isAggregateDefinition, u as useAggregate };
@@ -0,0 +1,80 @@
1
+ import { RangeIndex as e } from "./rangeIndex.js";
2
+ import { groupKeyOf as t } from "./ivm.js";
3
+ //#region src/aggregateExtrema.ts
4
+ var n = Symbol("unavailable"), r = (e, r) => {
5
+ let i = Object.getPrototypeOf(r);
6
+ if (i !== Object.prototype && i !== null) return;
7
+ let a = (e) => {
8
+ let t = Object.getOwnPropertyDescriptor(r, e);
9
+ return t === void 0 ? e in r ? n : void 0 : "value" in t ? t.value : n;
10
+ }, o = a("id");
11
+ if (typeof o != "string" && (typeof o != "number" || !Number.isFinite(o))) return;
12
+ let s = e.column === void 0 ? 0 : a(e.column);
13
+ if (s === n || typeof s == "object" && s || typeof s == "function" || typeof s == "symbol") return;
14
+ let c = Number(s ?? 0);
15
+ if (!Number.isFinite(c) || Object.is(c, -0)) return;
16
+ for (let t of e.groupBy ?? []) {
17
+ let e = a(t);
18
+ if (e === n || typeof e == "object" && e || typeof e == "function" || typeof e == "symbol" || typeof e == "bigint") return;
19
+ }
20
+ let l = /* @__PURE__ */ new Set([
21
+ "id",
22
+ ...e.column === void 0 ? [] : [e.column],
23
+ ...e.groupBy ?? []
24
+ ]), u = Object.fromEntries([...l].map((e) => [e, a(e)]));
25
+ return {
26
+ id: `${typeof o}:${String(o)}`,
27
+ key: t(r, e.groupBy),
28
+ value: c,
29
+ image: u
30
+ };
31
+ }, i = class t {
32
+ shape;
33
+ static capacity = 1e5;
34
+ rows = /* @__PURE__ */ new Map();
35
+ groups = /* @__PURE__ */ new Map();
36
+ constructor(e) {
37
+ this.shape = e;
38
+ }
39
+ static seed(e, n) {
40
+ if (e.op !== "min" && e.op !== "max" || n.length > t.capacity) return;
41
+ let i = new t(e);
42
+ for (let t of n) {
43
+ let n = r(e, t);
44
+ if (n === void 0 || i.rows.has(n.id)) return;
45
+ i.add(n);
46
+ }
47
+ return i;
48
+ }
49
+ add(t) {
50
+ let n = this.groups.get(t.key);
51
+ n === void 0 && (n = new e(), this.groups.set(t.key, n)), n.add(t.id, t.value), this.rows.set(t.id, t);
52
+ }
53
+ withPreviousRow(e) {
54
+ if (e.op !== "update" || e.old != null || e.new == null) return e;
55
+ let t = r(this.shape, e.new), n = t === void 0 ? void 0 : this.rows.get(t.id);
56
+ return n === void 0 ? e : {
57
+ ...e,
58
+ old: n.image
59
+ };
60
+ }
61
+ apply(e) {
62
+ let n = e.op === "insert" ? void 0 : e.old, i = e.op === "delete" ? void 0 : e.new, a = n == null ? void 0 : r(this.shape, n), o = i == null ? void 0 : r(this.shape, i);
63
+ if (n != null && a === void 0 || i != null && o === void 0) return !1;
64
+ if (a !== void 0) {
65
+ let e = this.rows.get(a.id);
66
+ if (e === void 0 || e.key !== a.key || e.value !== a.value) return !1;
67
+ }
68
+ if (o !== void 0 && o.id !== a?.id && this.rows.has(o.id) || this.rows.size - (a === void 0 ? 0 : 1) + (o === void 0 ? 0 : 1) > t.capacity) return !1;
69
+ if (a !== void 0) {
70
+ let e = this.groups.get(a.key);
71
+ e.delete(a.id), e.size === 0 && this.groups.delete(a.key), this.rows.delete(a.id);
72
+ }
73
+ return o !== void 0 && this.add(o), !0;
74
+ }
75
+ value(e) {
76
+ return this.groups.get(e)?.numericExtreme(this.shape.op);
77
+ }
78
+ };
79
+ //#endregion
80
+ export { i as AggregateExtrema };
@@ -0,0 +1,115 @@
1
+ import { applyDeltaInto as e, buildIvmState as t, groupKeyOf as n, ivmValue as r, materializeIvm as i, resolveRescanInto as a } from "./ivm.js";
2
+ import { AggregateExtrema as o } from "./aggregateExtrema.js";
3
+ //#region src/aggregateIvm.ts
4
+ var s = class {
5
+ shape;
6
+ state = /* @__PURE__ */ new Map();
7
+ extrema;
8
+ groups = /* @__PURE__ */ new Map();
9
+ positions = /* @__PURE__ */ new Map();
10
+ nextPosition = 0;
11
+ constructor(e) {
12
+ this.shape = e;
13
+ }
14
+ seed(e) {
15
+ this.extrema = o.seed(this.shape, e), this.state = new Map(t(this.shape, e)), this.groups.clear(), this.positions.clear(), this.nextPosition = 0;
16
+ for (let e of this.state.keys()) this.positions.set(e, this.nextPosition++);
17
+ for (let t of e) {
18
+ let e = n(t, this.shape.groupBy);
19
+ this.groups.has(e) || this.groups.set(e, this.groupValues(t));
20
+ }
21
+ }
22
+ applyChange(t) {
23
+ let r = t.op === "update" && t.old == null;
24
+ t = this.extrema?.withPreviousRow(t) ?? t;
25
+ let i = r && t.old != null && t.new != null && n(t.old, this.shape.groupBy) !== n(t.new, this.shape.groupBy), a = r && t.old == null && (this.shape.op === "min" || this.shape.op === "max"), o = t.new;
26
+ if (o) {
27
+ let e = n(o, this.shape.groupBy);
28
+ this.groups.has(e) || this.groups.set(e, this.groupValues(o));
29
+ }
30
+ let s = this.extrema !== void 0 && !this.extrema.apply(t);
31
+ s && (this.extrema = void 0);
32
+ let c = e(this.shape, this.state, t, {
33
+ removed: (e) => {
34
+ this.positions.delete(e);
35
+ },
36
+ added: (e) => {
37
+ this.positions.set(e, this.nextPosition++);
38
+ }
39
+ });
40
+ for (let e of [t.old, t.new]) {
41
+ if (e == null) continue;
42
+ let t = n(e, this.shape.groupBy);
43
+ this.state.has(t) || this.groups.delete(t);
44
+ }
45
+ if ((i || a) && (this.extrema = void 0), this.extrema !== void 0) {
46
+ for (let e of [t.old, t.new]) {
47
+ if (e == null) continue;
48
+ let t = n(e, this.shape.groupBy), r = this.state.get(t);
49
+ r !== void 0 && this.state.set(t, {
50
+ ...r,
51
+ extreme: this.extrema.value(t)
52
+ });
53
+ }
54
+ return [];
55
+ }
56
+ if (s || i || a) {
57
+ let e = new Set(a ? this.state.keys() : c.rescanGroups);
58
+ for (let r of [t.op === "insert" ? void 0 : t.old, t.op === "delete" ? void 0 : t.new]) r != null && e.add(n(r, this.shape.groupBy));
59
+ return [...e];
60
+ }
61
+ return c.rescanGroups;
62
+ }
63
+ resolveRescan(e, t) {
64
+ if (e.length === 0) return;
65
+ this.extrema = void 0, a(this.shape, this.state, e, t);
66
+ let r = new Set(e);
67
+ for (let e of t) {
68
+ let t = n(e, this.shape.groupBy);
69
+ r.has(t) && this.state.has(t) && !this.groups.has(t) && this.groups.set(t, this.groupValues(e));
70
+ }
71
+ for (let e of r) this.state.has(e) ? this.positions.has(e) || this.positions.set(e, this.nextPosition++) : (this.groups.delete(e), this.positions.delete(e));
72
+ }
73
+ materializeGroups(e) {
74
+ let t = [], n, i, a = 0;
75
+ for (let t of e) a === 0 ? n = t : (i === void 0 && (i = /* @__PURE__ */ new Set(), i.add(n)), i.add(t)), a += 1;
76
+ let o = (e) => {
77
+ let n = this.state.get(e);
78
+ n !== void 0 && t.push({
79
+ groupKey: e,
80
+ group: this.groups.get(e) ?? {},
81
+ value: r(this.shape, n),
82
+ rowIndex: this.positions.get(e)
83
+ });
84
+ };
85
+ if (i !== void 0) for (let e of i) o(e);
86
+ else a !== 0 && o(n);
87
+ return t;
88
+ }
89
+ materialize() {
90
+ let e = Object.getOwnPropertyDescriptor(this.shape, "op");
91
+ if (Object.getPrototypeOf(this.shape) === Object.prototype && e !== void 0 && "value" in e) {
92
+ let e = [];
93
+ for (let [t, n] of this.state) e.push({
94
+ groupKey: t,
95
+ group: this.groups.get(t) ?? {},
96
+ value: r(this.shape, n)
97
+ });
98
+ return e;
99
+ }
100
+ return i(this.shape, this.state).map((e) => ({
101
+ groupKey: e.groupKey,
102
+ group: this.groups.get(e.groupKey) ?? {},
103
+ value: e.value
104
+ }));
105
+ }
106
+ groupValues(e) {
107
+ let t = this.shape.groupBy;
108
+ if (t === void 0 || t.length === 0) return {};
109
+ let n = {};
110
+ for (let r of t) n[r] = e[r];
111
+ return n;
112
+ }
113
+ };
114
+ //#endregion
115
+ export { s as AggregateMaintainer };
@@ -0,0 +1,228 @@
1
+ import { Cause as e, Duration as t, Effect as n, Metric as r, Schedule as i } from "effect";
2
+ import { eq as a } from "@voltro/database";
3
+ //#region src/analyticsMirror.ts
4
+ var o = r.counter("voltro_analytics_mirror_forwarded_total", { description: "Reactive-table changes successfully applied to the analytics mirror." }), s = r.counter("voltro_analytics_mirror_retries_total", { description: "Analytics-mirror write attempts that failed (each is retried until the budget runs out)." }), c = r.counter("voltro_analytics_mirror_repair_queued_total", { description: "Analytics-mirror changes whose retries were exhausted and were queued for repair." }), l = r.counter("voltro_analytics_mirror_dropped_total", { description: "Analytics-mirror changes provably lost (repair queue overflow)." }), u = (e) => {
5
+ n.runSync(r.update(e, 1));
6
+ }, d = (t) => e.pretty(t), f = {
7
+ retryAttempts: 5,
8
+ retryBaseDelayMs: 100,
9
+ retryMaxDelayMs: 3e4,
10
+ repairIntervalMs: 6e4,
11
+ repairQueueLimit: 1e4,
12
+ versionStateLimit: 1e5
13
+ }, p = (e) => {
14
+ let t = process.env[e];
15
+ if (t === void 0 || t.trim() === "") return;
16
+ let n = Number(t);
17
+ return Number.isFinite(n) && n >= 0 ? n : void 0;
18
+ }, m = (e) => {
19
+ let t = f, n = e ?? {};
20
+ return {
21
+ retryAttempts: Math.max(1, n.retryAttempts ?? p("VOLTRO_ANALYTICS_MIRROR_RETRY_ATTEMPTS") ?? t.retryAttempts),
22
+ retryBaseDelayMs: n.retryBaseDelayMs ?? p("VOLTRO_ANALYTICS_MIRROR_RETRY_BASE_MS") ?? t.retryBaseDelayMs,
23
+ retryMaxDelayMs: n.retryMaxDelayMs ?? p("VOLTRO_ANALYTICS_MIRROR_RETRY_MAX_MS") ?? t.retryMaxDelayMs,
24
+ repairIntervalMs: n.repairIntervalMs ?? p("VOLTRO_ANALYTICS_MIRROR_REPAIR_INTERVAL_MS") ?? t.repairIntervalMs,
25
+ repairQueueLimit: Math.max(1, n.repairQueueLimit ?? p("VOLTRO_ANALYTICS_MIRROR_REPAIR_QUEUE_LIMIT") ?? t.repairQueueLimit),
26
+ versionStateLimit: Math.max(1, n.versionStateLimit ?? p("VOLTRO_ANALYTICS_MIRROR_VERSION_STATE_LIMIT") ?? t.versionStateLimit)
27
+ };
28
+ }, h = {
29
+ tables: [],
30
+ detach: () => {},
31
+ flush: () => Promise.resolve(),
32
+ repair: () => Promise.resolve(0),
33
+ stats: () => ({
34
+ forwarded: 0,
35
+ pending: 0,
36
+ awaitingRepair: 0,
37
+ dropped: 0
38
+ })
39
+ }, g = (e, t) => `${e}\u0000${String(t)}`, _ = (e) => {
40
+ let { store: r, sink: f, log: p } = e, _ = f.mirror;
41
+ if (_ === void 0 || _.tables.length === 0) return h;
42
+ let v = m(e), y = e.run ?? ((e) => n.runPromise(e)), b = _.primaryKey ?? "id", x = new Set(_.tables), S = f.info.provider, C = r.changeScope === "fleet", w = 0, T = () => {
43
+ let e = Date.now() * 1e3;
44
+ return w = e > w ? e : w + 1, w;
45
+ }, E = /* @__PURE__ */ new Map(), D = /* @__PURE__ */ new Set(), O = /* @__PURE__ */ new Map(), k = /* @__PURE__ */ new Set(), A = 0, j = 0, M = /* @__PURE__ */ new Map(), N = (e) => {
46
+ let t = M.get(e);
47
+ if (t === void 0 ? t = {
48
+ baseline: null,
49
+ ordinal: 0
50
+ } : M.delete(e), M.set(e, t), M.size > v.versionStateLimit) for (let t of M.keys()) {
51
+ if (M.size <= v.versionStateLimit) break;
52
+ t !== e && (E.has(t) || D.has(t) || O.has(t) || M.delete(t));
53
+ }
54
+ return t;
55
+ }, P = (e, t) => n.suspend(() => {
56
+ let r = M.get(e);
57
+ return r === void 0 ? n.fail(/* @__PURE__ */ Error(`analytics-mirror: no version state for ${t.table}`)) : r.baseline === null ? _.maxVersion({
58
+ table: t.table,
59
+ primaryKeyValue: t.pk
60
+ }).pipe(n.map((e) => (r.baseline = e ?? 0, r.baseline))) : n.succeed(r.baseline);
61
+ }), F = (e, t, r) => r.kind === "clock" ? n.succeed(r.version) : P(e, t).pipe(n.map((e) => e + r.ordinal)), I = i.exponential(t.millis(Math.max(1, v.retryBaseDelayMs)), 2), L = i.max([i.min([I, i.spaced(t.millis(Math.max(1, v.retryMaxDelayMs)))]), i.recurs(v.retryAttempts - 1)]), R = (e, t) => n.gen(function* () {
62
+ let n = yield* F(g(e.table, e.pk), e, t.stamp);
63
+ yield* t.kind === "upsert" ? _.upsert({
64
+ table: e.table,
65
+ row: t.row,
66
+ version: n
67
+ }) : _.remove({
68
+ table: e.table,
69
+ primaryKeyValue: e.pk,
70
+ version: n
71
+ });
72
+ }), z = (e) => e instanceof Error ? `${e.name}: ${e.message}` : String(e), B = (e, t) => R(e, t).pipe(n.tapCause(() => n.sync(() => {
73
+ u(s);
74
+ })), n.retry(L), n.matchCauseEffect({
75
+ onSuccess: () => n.sync(() => {
76
+ A += 1, u(o);
77
+ }),
78
+ onFailure: (r) => n.sync(() => {
79
+ V(e, t, d(r));
80
+ })
81
+ })), V = (e, t, n) => {
82
+ let r = g(e.table, e.pk);
83
+ for (p.warn("analytics-mirror: change forward failed, queued for repair", {
84
+ provider: S,
85
+ table: e.table,
86
+ op: t.kind,
87
+ attempts: v.retryAttempts,
88
+ err: n
89
+ }), O.set(r, e), u(c); O.size > v.repairQueueLimit;) {
90
+ let e = O.keys().next();
91
+ if (e.done === !0) break;
92
+ let t = O.get(e.value);
93
+ O.delete(e.value), j += 1, u(l), p.error("analytics-mirror: repair queue full — change dropped, mirror is diverged", {
94
+ provider: S,
95
+ table: t?.table ?? "<unknown>",
96
+ limit: v.repairQueueLimit
97
+ });
98
+ }
99
+ }, H = (e) => {
100
+ let t = E.get(e);
101
+ if (t === void 0) D.delete(e);
102
+ else return E.delete(e), t;
103
+ }, U = (e, t) => n.gen(function* () {
104
+ for (;;) {
105
+ let r = yield* n.sync(() => H(e));
106
+ if (r === void 0) return;
107
+ yield* B(t, r);
108
+ }
109
+ }), W = (e) => {
110
+ let t = Promise.resolve(y(e)).catch((e) => {
111
+ p.error("analytics-mirror: mirror worker crashed", {
112
+ provider: S,
113
+ err: z(e)
114
+ });
115
+ });
116
+ k.add(t), t.finally(() => {
117
+ k.delete(t);
118
+ });
119
+ }, G = (e, t) => {
120
+ let n = g(e.table, e.pk);
121
+ E.set(n, t), !D.has(n) && (D.add(n), W(U(n, e)));
122
+ }, K = (e) => n.tryPromise({
123
+ try: () => r.query({
124
+ table: e.table,
125
+ predicate: a(b, e.pk),
126
+ order: [],
127
+ take: 1,
128
+ skip: void 0,
129
+ projection: void 0
130
+ }),
131
+ catch: (e) => e
132
+ }).pipe(n.map((e) => e[0] ?? null)), q = (e, t) => n.gen(function* () {
133
+ let n = C ? (yield* P(e, t)) + (M.get(e)?.ordinal ?? 0) : T(), r = yield* K(t);
134
+ return yield* r === null ? _.remove({
135
+ table: t.table,
136
+ primaryKeyValue: t.pk,
137
+ version: n
138
+ }) : _.upsert({
139
+ table: t.table,
140
+ row: r,
141
+ version: n
142
+ }), O.delete(e), A += 1, u(o), !0;
143
+ }).pipe(n.catchCause((e) => n.sync(() => (p.warn("analytics-mirror: repair pass failed, key stays queued", {
144
+ provider: S,
145
+ table: t.table,
146
+ err: d(e)
147
+ }), !1)))), J = () => n.gen(function* () {
148
+ let e = [...O.entries()];
149
+ if (e.length === 0) return 0;
150
+ let t = 0;
151
+ for (let [n, r] of e) (yield* q(n, r)) && (t += 1);
152
+ return t;
153
+ }), Y = () => {
154
+ let e = 0;
155
+ return Promise.resolve(y(J().pipe(n.tap((t) => n.sync(() => {
156
+ e = t;
157
+ })), n.asVoid))).then(() => e).catch((e) => (p.error("analytics-mirror: repair pass crashed", {
158
+ provider: S,
159
+ err: z(e)
160
+ }), 0));
161
+ }, X = v.repairIntervalMs > 0 ? setInterval(() => {
162
+ Y();
163
+ }, v.repairIntervalMs) : void 0;
164
+ X?.unref?.();
165
+ let Z = (e) => {
166
+ if (!C) return {
167
+ kind: "clock",
168
+ version: T()
169
+ };
170
+ let t = N(g(e.table, e.pk));
171
+ return t.ordinal += 1, {
172
+ kind: "ordinal",
173
+ ordinal: t.ordinal
174
+ };
175
+ }, Q = (e) => {
176
+ if (e.op === "delete") {
177
+ let t = e.old;
178
+ if (t === null) return;
179
+ let n = t[b];
180
+ if (n === void 0) return;
181
+ let r = {
182
+ table: e.table,
183
+ pk: n
184
+ };
185
+ G(r, {
186
+ kind: "remove",
187
+ stamp: Z(r)
188
+ });
189
+ return;
190
+ }
191
+ let t = e.new;
192
+ if (t === null) return;
193
+ let n = t[b];
194
+ if (n === void 0) return;
195
+ let r = {
196
+ table: e.table,
197
+ pk: n
198
+ };
199
+ G(r, {
200
+ kind: "upsert",
201
+ row: t,
202
+ stamp: Z(r)
203
+ });
204
+ }, $ = r.onChange((e) => {
205
+ x.has(e.table) && Q(e);
206
+ });
207
+ return {
208
+ tables: _.tables,
209
+ detach: () => {
210
+ $(), X !== void 0 && clearInterval(X);
211
+ },
212
+ flush: async () => {
213
+ for (let e = 0; e < 1e3; e++) {
214
+ if (k.size === 0 && E.size === 0) return;
215
+ await Promise.all([...k]);
216
+ }
217
+ },
218
+ repair: Y,
219
+ stats: () => ({
220
+ forwarded: A,
221
+ pending: E.size,
222
+ awaitingRepair: O.size,
223
+ dropped: j
224
+ })
225
+ };
226
+ };
227
+ //#endregion
228
+ export { f as DEFAULT_ANALYTICS_MIRROR_TUNABLES, _ as attachAnalyticsMirror };
@@ -0,0 +1,32 @@
1
+ import { Context as e, Effect as t, Option as n } from "effect";
2
+ import { SubjectService as r } from "@voltro/protocol";
3
+ import { AnalyticsCapabilityNotSupported as i, AnalyticsError as a } from "@voltro/kv/analytics";
4
+ //#region src/analyticsSink.ts
5
+ var o = class extends e.Service()("@voltro/AnalyticsSink") {}, s = (e, t) => ({
6
+ info: e.info,
7
+ track: (n) => e.track(n.tenantId === void 0 ? {
8
+ ...n,
9
+ tenantId: t
10
+ } : n),
11
+ aggregate: (n) => e.aggregate(n.tenantId === void 0 ? {
12
+ ...n,
13
+ tenantId: t
14
+ } : n),
15
+ timeseries: (n) => e.timeseries(n.tenantId === void 0 ? {
16
+ ...n,
17
+ tenantId: t
18
+ } : n),
19
+ topN: (n) => e.topN(n.tenantId === void 0 ? {
20
+ ...n,
21
+ tenantId: t
22
+ } : n),
23
+ ...e.mirror === void 0 ? {} : { mirror: e.mirror }
24
+ }), c = () => t.gen(function* () {
25
+ let e = yield* o, i = yield* t.serviceOption(r), a = n.match(i, {
26
+ onNone: () => null,
27
+ onSome: (e) => e.tenantId ?? null
28
+ });
29
+ return a === null ? e : s(e, a);
30
+ });
31
+ //#endregion
32
+ export { i as AnalyticsCapabilityNotSupported, a as AnalyticsError, o as AnalyticsSink, c as useAnalytics };
@@ -0,0 +1,33 @@
1
+ //#region src/apiKeyUsage.ts
2
+ var e = (e) => {
3
+ let t = e.now ?? Date.now, n = e.intervalMs ?? 3e4, r = e.maxPending ?? 500, i = /* @__PURE__ */ new Map(), a = !1, o = async () => {
4
+ if (i.size === 0) return;
5
+ let t = i;
6
+ i = /* @__PURE__ */ new Map();
7
+ for (let [n, r] of t) try {
8
+ await e.flush(n, {
9
+ requests: r.requests,
10
+ lastUsedAt: r.lastUsedAt
11
+ });
12
+ } catch {}
13
+ }, s = setInterval(() => {
14
+ o();
15
+ }, n);
16
+ return s.unref?.(), {
17
+ touch: (e, n = t()) => {
18
+ if (a) return;
19
+ let s = i.get(e);
20
+ s === void 0 ? i.set(e, {
21
+ requests: 1,
22
+ lastUsedAt: n
23
+ }) : (s.requests += 1, n > s.lastUsedAt && (s.lastUsedAt = n)), i.size >= r && o();
24
+ },
25
+ flushNow: o,
26
+ shutdown: async () => {
27
+ a || (a = !0, clearInterval(s), await o());
28
+ },
29
+ pending: () => i.size
30
+ };
31
+ };
32
+ //#endregion
33
+ export { e as makeApiKeyUsageBuffer };