@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.
- package/CHANGELOG.md +1215 -0
- package/THIRD-PARTY-NOTICES.md +118 -600
- package/dist/aggregate.js +42 -0
- package/dist/aggregateExtrema.js +80 -0
- package/dist/aggregateIvm.js +115 -0
- package/dist/analyticsMirror.js +228 -0
- package/dist/analyticsSink.js +38 -0
- package/dist/apiKeyUsage.js +33 -0
- package/dist/apiKeys.js +223 -0
- package/dist/apiKeysWiring.js +114 -0
- package/dist/approvalBuiltins.js +10 -0
- package/dist/approvals.js +269 -0
- package/dist/authMiddlewareLayer.js +47 -0
- package/dist/bindEvent.js +72 -0
- package/dist/bootListener.js +76 -0
- package/dist/bootRefusal.js +7 -0
- package/dist/bootStoreCodec.js +70 -0
- package/dist/businessRules.js +67 -0
- package/dist/changeClaimsTable.js +12 -0
- package/dist/circuitBreaker.js +72 -0
- package/dist/columnOnUpdate.js +23 -0
- package/dist/composeAnalytics.js +108 -0
- package/dist/computedQuery.js +4 -0
- package/dist/connectionBuiltins.js +81 -0
- package/dist/connectionCredentials.js +66 -0
- package/dist/connectionState.js +34 -0
- package/dist/connectionVault.js +410 -0
- package/dist/connectionsFacade.js +54 -0
- package/dist/coordinatedSchedule.js +139 -0
- package/dist/crdtHistory.js +138 -0
- package/dist/crdtMerge.js +179 -0
- package/dist/crdtVersionsTable.js +25 -0
- package/dist/credentialBound.js +12 -0
- package/dist/crud.js +72 -0
- package/dist/dataStore.js +679 -0
- package/dist/dataloader.js +61 -0
- package/dist/declaredTablesGuard.js +48 -0
- package/dist/defineExecutor.js +4 -0
- package/dist/dependencyGraph.js +32 -0
- package/dist/devices.js +228 -0
- package/dist/devicesWiring.js +168 -0
- package/dist/dialectReplicationAdapter.js +29 -0
- package/dist/dispatcher.js +447 -0
- package/dist/effectStore.js +71 -0
- package/dist/eventBus.js +260 -0
- package/dist/eventPublish.js +49 -0
- package/dist/events.js +193 -0
- package/dist/expectation.js +52 -0
- package/dist/expectationEvaluator.js +308 -0
- package/dist/experiment.js +96 -0
- package/dist/experimentEvaluator.js +245 -0
- package/dist/externalizedTasks.js +25 -0
- package/dist/fieldCipher.js +61 -0
- package/dist/fingerprint.js +21 -0
- package/dist/finops.js +42 -0
- package/dist/finopsAccountant.js +216 -0
- package/dist/handlerBody.js +8 -0
- package/dist/healthProbes.js +55 -0
- package/dist/httpClientSsrf.js +49 -0
- package/dist/httpClientTracing.js +11 -0
- package/dist/httpCompression.js +76 -0
- package/dist/httpKeepAlive.js +12 -0
- package/dist/httpRouteMounts.js +47 -0
- package/dist/idempotencyStore.js +70 -0
- package/dist/idleDetector.js +21 -0
- package/dist/index.d.ts +252 -233
- package/dist/index.js +144 -14405
- package/dist/insertRequiredColumns.js +19 -0
- package/dist/inspectWorkflow.js +1 -0
- package/dist/instanceMembership.js +98 -0
- package/dist/ivm.js +125 -0
- package/dist/kvDataStore.js +86 -0
- package/dist/kvFacade.js +32 -0
- package/dist/matcher.js +304 -0
- package/dist/memoryColumnDefaults.js +11 -0
- package/dist/memoryQueryJoins.js +114 -0
- package/dist/metrics.js +161 -0
- package/dist/observability.js +123 -0
- package/dist/observedGraph.js +52 -0
- package/dist/originGuard.js +116 -0
- package/dist/outbox.js +352 -0
- package/dist/pluginRefRules.js +65 -0
- package/dist/policyGuardResolver.js +55 -0
- package/dist/preview.js +62 -0
- package/dist/projectedRows.js +12 -0
- package/dist/provenance.js +63 -0
- package/dist/rangeIndex.js +89 -0
- package/dist/rawReadScope.js +17 -0
- package/dist/reaction.js +85 -0
- package/dist/reactiveConfig.js +11 -0
- package/dist/readClassification.js +57 -0
- package/dist/readRecorder.js +66 -0
- package/dist/rebac.js +104 -0
- package/dist/rebacInterceptor.js +81 -0
- package/dist/registry.js +29 -0
- package/dist/relevantFields.js +65 -0
- package/dist/replicaHealth.js +42 -0
- package/dist/replicaLocality.js +44 -0
- package/dist/replicaObservations.js +121 -0
- package/dist/replicaSelection.js +32 -0
- package/dist/replicatedDataStore.js +239 -0
- package/dist/requestLoader.js +7 -0
- package/dist/resumeCensus.js +28 -0
- package/dist/revalidation.js +22 -0
- package/dist/rowFilter.js +65 -0
- package/dist/rowStateBytes.js +45 -0
- package/dist/rpcBodyCap.js +63 -0
- package/dist/rpcConnections.js +83 -0
- package/dist/rpcDefectDiagnostics.js +15 -0
- package/dist/rpcServer.js +992 -0
- package/dist/runAsSystem.js +23 -0
- package/dist/rywPositionStore.js +84 -0
- package/dist/schedule.js +23 -0
- package/dist/scheduleBackfillRequests.js +151 -0
- package/dist/scheduleClaimsTable.js +11 -0
- package/dist/scheduleRunsTable.js +37 -0
- package/dist/scheduler.js +835 -0
- package/dist/schemaRegistry.js +65 -0
- package/dist/secretsResolver.js +35 -0
- package/dist/securityHeaders.js +33 -0
- package/dist/servePipeline.js +305 -0
- package/dist/serverOnlyAudit.js +66 -0
- package/dist/serverTracer.js +15 -0
- package/dist/serviceCredentials.js +40 -0
- package/dist/singleNodeOrchestrator.js +91 -0
- package/dist/sourceResolution.js +42 -0
- package/dist/storeMiddleware.js +880 -0
- package/dist/storeQuery.js +204 -0
- package/dist/storeRead.js +10 -0
- package/dist/subscriber.js +7 -0
- package/dist/subscriptionOutbox.js +139 -0
- package/dist/subscriptionRegistry.js +82 -0
- package/dist/subscriptionResume.js +132 -0
- package/dist/subscriptionSocketMetrics.js +65 -0
- package/dist/systemSubject.js +5 -0
- package/dist/tenantScope.js +23 -0
- package/dist/tenantSelection.js +27 -0
- package/dist/timeline.js +105 -0
- package/dist/traceContext.js +5 -0
- package/dist/traceSink.js +63 -0
- package/dist/traceSpansTable.js +29 -0
- package/dist/traceparent.js +34 -0
- package/dist/transactionSubject.js +12 -0
- package/dist/trustedProxy.js +155 -0
- package/dist/undo.js +129 -0
- package/dist/undoBuiltins.js +69 -0
- package/dist/undoCapture.js +74 -0
- package/dist/undoLogTable.js +16 -0
- package/dist/upsertBinding.js +20 -0
- package/dist/wakeOrchestrator.js +34 -0
- package/dist/wakeRegistry.js +71 -0
- package/dist/wakeRouter.js +63 -0
- package/dist/wakeSupervisor.js +62 -0
- package/dist/wakeupsTable.js +12 -0
- package/dist/webSocketGatewayRuntime.js +114 -0
- package/dist/webhookVerification.js +18 -0
- package/dist/wireColumnStrip.js +84 -0
- package/dist/workflowReadBuiltins.js +87 -0
- package/dist/workflowRuntime.js +708 -0
- 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 };
|
package/dist/reaction.js
ADDED
|
@@ -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 };
|
package/dist/registry.js
ADDED
|
@@ -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 };
|