@voltro/runtime 0.84.0 → 0.86.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 (160) hide show
  1. package/CHANGELOG.md +1215 -0
  2. package/THIRD-PARTY-NOTICES.md +118 -600
  3. package/dist/aggregate.js +42 -0
  4. package/dist/aggregateExtrema.js +80 -0
  5. package/dist/aggregateIvm.js +115 -0
  6. package/dist/analyticsMirror.js +228 -0
  7. package/dist/analyticsSink.js +38 -0
  8. package/dist/apiKeyUsage.js +33 -0
  9. package/dist/apiKeys.js +223 -0
  10. package/dist/apiKeysWiring.js +114 -0
  11. package/dist/approvalBuiltins.js +10 -0
  12. package/dist/approvals.js +269 -0
  13. package/dist/authMiddlewareLayer.js +47 -0
  14. package/dist/bindEvent.js +72 -0
  15. package/dist/bootListener.js +76 -0
  16. package/dist/bootRefusal.js +7 -0
  17. package/dist/bootStoreCodec.js +70 -0
  18. package/dist/businessRules.js +67 -0
  19. package/dist/changeClaimsTable.js +12 -0
  20. package/dist/circuitBreaker.js +72 -0
  21. package/dist/columnOnUpdate.js +23 -0
  22. package/dist/composeAnalytics.js +108 -0
  23. package/dist/computedQuery.js +4 -0
  24. package/dist/connectionBuiltins.js +81 -0
  25. package/dist/connectionCredentials.js +66 -0
  26. package/dist/connectionState.js +34 -0
  27. package/dist/connectionVault.js +410 -0
  28. package/dist/connectionsFacade.js +54 -0
  29. package/dist/coordinatedSchedule.js +139 -0
  30. package/dist/crdtHistory.js +138 -0
  31. package/dist/crdtMerge.js +179 -0
  32. package/dist/crdtVersionsTable.js +25 -0
  33. package/dist/credentialBound.js +12 -0
  34. package/dist/crud.js +72 -0
  35. package/dist/dataStore.js +679 -0
  36. package/dist/dataloader.js +61 -0
  37. package/dist/declaredTablesGuard.js +48 -0
  38. package/dist/defineExecutor.js +4 -0
  39. package/dist/dependencyGraph.js +32 -0
  40. package/dist/devices.js +228 -0
  41. package/dist/devicesWiring.js +168 -0
  42. package/dist/dialectReplicationAdapter.js +29 -0
  43. package/dist/dispatcher.js +447 -0
  44. package/dist/effectStore.js +71 -0
  45. package/dist/eventBus.js +260 -0
  46. package/dist/eventPublish.js +49 -0
  47. package/dist/events.js +193 -0
  48. package/dist/expectation.js +52 -0
  49. package/dist/expectationEvaluator.js +308 -0
  50. package/dist/experiment.js +96 -0
  51. package/dist/experimentEvaluator.js +245 -0
  52. package/dist/externalizedTasks.js +25 -0
  53. package/dist/fieldCipher.js +61 -0
  54. package/dist/fingerprint.js +21 -0
  55. package/dist/finops.js +42 -0
  56. package/dist/finopsAccountant.js +216 -0
  57. package/dist/handlerBody.js +8 -0
  58. package/dist/healthProbes.js +55 -0
  59. package/dist/httpClientSsrf.js +49 -0
  60. package/dist/httpClientTracing.js +11 -0
  61. package/dist/httpCompression.js +76 -0
  62. package/dist/httpKeepAlive.js +12 -0
  63. package/dist/httpRouteMounts.js +47 -0
  64. package/dist/idempotencyStore.js +70 -0
  65. package/dist/idleDetector.js +21 -0
  66. package/dist/index.d.ts +252 -233
  67. package/dist/index.js +144 -14405
  68. package/dist/insertRequiredColumns.js +19 -0
  69. package/dist/inspectWorkflow.js +1 -0
  70. package/dist/instanceMembership.js +98 -0
  71. package/dist/ivm.js +125 -0
  72. package/dist/kvDataStore.js +86 -0
  73. package/dist/kvFacade.js +32 -0
  74. package/dist/matcher.js +304 -0
  75. package/dist/memoryColumnDefaults.js +11 -0
  76. package/dist/memoryQueryJoins.js +114 -0
  77. package/dist/metrics.js +161 -0
  78. package/dist/observability.js +123 -0
  79. package/dist/observedGraph.js +52 -0
  80. package/dist/originGuard.js +116 -0
  81. package/dist/outbox.js +352 -0
  82. package/dist/pluginRefRules.js +65 -0
  83. package/dist/policyGuardResolver.js +55 -0
  84. package/dist/preview.js +62 -0
  85. package/dist/projectedRows.js +12 -0
  86. package/dist/provenance.js +63 -0
  87. package/dist/rangeIndex.js +89 -0
  88. package/dist/rawReadScope.js +17 -0
  89. package/dist/reaction.js +85 -0
  90. package/dist/reactiveConfig.js +11 -0
  91. package/dist/readClassification.js +57 -0
  92. package/dist/readRecorder.js +66 -0
  93. package/dist/rebac.js +104 -0
  94. package/dist/rebacInterceptor.js +81 -0
  95. package/dist/registry.js +29 -0
  96. package/dist/relevantFields.js +65 -0
  97. package/dist/replicaHealth.js +42 -0
  98. package/dist/replicaLocality.js +44 -0
  99. package/dist/replicaObservations.js +121 -0
  100. package/dist/replicaSelection.js +32 -0
  101. package/dist/replicatedDataStore.js +239 -0
  102. package/dist/requestLoader.js +7 -0
  103. package/dist/resumeCensus.js +28 -0
  104. package/dist/revalidation.js +22 -0
  105. package/dist/rowFilter.js +65 -0
  106. package/dist/rowStateBytes.js +45 -0
  107. package/dist/rpcBodyCap.js +63 -0
  108. package/dist/rpcConnections.js +83 -0
  109. package/dist/rpcDefectDiagnostics.js +15 -0
  110. package/dist/rpcServer.js +992 -0
  111. package/dist/runAsSystem.js +23 -0
  112. package/dist/rywPositionStore.js +84 -0
  113. package/dist/schedule.js +23 -0
  114. package/dist/scheduleBackfillRequests.js +151 -0
  115. package/dist/scheduleClaimsTable.js +11 -0
  116. package/dist/scheduleRunsTable.js +37 -0
  117. package/dist/scheduler.js +835 -0
  118. package/dist/schemaRegistry.js +65 -0
  119. package/dist/secretsResolver.js +35 -0
  120. package/dist/securityHeaders.js +33 -0
  121. package/dist/servePipeline.js +305 -0
  122. package/dist/serverOnlyAudit.js +66 -0
  123. package/dist/serverTracer.js +15 -0
  124. package/dist/serviceCredentials.js +40 -0
  125. package/dist/singleNodeOrchestrator.js +91 -0
  126. package/dist/sourceResolution.js +42 -0
  127. package/dist/storeMiddleware.js +880 -0
  128. package/dist/storeQuery.js +204 -0
  129. package/dist/storeRead.js +10 -0
  130. package/dist/subscriber.js +7 -0
  131. package/dist/subscriptionOutbox.js +139 -0
  132. package/dist/subscriptionRegistry.js +82 -0
  133. package/dist/subscriptionResume.js +132 -0
  134. package/dist/subscriptionSocketMetrics.js +65 -0
  135. package/dist/systemSubject.js +5 -0
  136. package/dist/tenantScope.js +23 -0
  137. package/dist/tenantSelection.js +27 -0
  138. package/dist/timeline.js +105 -0
  139. package/dist/traceContext.js +5 -0
  140. package/dist/traceSink.js +63 -0
  141. package/dist/traceSpansTable.js +29 -0
  142. package/dist/traceparent.js +34 -0
  143. package/dist/transactionSubject.js +12 -0
  144. package/dist/trustedProxy.js +155 -0
  145. package/dist/undo.js +129 -0
  146. package/dist/undoBuiltins.js +69 -0
  147. package/dist/undoCapture.js +74 -0
  148. package/dist/undoLogTable.js +16 -0
  149. package/dist/upsertBinding.js +20 -0
  150. package/dist/wakeOrchestrator.js +34 -0
  151. package/dist/wakeRegistry.js +71 -0
  152. package/dist/wakeRouter.js +63 -0
  153. package/dist/wakeSupervisor.js +62 -0
  154. package/dist/wakeupsTable.js +12 -0
  155. package/dist/webSocketGatewayRuntime.js +114 -0
  156. package/dist/webhookVerification.js +18 -0
  157. package/dist/wireColumnStrip.js +84 -0
  158. package/dist/workflowReadBuiltins.js +87 -0
  159. package/dist/workflowRuntime.js +708 -0
  160. package/package.json +10 -13
@@ -0,0 +1,17 @@
1
+ import { resolveReactiveConfig as e } from "./reactiveConfig.js";
2
+ import { AsyncLocalStorage as t } from "node:async_hooks";
3
+ //#region src/rawReadScope.ts
4
+ var n = new t(), r = () => {
5
+ n.getStore() !== void 0 && n.enterWith({ observations: [] });
6
+ }, i = (t) => {
7
+ let r = n.getStore();
8
+ r === void 0 && (r = { observations: [] }, n.enterWith(r)), r.membership !== void 0 && !a(t) && (r.membership = void 0, r.exposed = !0), !r.exposed && r.membership === void 0 && r.observations.length >= 128 && a(t) && (r.observations.every(a) ? r.membership = new Set(r.observations.map((e) => e.sql)) : r.exposed = !0), !(r.membership?.has(t.sql) ?? r.observations.some((e) => e.sql === t.sql)) && (r.observations.length >= e().rawReadTrackingLimit || (r.observations.push(t), r.membership?.add(t.sql)));
9
+ }, a = (e) => {
10
+ let t = Object.getOwnPropertyDescriptor(e, "sql");
11
+ return Object.isFrozen(e) && t !== void 0 && "value" in t && typeof t.value == "string";
12
+ }, o = () => {
13
+ let e = n.getStore();
14
+ return e === void 0 ? [] : (e.exposed = !0, e.membership = void 0, e.observations);
15
+ }, s = (e) => n.run({ observations: [] }, e), c = (e) => e.join("?").replace(/\s+/g, " ").trim();
16
+ //#endregion
17
+ export { o as currentRawReads, r as isolateRawReadScope, c as rawSqlPreview, i as recordRawRead, s as runWithRawReadScope };
@@ -0,0 +1,85 @@
1
+ //#region src/reaction.ts
2
+ var e = (e) => typeof e.query == "string", t = (t) => e(t.watch), n = /^(\d+(?:\.\d+)?)\s*(ms|milliseconds?|s|seconds?|m|minutes?|h|hours?|d|days?)$/i, r = (e, t, r) => {
3
+ if (typeof e == "number") {
4
+ if (!Number.isFinite(e) || e <= 0) throw Error(`reaction '${r}': ${t} must be a positive duration`);
5
+ return e;
6
+ }
7
+ let i = n.exec(e.trim());
8
+ if (i === null) throw Error(`reaction '${r}': ${t} ${JSON.stringify(e)} is not a duration like '5 minutes' or '30s'`);
9
+ let a = Number(i[1]), o = i[2].toLowerCase();
10
+ return a * (o.startsWith("ms") || o.startsWith("milli") ? 1 : o.startsWith("s") ? 1e3 : o.startsWith("m") ? 6e4 : o.startsWith("h") ? 36e5 : 864e5);
11
+ }, i = 3e5, a = 3e4, o = 9e5;
12
+ function s(t) {
13
+ if (typeof t.name != "string" || t.name.length === 0) throw Error("defineReaction: a non-empty `name` is required");
14
+ if (t.watch !== void 0 && e(t.watch)) return c(t);
15
+ if (t.watch === void 0 || typeof t.watch.table != "string" || t.watch.table.length === 0) throw Error(`reaction '${t.name}': watch.table is required`);
16
+ if (t.act === void 0 || t.act.kind !== "agent" && t.act.kind !== "workflow") throw Error(`reaction '${t.name}': act must be { kind: 'agent' | 'workflow', … }`);
17
+ let n = t;
18
+ if (n.guards === void 0 || typeof n.guards.dedupeKey != "function") throw Error(`reaction '${t.name}': guards.dedupeKey is REQUIRED — an ungated reaction whose act writes the watched table self-triggers into a spend storm.`);
19
+ if (n.guards.rateLimit !== void 0 && (n.guards.rateLimit.limit <= 0 || n.guards.rateLimit.windowMs <= 0)) throw Error(`reaction '${t.name}': guards.rateLimit needs positive limit + windowMs`);
20
+ if (n.guards.costBudgetUsd !== void 0 && n.guards.costBudgetUsd < 0) throw Error(`reaction '${t.name}': guards.costBudgetUsd must be >= 0`);
21
+ return t;
22
+ }
23
+ var c = (e) => {
24
+ let t = `reaction '${e.name}'`;
25
+ if (e.watch.query.trim() === "") throw Error(`${t}: watch.query names the live query to watch`);
26
+ if (e.watch.api !== void 0 && !/^[a-z][a-zA-Z0-9]*$/.test(e.watch.api)) throw Error(`${t}: watch.api ${JSON.stringify(e.watch.api)} is not a binding name from \`apis:\``);
27
+ if (e.act === void 0 || e.act.kind !== "workflow") throw Error(`${t}: a reaction to a query's rows acts through a WORKFLOW — its idempotencyKey is what makes the act exactly-once across every replica that sees the row. An agent act has no such identity.`);
28
+ if (typeof e.key != "function") throw Error(`${t}: \`key: (row) => string\` is required — the identity of a row in the set`);
29
+ if (e.guards?.dedupeKey !== void 0) throw Error(`${t}: \`guards.dedupeKey\` belongs to the table form. Watching a query, the row's \`key\` and the workflow's idempotencyKey replace it — drop dedupeKey.`);
30
+ if (e.resync !== void 0 && r(e.resync, "resync", e.name) < 3e4) throw Error(`${t}: resync must be at least 30 seconds — it re-reads the whole set`);
31
+ e.stuckAfter !== void 0 && r(e.stuckAfter, "stuckAfter", e.name);
32
+ let n = e.guards?.rateLimit;
33
+ if (n !== void 0 && (n.limit <= 0 || n.windowMs <= 0)) throw Error(`${t}: guards.rateLimit needs positive limit + windowMs`);
34
+ return e;
35
+ }, l = (e, t) => e === void 0 ? !0 : Array.isArray(e) ? e.includes(t) : e === t, u = (e) => e.new?.tenantId ?? e.old?.tenantId ?? null, d = class {
36
+ hits = /* @__PURE__ */ new Map();
37
+ sweep;
38
+ allow(e, t, n, r) {
39
+ this.sweep ??= this.hits.entries();
40
+ for (let t = 0; t < 16; t++) {
41
+ let t = this.sweep.next();
42
+ if (t.done) {
43
+ this.sweep = void 0;
44
+ break;
45
+ }
46
+ let [n, i] = t.value;
47
+ n !== e && i.expiresAt <= r && this.hits.delete(n);
48
+ }
49
+ let i = r - n, a = this.hits.get(e) ?? {
50
+ times: [],
51
+ head: 0,
52
+ ordered: !0,
53
+ expiresAt: r + n
54
+ };
55
+ if (!a.ordered || Number.isNaN(i)) a.times = a.times.slice(a.head).filter((e) => e > i), a.head = 0, a.ordered = a.times.every((e, t) => t === 0 || e >= a.times[t - 1]);
56
+ else for (; a.head < a.times.length && !(a.times[a.head] > i);) a.head++;
57
+ if (a.head > 0 && (a.head >= 1024 || a.head * 2 >= a.times.length) && (a.times = a.times.slice(a.head), a.head = 0), a.expiresAt = Math.max(r + n, a.expiresAt), a.times.length - a.head >= t) return !1;
58
+ let o = a.times[a.times.length - 1];
59
+ return (Number.isNaN(r) || o !== void 0 && r < o) && (a.ordered = !1), a.times.push(r), this.hits.set(e, a), !0;
60
+ }
61
+ }, f = (e, t, n, r) => JSON.stringify([
62
+ e,
63
+ t,
64
+ Math.floor(r / n)
65
+ ]), p = async (e, t, n) => {
66
+ if (t.table !== e.watch.table || !l(e.watch.on, t.op)) return "skipped-op";
67
+ if (e.when !== void 0 && !e.when(t)) return "skipped-when";
68
+ let r = `${e.name}:${e.guards.dedupeKey(t)}`;
69
+ if (!await n.dedupe.claim(r)) return "skipped-dedupe";
70
+ let i = e.guards.rateLimit;
71
+ if (i !== void 0) {
72
+ let r = n.now?.() ?? Date.now(), a = i.key === void 0 ? "" : i.key(t);
73
+ if (n.claimRate !== void 0) {
74
+ let t = new Date((Math.floor(r / i.windowMs) + 1) * i.windowMs);
75
+ if (!await n.claimRate(f(e.name, a, i.windowMs, r), i.limit, t)) return "skipped-ratelimit";
76
+ } else if (n.rateLimiter !== void 0 && !n.rateLimiter.allow(`${e.name} ${a}`, i.limit, i.windowMs, r)) return "skipped-ratelimit";
77
+ }
78
+ let a = u(t);
79
+ return e.guards.costBudgetUsd !== void 0 && n.checkBudget !== void 0 && !await n.checkBudget(a, e.guards.costBudgetUsd) ? "skipped-budget" : (await n.runAct(e.act, t, {
80
+ id: a,
81
+ via: "agent"
82
+ }), "acted");
83
+ };
84
+ //#endregion
85
+ export { i as DEFAULT_LEVEL_RESYNC_MS, o as DEFAULT_LEVEL_STUCK_MS, a as MIN_LEVEL_RESYNC_MS, d as ReactionRateLimiter, s as defineReaction, t as isLevelReaction, e as isLevelWatch, r as reactionDurationMs, f as reactionRateBucketKey, p as runReaction };
@@ -0,0 +1,11 @@
1
+ //#region src/reactiveConfig.ts
2
+ var e = 8, t = 32, n = (e) => {
3
+ if (e == null || e === "") return;
4
+ let t = Number(e);
5
+ return Number.isFinite(t) && t >= 1 ? Math.floor(t) : void 0;
6
+ }, r = (e, t = process.env) => ({
7
+ deliveryConcurrency: n(t.VOLTRO_REACTIVE_DELIVERY_CONCURRENCY) ?? n(e?.deliveryConcurrency) ?? 8,
8
+ rawReadTrackingLimit: n(t.VOLTRO_RAW_READ_TRACKING_LIMIT) ?? n(e?.rawReadTrackingLimit) ?? 32
9
+ });
10
+ //#endregion
11
+ export { e as DEFAULT_DELIVERY_CONCURRENCY, t as DEFAULT_RAW_READ_TRACKING_LIMIT, r as resolveReactiveConfig };
@@ -0,0 +1,57 @@
1
+ import { getRelations as e } from "@voltro/database";
2
+ //#region src/readClassification.ts
3
+ var t = (e) => {
4
+ if (typeof e != "object" || !e) return;
5
+ let t = e.subquery;
6
+ return typeof t == "object" && t && typeof t.table == "string" ? t : void 0;
7
+ }, n = (e, r) => {
8
+ if (typeof e != "object" || !e) return;
9
+ let i = t(e);
10
+ if (i !== void 0) {
11
+ r.add(i.table);
12
+ for (let e of i.joins ?? []) r.add(e.table);
13
+ n(i.predicate, r);
14
+ } else for (let t of Object.values(e)) if (Array.isArray(t)) for (let e of t) n(e, r);
15
+ else n(t, r);
16
+ }, r = (t, n, a, o = 0) => {
17
+ if (o > 10 || typeof n != "object" || !n) return;
18
+ let s = e(t);
19
+ if (s !== void 0) for (let [e, t] of Object.entries(n)) {
20
+ let n = s[e];
21
+ if (n === void 0) continue;
22
+ let c = i(n.through);
23
+ c !== void 0 && a.add(c);
24
+ let l = i(n.target);
25
+ if (l === void 0) continue;
26
+ a.add(l);
27
+ let u = typeof t == "object" && t ? t.with : void 0;
28
+ u !== void 0 && r(l, u, a, o + 1);
29
+ }
30
+ }, i = (e) => {
31
+ try {
32
+ let t = (typeof e == "function" ? e() : e)?.tableName;
33
+ return typeof t == "string" && t.length > 0 ? t : void 0;
34
+ } catch {
35
+ return;
36
+ }
37
+ }, a = (e, t = !1) => {
38
+ let i = /* @__PURE__ */ new Set();
39
+ if (n(e.predicate, i), t) {
40
+ i.add(e.table);
41
+ for (let t of e.joins ?? []) i.add(t.table);
42
+ return r(e.table, e.eager, i), {
43
+ composed: /* @__PURE__ */ new Set(),
44
+ restricting: i
45
+ };
46
+ }
47
+ let a = /* @__PURE__ */ new Set([e.table]);
48
+ for (let t of e.joins ?? []) a.add(t.table);
49
+ r(e.table, e.eager, a);
50
+ for (let e of a) i.delete(e);
51
+ return {
52
+ composed: a,
53
+ restricting: i
54
+ };
55
+ };
56
+ //#endregion
57
+ export { a as classifyRead };
@@ -0,0 +1,66 @@
1
+ import { classifyRead as e } from "./readClassification.js";
2
+ import { Effect as t } from "effect";
3
+ import { AsyncLocalStorage as n } from "node:async_hooks";
4
+ //#region src/readRecorder.ts
5
+ var r = new n(), i, a = /* @__PURE__ */ new Set(), o = (e) => {
6
+ i = e, a.clear();
7
+ }, s = () => i !== void 0, c = (e, t) => {
8
+ if (i === void 0) return;
9
+ let n = new Set(t), o = a.has(e) || n.size === 0;
10
+ r.enterWith({
11
+ procedure: e,
12
+ declared: n,
13
+ composed: /* @__PURE__ */ new Set(),
14
+ restricting: /* @__PURE__ */ new Set(),
15
+ depth: { n: 0 },
16
+ ...o ? { muted: !0 } : {}
17
+ });
18
+ }, l = async (e, t, n = []) => {
19
+ let i = {
20
+ procedure: e,
21
+ declared: new Set(n),
22
+ composed: /* @__PURE__ */ new Set(),
23
+ restricting: /* @__PURE__ */ new Set(),
24
+ depth: { n: 0 }
25
+ };
26
+ return {
27
+ result: await r.run(i, t),
28
+ composed: i.composed,
29
+ restricting: i.restricting
30
+ };
31
+ }, u = async (e) => {
32
+ let t = r.getStore();
33
+ if (t === void 0) return e();
34
+ t.depth.n += 1;
35
+ try {
36
+ return await e();
37
+ } finally {
38
+ --t.depth.n;
39
+ }
40
+ }, d = (e) => t.suspend(() => {
41
+ let n = r.getStore();
42
+ return n === void 0 ? e : t.acquireUseRelease(t.sync(() => {
43
+ n.depth.n += 1;
44
+ }), () => e, () => t.sync(() => {
45
+ --n.depth.n;
46
+ }));
47
+ }), f = (t) => {
48
+ let n = r.getStore();
49
+ if (n === void 0 || n.muted === !0) return;
50
+ let { composed: o, restricting: s } = e(t, n.depth.n > 0);
51
+ for (let e of o) n.composed.add(e);
52
+ for (let e of s) n.composed.has(e) || n.restricting.add(e);
53
+ if (i === void 0 || a.has(n.procedure)) return;
54
+ let c = m(n.procedure, n.composed, n.declared, n.restricting);
55
+ c !== void 0 && (a.add(n.procedure), i(c));
56
+ }, p = (e) => new Proxy(e, { get(e, t) {
57
+ let n = Reflect.get(e, t, e);
58
+ return typeof n == "function" && t !== "query" ? n.bind(e) : t !== "query" || typeof n != "function" ? n : (t) => (f(t), n.call(e, t));
59
+ } }), m = (e, t, n, r) => {
60
+ let i = [...t], a = 0;
61
+ for (let e of i) n.has(e) || (i[a++] = e);
62
+ if (i.length = a, i.sort(), i.length !== 0) return `${e}: read ${i.map((e) => `\`${e}\``).join(", ")} without declaring ${i.length === 1 ? "it" : "them"} in \`source:\`.\n A write to ${i.length === 1 ? "that table" : "those tables"} will not re-run this query, so an open view keeps\n showing what it showed before. The write itself is fine, which is why nothing else reports this.
63
+ Observed on this boot — a branch that did not run contributes nothing, so this names what WAS\n read and never claims the declaration is otherwise complete.` + (r.size > 0 ? `\n Not counted (read only to narrow the result): ${[...r].sort().map((e) => `\`${e}\``).join(", ")}.` : "");
64
+ };
65
+ //#endregion
66
+ export { c as enterReadRecording, f as noteRead, s as readRecordingActive, l as recordReads, p as recordingStore, u as restrictingReads, d as restrictingReadsEffect, o as setSourceGapSink, m as sourceGapReport };
package/dist/rebac.js ADDED
@@ -0,0 +1,104 @@
1
+ import { Schema as e } from "effect";
2
+ //#region src/rebac.ts
3
+ var t = "admin:full", n = class extends e.TaggedError()("AccessDenied", {
4
+ action: e.String,
5
+ resourceType: e.String,
6
+ resourceId: e.String
7
+ }) {}, r = /* @__PURE__ */ new Map(), i = (e) => {
8
+ if (typeof e.resourceType != "string" || e.resourceType.length === 0) throw Error("defineResourcePolicy: a non-empty `resourceType` is required");
9
+ if (e.actions === void 0 || Object.keys(e.actions).length === 0) throw Error(`resource policy '${e.resourceType}': at least one action is required`);
10
+ if (r.has(e.resourceType)) throw Error(`resource policy '${e.resourceType}' is already registered — types must be unique`);
11
+ return r.set(e.resourceType, e), e;
12
+ }, a = (e) => r.get(e), o = () => [...r.values()], s = () => r.clear(), c = () => o().map((e) => {
13
+ let t = /* @__PURE__ */ new Set();
14
+ for (let n of Object.values(e.actions)) for (let e of n) t.add(e);
15
+ for (let [n, r] of Object.entries(e.implies ?? {})) {
16
+ t.add(n);
17
+ for (let e of r) t.add(e);
18
+ }
19
+ return {
20
+ resourceType: e.resourceType,
21
+ actions: e.actions,
22
+ relations: [...t],
23
+ implies: e.implies ?? {}
24
+ };
25
+ }), l = (e, t) => {
26
+ let n = new Set(e);
27
+ if (t === void 0) return n;
28
+ let r = [...e];
29
+ for (; r.length > 0;) {
30
+ let e = r.pop();
31
+ for (let i of t[e] ?? []) n.has(i) || (n.add(i), r.push(i));
32
+ }
33
+ return n;
34
+ }, u = (e, t, n, r) => e.id === null ? /* @__PURE__ */ new Set() : l(n.filter((n) => n.subjectId === e.id && n.resourceType === t.type && n.resourceId === t.id).map((e) => e.relation), r.implies), d = (e, t, n, r) => {
35
+ if (e.scopes?.includes("admin:full")) return !0;
36
+ if (e.id === null || n.tenantId != null && e.tenantId !== n.tenantId) return !1;
37
+ let i = r.policy.actions[t];
38
+ if (i === void 0 || i.length === 0) return !1;
39
+ let a = u(e, n, r.tuples, r.policy);
40
+ return i.some((e) => a.has(e));
41
+ }, f = (e, t, r, i) => {
42
+ if (!d(e, t, r, i)) throw new n({
43
+ action: t,
44
+ resourceType: r.type,
45
+ resourceId: r.id
46
+ });
47
+ }, p = (e) => {
48
+ if (Object.getPrototypeOf(e) !== Array.prototype || e.some !== Array.prototype.some || e[Symbol.iterator] !== Array.prototype[Symbol.iterator]) return !1;
49
+ for (let t = 0; t < e.length; t++) {
50
+ let n = Object.getOwnPropertyDescriptor(e, t);
51
+ if (n === void 0 || !("value" in n) || typeof n.value != "string") return !1;
52
+ }
53
+ return !0;
54
+ }, m = (e, t, n, r, i) => {
55
+ let a = Object.getOwnPropertyDescriptor(e, "policy");
56
+ if (n.length < 32 || a === void 0 || !("value" in a) || !p(r) || Object.getPrototypeOf(n) !== Array.prototype || n.every !== Array.prototype.every || n.filter !== Array.prototype.filter) return !1;
57
+ let o = Object.getOwnPropertyDescriptor(t, "tenantId");
58
+ if (o === void 0 ? "tenantId" in t : !("value" in o)) return !1;
59
+ let s = Object.getOwnPropertyDescriptor(e.policy, "implies");
60
+ if (s === void 0 || !("value" in s) || s.value === void 0) return !1;
61
+ let c = Object.getPrototypeOf(s.value);
62
+ if (c !== Object.prototype && c !== null) return !1;
63
+ let l = (e) => !Object.hasOwn(s.value, e) && e in s.value;
64
+ for (let e of Object.values(Object.getOwnPropertyDescriptors(s.value))) if (!("value" in e) || !Array.isArray(e.value) || !p(e.value) || e.value.some(l)) return !1;
65
+ for (let e of i.values()) if (e.some(l)) return !1;
66
+ return n.every((e) => {
67
+ let t = Object.getOwnPropertyDescriptor(e, "id"), n = Object.getOwnPropertyDescriptor(e, "tenantId");
68
+ return t !== void 0 && "value" in t && (n === void 0 ? !("tenantId" in e) : "value" in n);
69
+ });
70
+ }, h = (e, t, n, r, i) => {
71
+ if (e.scopes?.includes("admin:full")) return r.filter(() => !0);
72
+ if (e.id === null || r.length === 0) return [];
73
+ let a = i.policy.actions[t];
74
+ if (a === void 0 || a.length === 0) return [];
75
+ if (r.length === 1) return r.filter((r) => d(e, t, {
76
+ type: n,
77
+ id: r.id,
78
+ ...r.tenantId === void 0 ? {} : { tenantId: r.tenantId }
79
+ }, i));
80
+ let o = /* @__PURE__ */ new Map();
81
+ for (let t of i.tuples) {
82
+ if (t.subjectId !== e.id || t.resourceType !== n) continue;
83
+ let r = o.get(t.resourceId);
84
+ r === void 0 ? o.set(t.resourceId, [t.relation]) : r.push(t.relation);
85
+ }
86
+ let s = /* @__PURE__ */ new Map(), c = m(i, e, r, a, o) ? /* @__PURE__ */ new Map() : void 0;
87
+ return r.filter((t) => {
88
+ if (t.tenantId != null && e.tenantId !== t.tenantId) return !1;
89
+ let n = s.get(t.id);
90
+ if (n !== void 0) return n;
91
+ let r = o.get(t.id) ?? [], u = c === void 0 ? l(r, i.policy.implies) : void 0, d = c === void 0 ? a.some((e) => u.has(e)) : r.some((e) => {
92
+ let t = c.get(e);
93
+ if (t !== void 0) return t;
94
+ let n = l([e], i.policy.implies), r = a.some((e) => n.has(e));
95
+ return c.set(e, r), r;
96
+ });
97
+ return s.set(t.id, d), d;
98
+ });
99
+ }, g = (e, t) => {
100
+ let n = new Set(t.map((e) => e.id));
101
+ return e.filter((e) => !n.has(e.id)).map((e) => e.id);
102
+ };
103
+ //#endregion
104
+ export { n as AccessDenied, t as REBAC_ADMIN_SCOPE, f as assertCan, d as can, s as clearResourcePolicies, i as defineResourcePolicy, u as effectiveRelations, a as getResourcePolicy, o as listResourcePolicies, c as rebacPolicyGraph, g as revokedIds, h as visibleRows };
@@ -0,0 +1,81 @@
1
+ import { AccessDenied as e, can as t, getResourcePolicy as n, visibleRows as r } from "./rebac.js";
2
+ import { Effect as i } from "effect";
3
+ import { and as a, eq as o, id as s, table as c, text as l, timestamp as u } from "@voltro/database";
4
+ //#region src/rebacInterceptor.ts
5
+ var d = c("_voltro_rebac_tuples", {
6
+ id: s({ prefix: "rbct" }),
7
+ subjectId: l(),
8
+ relation: l(),
9
+ resourceType: l(),
10
+ resourceId: l(),
11
+ tenantId: l().nullable(),
12
+ createdAt: u().default("now")
13
+ }).index([
14
+ "subjectId",
15
+ "resourceType",
16
+ "resourceId"
17
+ ]), f = async (e, t, n, r) => t === null ? [] : (await e.query({
18
+ table: "_voltro_rebac_tuples",
19
+ predicate: a(o("subjectId", t), o("resourceType", n), o("resourceId", r)),
20
+ order: [],
21
+ take: void 0,
22
+ skip: void 0,
23
+ projection: void 0
24
+ })).map((e) => ({
25
+ subjectId: e.subjectId,
26
+ relation: e.relation,
27
+ resourceType: e.resourceType,
28
+ resourceId: e.resourceId
29
+ })), p = (e) => ({
30
+ id: e.id,
31
+ tenantId: e.tenantId,
32
+ ...e.scopes === void 0 ? {} : { scopes: e.scopes }
33
+ }), m = (r, a) => {
34
+ let o = a.getPolicy ?? n;
35
+ return (n, s) => {
36
+ let c = r[s.tag];
37
+ if (c === void 0) return n;
38
+ let l = o(c.resourceType), u = (t) => i.fail(new e({
39
+ action: c.action,
40
+ resourceType: c.resourceType,
41
+ resourceId: t
42
+ }));
43
+ if (l === void 0) return u("");
44
+ let d = c.resourceId(s.input);
45
+ if (d === null || d === "") return u("");
46
+ let f = p(s.subject), m = {
47
+ type: c.resourceType,
48
+ id: d,
49
+ tenantId: s.subject.tenantId
50
+ };
51
+ return i.flatMap(i.promise(async () => a.loadTuples(f.id, c.resourceType, d)), (e) => t(f, c.action, m, {
52
+ policy: l,
53
+ tuples: e
54
+ }) ? n : u(d));
55
+ };
56
+ }, h = async (e, t, n) => t === null ? [] : (await e.query({
57
+ table: "_voltro_rebac_tuples",
58
+ predicate: a(o("subjectId", t), o("resourceType", n)),
59
+ order: [],
60
+ take: void 0,
61
+ skip: void 0,
62
+ projection: void 0
63
+ })).map((e) => ({
64
+ subjectId: e.subjectId,
65
+ relation: e.relation,
66
+ resourceType: e.resourceType,
67
+ resourceId: e.resourceId
68
+ })), g = (e) => {
69
+ let t = e.getPolicy ?? n;
70
+ return async (n, i, a, o) => {
71
+ let s = t(i);
72
+ if (s === void 0) return [];
73
+ let c = p(n), l = await e.loadSubjectTuples(c.id, i);
74
+ return r(c, a, i, o, {
75
+ policy: s,
76
+ tuples: l
77
+ });
78
+ };
79
+ };
80
+ //#endregion
81
+ export { d as _voltroRebacTuplesTable, m as buildRebacInterceptor, g as buildRebacReadFilter, f as loadResourceTuples, h as loadSubjectTuples };
@@ -0,0 +1,29 @@
1
+ import { Effect as e } from "effect";
2
+ import { ValidationError as t, ValidationErrors as n, effectiveScopes as r, hasEffectiveScope as i, recheckGuardsEffect as a } from "@voltro/protocol";
3
+ //#region src/registry.ts
4
+ var o = (t) => ({
5
+ has: (e) => i(t, e),
6
+ hasAny: (e) => e.some((e) => i(t, e)),
7
+ require: (n) => e.suspend(() => a(t, [{ scope: n }], void 0).pipe(e.flatMap((t) => t === null ? e.void : t._tag === "ScopeError" ? e.fail(t) : e.die(t)))),
8
+ get scopes() {
9
+ return r(t);
10
+ }
11
+ }), s = {
12
+ fail: (n, r, i) => e.fail(new t({
13
+ field: n,
14
+ message: r,
15
+ ...i === void 0 ? {} : { params: i }
16
+ })),
17
+ failAll: (t) => t.length === 0 ? e.die(/* @__PURE__ */ Error("ctx.validation.failAll([]) — an empty issue list has nothing to route; fail the field(s) you checked.")) : e.fail(new n({ issues: t.map((e) => ({
18
+ field: e.field,
19
+ message: e.message,
20
+ ...e.params === void 0 ? {} : { params: e.params }
21
+ })) })),
22
+ require: (n, r, i, a) => n ? e.void : e.fail(new t({
23
+ field: r,
24
+ message: i,
25
+ ...a === void 0 ? {} : { params: a }
26
+ }))
27
+ };
28
+ //#endregion
29
+ export { s as appValidation, o as makeAppAccess };
@@ -0,0 +1,65 @@
1
+ import { resolveDependentTables as e } from "./dependencyGraph.js";
2
+ import { getRelations as t, getTable as n, isColumnReference as r } from "@voltro/database";
3
+ //#region src/relevantFields.ts
4
+ var i = (e) => e.alias !== void 0 || (e.joins?.length ?? 0) > 0 || e.setOp !== void 0 || e.ctes !== void 0 && e.ctes.length > 0 || e.aggregations !== void 0 && e.aggregations.some((e) => e.op.startsWith("window-")), a = /* @__PURE__ */ new Set(["*"]), o = (e, t) => {
5
+ if (e !== void 0) {
6
+ if ("not" in e) o(e.not, t);
7
+ else if ("and" in e) for (let n of e.and) o(n, t);
8
+ else if ("or" in e) for (let n of e.or) o(n, t);
9
+ else e.op === "subquery-in" || e.op === "subquery-not-in" ? t.add(e.column) : e.op !== "exists" && e.op !== "not-exists" && (t.add(e.column), "value" in e && r(e.value) && t.add(e.value.column));
10
+ }
11
+ }, s = (e, t, n) => {
12
+ let r = e.get(t);
13
+ r === void 0 && (r = /* @__PURE__ */ new Set(), e.set(t, r));
14
+ for (let e of n) r.add(e);
15
+ }, c = (e, n, r) => {
16
+ let i = t(e);
17
+ if (i !== void 0) for (let [t, a] of Object.entries(n)) {
18
+ let n = i[t];
19
+ if (n === void 0) continue;
20
+ let o = a === !0 ? {} : a;
21
+ if (n.kind === "manyToMany") {
22
+ let t = n.through(), i = n.target();
23
+ s(r, t.tableName, [n.sourceKey, n.targetKey]), s(r, e, ["id"]), s(r, i.tableName, ["id"]), l(i.tableName, o, r), o.with !== void 0 && c(i.tableName, o.with, r);
24
+ continue;
25
+ }
26
+ let u = n.target();
27
+ n.foreignKey !== void 0 && s(r, u.tableName, [n.foreignKey]), n.sourceKey === void 0 ? s(r, e, ["id"]) : s(r, e, [n.sourceKey]), s(r, u.tableName, ["id"]), l(u.tableName, o, r), o.with !== void 0 && c(u.tableName, o.with, r);
28
+ }
29
+ }, l = (e, t, r) => {
30
+ if (t.where !== void 0) {
31
+ let n = /* @__PURE__ */ new Set();
32
+ o(t.where, n), s(r, e, n);
33
+ }
34
+ t.orderBy !== void 0 && s(r, e, t.orderBy.map((e) => e.column));
35
+ let i = n(e);
36
+ i === void 0 ? s(r, e, ["*"]) : s(r, e, Object.keys(i.fields));
37
+ }, u = (t) => {
38
+ let n = /* @__PURE__ */ new Map();
39
+ for (let e of t.dependentTables ?? []) s(n, e, ["*"]);
40
+ if (i(t)) {
41
+ for (let r of e(t)) s(n, r, ["*"]);
42
+ return n;
43
+ }
44
+ let r = /* @__PURE__ */ new Set();
45
+ if (t.projection !== void 0) for (let e of t.projection) r.add(e);
46
+ else r.add("*");
47
+ o(t.predicate, r);
48
+ for (let e of t.order) r.add(e.column);
49
+ return s(n, t.table, r), t.eager !== void 0 && c(t.table, t.eager, n), n;
50
+ }, d = (e) => {
51
+ if (e.op === "insert" || e.op === "delete" || e.old === null || e.new === null) return a;
52
+ let t = /* @__PURE__ */ new Set(), n = e.old, r = e.new, i = /* @__PURE__ */ new Set([...Object.keys(n), ...Object.keys(r)]);
53
+ for (let e of i) Object.is(n[e], r[e]) || t.add(e);
54
+ return t;
55
+ }, f = (e, t) => {
56
+ let n = t.get(e.table);
57
+ if (n === void 0) return !1;
58
+ if (n.has("*")) return !0;
59
+ let r = d(e);
60
+ if (r.has("*")) return !0;
61
+ for (let e of r) if (n.has(e)) return !0;
62
+ return !1;
63
+ };
64
+ //#endregion
65
+ export { a as ALL_COLUMNS, f as isEventRelevant, d as mutatedColumns, u as resolveRelevantFields };
@@ -0,0 +1,42 @@
1
+ //#region src/replicaHealth.ts
2
+ var e = (e = {}) => {
3
+ let t = e.thresholdFailures ?? 3, n = e.windowMs ?? 1e4, r = e.blackoutMs ?? 6e4, i = e.now ?? Date.now, a = /* @__PURE__ */ new Map(), o = (e) => {
4
+ let t = a.get(e);
5
+ return t === void 0 && (t = {
6
+ failures: [],
7
+ blackoutUntilMs: null
8
+ }, a.set(e, t)), t;
9
+ }, s = (e, t) => {
10
+ let r = t - n, i = 0;
11
+ for (; i < e.failures.length && e.failures[i] < r;) i++;
12
+ i > 0 && e.failures.splice(0, i);
13
+ };
14
+ return {
15
+ isHealthy(e) {
16
+ let t = a.get(e);
17
+ if (t === void 0) return !0;
18
+ let n = i();
19
+ return t.blackoutUntilMs !== null && t.blackoutUntilMs > n ? !1 : (t.blackoutUntilMs !== null && t.blackoutUntilMs <= n && (t.blackoutUntilMs = null, t.failures = []), !0);
20
+ },
21
+ reportFailure(e, n) {
22
+ let a = i(), c = o(e);
23
+ s(c, a), c.failures.push(a), c.failures.length >= t && (c.blackoutUntilMs = a + r);
24
+ },
25
+ reportSuccess(e) {
26
+ let t = a.get(e);
27
+ t !== void 0 && (t.failures = []);
28
+ },
29
+ snapshot() {
30
+ let e = Array(a.size), t = 0;
31
+ return a.forEach((n, r) => {
32
+ e[t++] = {
33
+ replica: r,
34
+ failureCount: n.failures.length,
35
+ blackoutUntilMs: n.blackoutUntilMs
36
+ };
37
+ }), e;
38
+ }
39
+ };
40
+ };
41
+ //#endregion
42
+ export { e as replicaHealth };
@@ -0,0 +1,44 @@
1
+ //#region src/replicaLocality.ts
2
+ var e = (e) => {
3
+ let { currentRegion: t, replicaRegions: n, inner: r } = e, i = (e) => {
4
+ if (t === void 0) return {
5
+ sameRegion: [],
6
+ otherRegion: e
7
+ };
8
+ let r = [], i = [];
9
+ for (let a of e) n.get(a) === t ? r.push(a) : i.push(a);
10
+ return {
11
+ sameRegion: r,
12
+ otherRegion: i
13
+ };
14
+ };
15
+ return {
16
+ pick(e) {
17
+ let { sameRegion: t, otherRegion: n } = i(e);
18
+ return r.pick(t) ?? r.pick(n);
19
+ },
20
+ beforeQuery(e) {
21
+ r.beforeQuery(e);
22
+ },
23
+ afterQuery(e) {
24
+ r.afterQuery(e);
25
+ },
26
+ loadSnapshot() {
27
+ return r.loadSnapshot();
28
+ }
29
+ };
30
+ }, t = (e = process.env) => {
31
+ for (let t of [
32
+ "VOLTRO_REGION",
33
+ "AWS_REGION",
34
+ "FLY_REGION",
35
+ "RAILWAY_REGION",
36
+ "GCP_REGION",
37
+ "CLOUD_RUN_REGION"
38
+ ]) {
39
+ let n = e[t];
40
+ if (typeof n == "string" && n.length > 0) return n;
41
+ }
42
+ };
43
+ //#endregion
44
+ export { e as localityAwareSelector, t as resolveCurrentRegion };