@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,27 @@
1
+ import { TENANT_SELECTIONS_TABLE as e, deleteTenantSelection as t, readTenantSelection as n, tenantSelectionId as r, writeTenantSelection as i } from "@voltro/database";
2
+ import { isTenantSelectable as a, selectSubjectTenant as o } from "@voltro/protocol";
3
+ //#region src/tenantSelection.ts
4
+ var s = (e) => {
5
+ let r = () => {
6
+ let t = e();
7
+ if (t === void 0) throw Error("the data store is not built yet");
8
+ return t;
9
+ };
10
+ return {
11
+ read: (e) => n(r(), e),
12
+ clear: (e) => t(r(), e)
13
+ };
14
+ }, c = async (e, t, n, r) => {
15
+ let s = o(t, n, r);
16
+ return a(s) && await i(e, {
17
+ type: s.type,
18
+ id: s.id
19
+ }, n), s;
20
+ }, l = async (e, n) => {
21
+ a(n) && await t(e, {
22
+ type: n.type,
23
+ id: n.id
24
+ });
25
+ };
26
+ //#endregion
27
+ export { e as TENANT_SELECTIONS_TABLE, l as clearTenantSelection, c as selectTenant, r as tenantSelectionId, s as tenantSelectionStore };
@@ -0,0 +1,105 @@
1
+ import { randomUUID as e } from "node:crypto";
2
+ import { BoundedRing as t } from "@voltro/protocol";
3
+ //#region src/timeline.ts
4
+ var n = (e = process.env) => {
5
+ let t = (e.VOLTRO_TIMELINE ?? "").toLowerCase();
6
+ return t === "off" || t === "0" || t === "false" ? "off" : t === "all" ? "all" : t === "interesting" || t === "on" || t === "1" || t === "true" ? "interesting" : e.NODE_ENV === "production" ? "off" : "interesting";
7
+ }, r = /pass(word)?|secret|token|api[_-]?key|authorization|cookie|ssn|credit[_-]?card|card[_-]?number|cvv|encrypted/i, i = "«redacted»", a = (e) => {
8
+ if (e == null) return null;
9
+ let t = {};
10
+ for (let [n, a] of Object.entries(e)) t[n] = r.test(n) ? i : a;
11
+ return t;
12
+ }, o = (e) => {
13
+ let t = e?.id;
14
+ return typeof t == "string" || typeof t == "number" && Number.isFinite(t) ? t : void 0;
15
+ }, s = (e) => {
16
+ if (e.oversized !== void 0 || e.netChange === !0) throw Error("timeline event has an incomplete or reconstructed row image");
17
+ let t = o(e.op === "delete" ? e.old : e.new);
18
+ if (t === void 0 || e.op === "update" && (e.old == null || o(e.old) === void 0)) throw Error("timeline event lacks a required row image or identity");
19
+ return t;
20
+ }, c = (e, t) => {
21
+ let n = s(t);
22
+ return t.op === "insert" ? e.filter((e) => e.id !== n) : t.op === "update" ? e.map((e) => e.id === n ? t.old : e) : [...e, t.old];
23
+ }, l = (e, t) => {
24
+ if (t.length === 0) return e;
25
+ let n = [...e], r = /* @__PURE__ */ new Map(), i = (e, t) => {
26
+ let n = e.id, i = r.get(n);
27
+ i ? i.add(t) : r.set(n, /* @__PURE__ */ new Set([t]));
28
+ };
29
+ n.forEach((e, t) => {
30
+ e && i(e, t);
31
+ });
32
+ for (let e of t) {
33
+ let t = s(e);
34
+ if (e.op === "delete") {
35
+ i(e.old, n.length), n.push(e.old);
36
+ continue;
37
+ }
38
+ let a = r.get(t);
39
+ if (a) {
40
+ r.delete(t);
41
+ for (let t of a) e.op === "insert" ? n[t] = void 0 : (n[t] = e.old, i(e.old, t));
42
+ }
43
+ }
44
+ let a = 0;
45
+ for (let e of n) e !== void 0 && (n[a++] = e);
46
+ return n.length = a, n;
47
+ }, u = class {
48
+ mode;
49
+ capacity;
50
+ now;
51
+ historyId = e();
52
+ get recorderId() {
53
+ return this.historyId;
54
+ }
55
+ invalidate() {
56
+ this.historyId = e(), this.ring.clear(), this.seqCounter = 0;
57
+ }
58
+ ring;
59
+ seqCounter = 0;
60
+ constructor(e = "interesting", n = 5e3, r = () => Date.now()) {
61
+ this.mode = e, this.capacity = n, this.now = r, this.ring = new t(n);
62
+ }
63
+ get enabled() {
64
+ return this.mode !== "off";
65
+ }
66
+ record(e) {
67
+ if (this.mode === "off" || e.table === "_voltro_timeline" || this.mode === "interesting" && e.table.startsWith("_voltro")) return -1;
68
+ let t = ++this.seqCounter, n = e.tenantId ?? e.new?.tenantId ?? e.old?.tenantId ?? null;
69
+ return this.ring.push({
70
+ seq: t,
71
+ ts: this.now(),
72
+ table: e.table,
73
+ op: e.op,
74
+ old: a(e.old),
75
+ new: a(e.new),
76
+ tenantId: n,
77
+ ...e.netChange === !0 ? { netChange: !0 } : {},
78
+ ...e.oversized === void 0 ? {} : { oversized: e.oversized }
79
+ }), t;
80
+ }
81
+ currentSeq() {
82
+ return this.seqCounter;
83
+ }
84
+ snapshot(e = {}) {
85
+ let t = [...this.ring], n = 0;
86
+ for (let r of t) (e.table === void 0 || r.table === e.table) && (e.tenantId === void 0 || r.tenantId === e.tenantId) && (e.from === void 0 || r.seq >= e.from) && (e.to === void 0 || r.seq <= e.to) && (t[n++] = r);
87
+ return t.length = n, e.limit !== void 0 && t.length > e.limit && (t = t.slice(t.length - e.limit)), t;
88
+ }
89
+ replay(e, t, n) {
90
+ let r = [...this.ring], i = 0;
91
+ for (let n of r) n.table === e && n.seq > t && (r[i++] = n);
92
+ return r.length = i, r.sort((e, t) => t.seq - e.seq), l(n, r);
93
+ }
94
+ }, d, f = () => (d === void 0 && (d = new u(n())), d), p = (e) => {
95
+ d = e;
96
+ }, m = (e) => {
97
+ let t = f();
98
+ if (t.enabled) try {
99
+ t.record(e);
100
+ } catch {
101
+ t.invalidate();
102
+ }
103
+ };
104
+ //#endregion
105
+ export { u as TimelineRecorder, f as getTimelineRecorder, m as recordTimelineEvent, a as redactRow, n as resolveTimelineMode, c as reverseEventOverRows, p as setTimelineRecorderForTest };
@@ -0,0 +1,5 @@
1
+ import { AsyncLocalStorage as e } from "node:async_hooks";
2
+ //#region src/traceContext.ts
3
+ var t = new e(), n = (e, n) => t.run(e, n), r = () => t.getStore();
4
+ //#endregion
5
+ export { r as currentTraceContext, n as runWithTraceContext };
@@ -0,0 +1,63 @@
1
+ import { SpanKind as e, SpanStatusCode as t } from "@opentelemetry/api";
2
+ //#region src/traceSink.ts
3
+ var n = {
4
+ [e.INTERNAL]: "internal",
5
+ [e.SERVER]: "server",
6
+ [e.CLIENT]: "client",
7
+ [e.PRODUCER]: "producer",
8
+ [e.CONSUMER]: "consumer"
9
+ }, r = (e) => e[0] * 1e3 + e[1] / 1e6, i = 512, a = 48, o = (e) => {
10
+ let t = {}, n = 0;
11
+ for (let r in e) {
12
+ if (n >= a) break;
13
+ let o = e[r];
14
+ if (typeof o == "string") t[r] = o.length > i ? `${o.slice(0, i)}…` : o;
15
+ else if (typeof o == "number" || typeof o == "boolean" || o == null) t[r] = o;
16
+ else {
17
+ let e;
18
+ try {
19
+ e = JSON.stringify(o);
20
+ } catch {
21
+ e = String(o);
22
+ }
23
+ t[r] = e.length > i ? `${e.slice(0, i)}…` : e;
24
+ }
25
+ n++;
26
+ }
27
+ return t;
28
+ }, s = {
29
+ [t.UNSET]: "unset",
30
+ [t.OK]: "ok",
31
+ [t.ERROR]: "error"
32
+ }, c = (e) => {
33
+ let t = e.spanContext(), i = e.events.length > 0 ? e.events.slice(0, 16).map((e) => ({
34
+ name: e.name,
35
+ tsMs: r(e.time),
36
+ ...e.attributes ? { attributes: o(e.attributes) } : {}
37
+ })) : void 0;
38
+ return {
39
+ traceId: t.traceId,
40
+ spanId: t.spanId,
41
+ parentSpanId: e.parentSpanContext?.spanId ?? null,
42
+ name: e.name,
43
+ kind: n[e.kind] ?? "internal",
44
+ attributes: o(e.attributes),
45
+ startMs: r(e.startTime),
46
+ endMs: r(e.endTime),
47
+ durationMs: r(e.duration),
48
+ status: s[e.status.code] ?? "unset",
49
+ ...e.status.message ? { statusMessage: e.status.message } : {},
50
+ ...i ? { events: i } : {}
51
+ };
52
+ }, l = (e) => ({
53
+ onStart: () => {},
54
+ onEnd: (t) => {
55
+ try {
56
+ e(c(t));
57
+ } catch {}
58
+ },
59
+ forceFlush: () => Promise.resolve(),
60
+ shutdown: () => Promise.resolve()
61
+ });
62
+ //#endregion
63
+ export { l as makeBufferingSpanProcessor };
@@ -0,0 +1,29 @@
1
+ import { id as e, integer as t, json as n, table as r, text as i, timestamp as a } from "@voltro/database";
2
+ //#region src/traceSpansTable.ts
3
+ var o = r("_voltro_traces", {
4
+ id: e({ prefix: "span" }),
5
+ traceId: i(),
6
+ spanId: i(),
7
+ parentSpanId: i().nullable(),
8
+ name: i(),
9
+ kind: i().oneOf([
10
+ "internal",
11
+ "server",
12
+ "client",
13
+ "producer",
14
+ "consumer"
15
+ ]).default("internal"),
16
+ status: i().oneOf([
17
+ "ok",
18
+ "error",
19
+ "unset"
20
+ ]).default("unset"),
21
+ statusMessage: i().nullable(),
22
+ startedAt: a(),
23
+ endedAt: a(),
24
+ durationMs: t(),
25
+ attributes: n().nullable(),
26
+ events: n().nullable()
27
+ }).index(["traceId", "startedAt"]).index(["startedAt"]).index(["status"]);
28
+ //#endregion
29
+ export { o as _voltroTracesTable };
@@ -0,0 +1,34 @@
1
+ import { Tracer as e } from "effect";
2
+ //#region src/traceparent.ts
3
+ var t = /^00-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/, n = (e) => `00-${e.traceId}-${e.spanId}-${e.sampled ? "01" : "00"}`, r = (e) => {
4
+ if (typeof e != "string") return null;
5
+ let n = t.exec(e.trim().toLowerCase());
6
+ if (n === null) return null;
7
+ let [, r, i, a] = n;
8
+ return r === "0".repeat(32) || i === "0".repeat(16) ? null : {
9
+ traceId: r,
10
+ spanId: i,
11
+ sampled: (parseInt(a, 16) & 1) == 1
12
+ };
13
+ }, i = (t) => {
14
+ let n = r(t);
15
+ if (n !== null) return e.externalSpan({
16
+ traceId: n.traceId,
17
+ spanId: n.spanId,
18
+ sampled: n.sampled
19
+ });
20
+ }, a = () => {
21
+ let e = globalThis.crypto, t = /* @__PURE__ */ new Uint8Array(16);
22
+ if (e?.getRandomValues !== void 0) e.getRandomValues(t);
23
+ else {
24
+ let e = `${Date.now().toString(16)}${Math.random().toString(16).slice(2)}`;
25
+ for (let n = 0; n < t.length; n += 1) {
26
+ let r = e.slice(n * 2 % e.length, n * 2 % e.length + 2);
27
+ t[n] = (parseInt(r, 16) || n + 1) ^ (Math.random() * 256 | 0);
28
+ }
29
+ }
30
+ let n = Array.from(t, (e) => e.toString(16).padStart(2, "0")).join("");
31
+ return n === "0".repeat(32) ? `${"0".repeat(31)}1` : n;
32
+ };
33
+ //#endregion
34
+ export { i as externalSpanFromTraceparent, n as formatTraceparent, a as newTraceId, r as parseTraceparent };
@@ -0,0 +1,12 @@
1
+ import { isMachineSubject as e, tenantScopedSubject as t } from "@voltro/protocol";
2
+ //#region src/transactionSubject.ts
3
+ var n = (n, r, i) => {
4
+ if (r === void 0) return n;
5
+ if (r.trim() === "") throw Error("transaction tenantId must be non-empty");
6
+ if (r === n.tenantId) return n;
7
+ if (i) throw Error("transaction cannot change tenant inside an active transaction");
8
+ if (!e(n)) throw Error("only a system context — the framework itself, or another app calling a `service:` procedure — can select another transaction tenant");
9
+ return t(n, r);
10
+ };
11
+ //#endregion
12
+ export { n as resolveTransactionSubject };
@@ -0,0 +1,155 @@
1
+ import { bootRefusal as e } from "./bootRefusal.js";
2
+ //#region src/trustedProxy.ts
3
+ var t = {
4
+ entries: [],
5
+ trustAll: !1
6
+ }, n = (e) => {
7
+ let t = e.trim();
8
+ if (t.length === 0) return;
9
+ if (t.startsWith("[")) {
10
+ let e = t.indexOf("]");
11
+ if (e < 0) return;
12
+ t = t.slice(1, e);
13
+ } else (t.match(/:/g) ?? []).length === 1 && t.includes(".") && (t = t.slice(0, t.indexOf(":")));
14
+ let n = t.indexOf("%");
15
+ return n >= 0 && (t = t.slice(0, n)), t = t.toLowerCase(), t.startsWith("::ffff:") && t.includes(".") && (t = t.slice(7)), t.length > 0 ? t : void 0;
16
+ }, r = (e) => {
17
+ let t = e.split(".");
18
+ if (t.length !== 4) return;
19
+ let n = /* @__PURE__ */ new Uint8Array(4);
20
+ for (let e = 0; e < 4; e++) {
21
+ let r = t[e];
22
+ if (!/^\d{1,3}$/.test(r)) return;
23
+ let i = Number(r);
24
+ if (i > 255) return;
25
+ n[e] = i;
26
+ }
27
+ return n;
28
+ }, i = (e) => {
29
+ if (!e.includes(":")) return;
30
+ let t = e.indexOf("::"), n, i;
31
+ if (t >= 0) {
32
+ if (e.indexOf("::", t + 1) >= 0) return;
33
+ n = e.slice(0, t).match(/[^:]+/g) ?? [], i = e.slice(t + 2).match(/[^:]+/g) ?? [];
34
+ } else n = e.split(":"), i = [];
35
+ let a = (e) => {
36
+ if (e.length === 0) return e;
37
+ let t = e[e.length - 1];
38
+ if (!t.includes(".")) return e;
39
+ let n = r(t);
40
+ if (!n) return;
41
+ let i = (n[0] << 8 | n[1]).toString(16), a = (n[2] << 8 | n[3]).toString(16);
42
+ return e[e.length - 1] = i, e.push(a), e;
43
+ }, o = a(n), s = a(i);
44
+ if (!o || !s) return;
45
+ let c = 8 - (o.length + s.length);
46
+ if (t < 0 ? c !== 0 : c < 0) return;
47
+ let l = [
48
+ ...o,
49
+ ...Array.from({ length: Math.max(0, c) }, () => "0"),
50
+ ...s
51
+ ];
52
+ if (l.length !== 8) return;
53
+ let u = /* @__PURE__ */ new Uint8Array(16);
54
+ for (let e = 0; e < 8; e++) {
55
+ let t = l[e];
56
+ if (!/^[0-9a-f]{1,4}$/.test(t)) return;
57
+ let n = Number.parseInt(t, 16);
58
+ u[e * 2] = n >> 8 & 255, u[e * 2 + 1] = n & 255;
59
+ }
60
+ return u;
61
+ }, a = (e) => e.includes(":") ? i(e) : r(e), o = (e, t, n) => {
62
+ if (e.length !== t.length || n < 0 || n > e.length * 8) return !1;
63
+ let r = n >> 3;
64
+ for (let n = 0; n < r; n++) if (e[n] !== t[n]) return !1;
65
+ let i = n & 7;
66
+ if (i === 0) return !0;
67
+ let a = 255 << 8 - i & 255;
68
+ return (e[r] & a) === (t[r] & a);
69
+ }, s = {
70
+ loopback: ["127.0.0.0/8", "::1/128"],
71
+ private: [
72
+ "10.0.0.0/8",
73
+ "172.16.0.0/12",
74
+ "192.168.0.0/16",
75
+ "100.64.0.0/10",
76
+ "169.254.0.0/16",
77
+ "fc00::/7",
78
+ "fe80::/10"
79
+ ]
80
+ }, c = (e) => {
81
+ let t = e.trim().toLowerCase();
82
+ return t === "private" || t === "uniquelocal" ? s.private : t === "loopback" ? s.loopback : [e.trim()];
83
+ }, l = (e, t) => {
84
+ if (t.trustAll) return !0;
85
+ if (e === void 0) return !1;
86
+ let r = n(e);
87
+ if (r === void 0) return !1;
88
+ let i = a(r);
89
+ if (i === void 0) return !1;
90
+ for (let e of t.entries) for (let t of c(e)) {
91
+ let e = t.indexOf("/"), r = n(e >= 0 ? t.slice(0, e) : t);
92
+ if (r === void 0) continue;
93
+ let s = a(r);
94
+ if (s === void 0) continue;
95
+ let c = e >= 0 ? Number(t.slice(e + 1)) : s.length * 8;
96
+ if (Number.isInteger(c) && o(i, s, c)) return !0;
97
+ }
98
+ return !1;
99
+ }, u = (n, r = process.env, i) => {
100
+ let a = n !== void 0 && n.length > 0 ? n : (r.VOLTRO_TRUSTED_PROXIES ?? "").split(",").map((e) => e.trim()).filter((e) => e.length > 0), o = a.length === 0 ? t : a.length === 1 && a[0] === "*" ? {
101
+ entries: [],
102
+ trustAll: !0
103
+ } : a.length === 1 && /^\d+$/.test(a[0]) ? Number(a[0]) > 0 ? {
104
+ entries: [],
105
+ trustAll: !1,
106
+ hops: Number(a[0])
107
+ } : t : {
108
+ entries: a,
109
+ trustAll: !1
110
+ }, s = d(i, r);
111
+ if (s === void 0) return o;
112
+ if (o === t) throw e(`security.clientAddressHeader is '${s}', but no trusted proxy is declared, so the header would never be believed. Declare the proxy that sets it in security.trustedProxies (or VOLTRO_TRUSTED_PROXIES).`);
113
+ return {
114
+ ...o,
115
+ clientAddressHeader: s
116
+ };
117
+ }, d = (t, n) => {
118
+ let r = (t ?? n.VOLTRO_CLIENT_ADDRESS_HEADER ?? "").trim().toLowerCase();
119
+ if (r !== "") {
120
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(r)) throw e(`security.clientAddressHeader '${r}' is not a valid HTTP header name.`);
121
+ if (r === "x-forwarded-for") throw e("security.clientAddressHeader is 'x-forwarded-for', which is always read as a chain. Remove the option; security.trustedProxies alone governs it.");
122
+ return r;
123
+ }
124
+ }, f = (e) => {
125
+ let { socketAddress: t, xForwardedFor: r, config: i } = e, a = t === void 0 ? void 0 : n(t) ?? t, o = p(e, a);
126
+ if (o !== void 0) return o;
127
+ if (r === void 0 || r.trim() === "") return a;
128
+ let s = r.split(",").map((e) => n(e) ?? e.trim()), c = 0;
129
+ for (let e of s) e.length > 0 && (s[c++] = e);
130
+ if (s.length = c, s.length === 0) return a;
131
+ if (i.trustAll) return s[0];
132
+ if (i.hops !== void 0) {
133
+ let e = s.length + (a === void 0 ? 0 : 1), t = Math.max(0, e - 1 - i.hops);
134
+ return t === s.length ? a : s[t];
135
+ }
136
+ if (!l(a, i)) return a;
137
+ for (let e = s.length - 1; e >= 0; e--) if (!l(s[e], i)) return s[e];
138
+ return s[0];
139
+ }, p = (e, t) => {
140
+ let { config: r, clientAddressHeader: i } = e;
141
+ if (r.clientAddressHeader === void 0 || i === void 0 || !(r.trustAll || r.hops !== void 0 || l(t, r)) || i.includes(",")) return;
142
+ let o = n(i);
143
+ return o !== void 0 && a(o) !== void 0 ? o : void 0;
144
+ }, m = (e) => f({
145
+ socketAddress: e.socketAddress,
146
+ xForwardedFor: e.headers["x-forwarded-for"],
147
+ clientAddressHeader: e.config.clientAddressHeader === void 0 ? void 0 : e.headers[e.config.clientAddressHeader],
148
+ config: e.config
149
+ }), h = (e) => {
150
+ if (e.socketEncrypted) return !0;
151
+ let t = e.xForwardedProto;
152
+ return t === void 0 || t.trim() === "" || !(e.config.trustAll || e.config.hops !== void 0 || l(e.socketAddress, e.config)) ? !1 : t.split(",")[0].trim().toLowerCase() === "https";
153
+ };
154
+ //#endregion
155
+ export { t as NO_TRUSTED_PROXIES, a as ipToBytes, l as isTrustedProxy, n as normalizeIp, f as resolveClientAddress, m as resolveRequestClientAddress, h as resolveRequestIsHttps, u as resolveTrustedProxies };
package/dist/undo.js ADDED
@@ -0,0 +1,129 @@
1
+ //#region src/undo.ts
2
+ var e = (e) => {
3
+ if (e.id !== void 0) return e.id;
4
+ let t = e.next?.id;
5
+ if (typeof t == "string") return t;
6
+ let n = e.prev?.id;
7
+ return typeof n == "string" ? n : void 0;
8
+ }, t = (t) => {
9
+ let n = e(t);
10
+ switch (t.op) {
11
+ case "insert": return n === void 0 ? null : {
12
+ table: t.table,
13
+ op: "delete",
14
+ id: n
15
+ };
16
+ case "update": return n !== void 0 && t.prev != null ? {
17
+ table: t.table,
18
+ op: "update",
19
+ id: n,
20
+ values: t.prev
21
+ } : null;
22
+ case "softDelete": return n === void 0 ? null : {
23
+ table: t.table,
24
+ op: "undelete",
25
+ id: n
26
+ };
27
+ case "delete": return t.prev == null ? null : {
28
+ table: t.table,
29
+ op: "insert",
30
+ row: t.prev
31
+ };
32
+ }
33
+ }, n = (e) => {
34
+ if (e.crossesAction) throw new r(e.invocationId, "action");
35
+ let n = [];
36
+ for (let r = e.changes.length - 1; r >= 0; --r) {
37
+ let i = t(e.changes[r]);
38
+ i !== null && n.push(i);
39
+ }
40
+ return n;
41
+ }, r = class extends Error {
42
+ invocationId;
43
+ boundary;
44
+ _tag = "UndoBoundaryError";
45
+ constructor(e, t) {
46
+ super(t === "action" ? `cannot undo invocation ${e}: it crossed an action (external side effect)` : `cannot undo invocation ${e}: the row changed since (conflict)`), this.invocationId = e, this.boundary = t, this.name = "UndoBoundaryError";
47
+ }
48
+ }, i = (e, t, n = ["updatedAt", "updatedBy"]) => {
49
+ if (t == null) return e == null;
50
+ if (e == null) return !1;
51
+ let r = new Set(n);
52
+ for (let [n, i] of Object.entries(t)) if (!r.has(n) && !Object.is(e[n], i) && JSON.stringify(e[n]) !== JSON.stringify(i)) return !1;
53
+ return !0;
54
+ }, a = class {
55
+ capacity;
56
+ done = [];
57
+ undone = [];
58
+ constructor(e = 100) {
59
+ this.capacity = e;
60
+ }
61
+ push(e) {
62
+ this.done.push(e), this.done.length > this.capacity && this.done.shift(), this.undone = [];
63
+ }
64
+ get canUndo() {
65
+ return this.done.length > 0;
66
+ }
67
+ get canRedo() {
68
+ return this.undone.length > 0;
69
+ }
70
+ peekUndo() {
71
+ return this.done[this.done.length - 1];
72
+ }
73
+ peekRedo() {
74
+ return this.undone[this.undone.length - 1];
75
+ }
76
+ undo() {
77
+ let e = this.done.pop();
78
+ return e !== void 0 && this.undone.push(e), e;
79
+ }
80
+ redo() {
81
+ let e = this.undone.pop();
82
+ return e !== void 0 && this.done.push(e), e;
83
+ }
84
+ snapshot() {
85
+ return {
86
+ undo: [...this.done],
87
+ redo: [...this.undone]
88
+ };
89
+ }
90
+ }, o = (e, t = /* @__PURE__ */ new Set()) => e.op === "insert" ? {
91
+ table: e.table,
92
+ op: "insert",
93
+ next: e.new ?? null
94
+ } : e.op === "update" ? {
95
+ table: e.table,
96
+ op: "update",
97
+ prev: e.old ?? null,
98
+ next: e.new ?? null
99
+ } : t.has(e.table) ? {
100
+ table: e.table,
101
+ op: "softDelete",
102
+ prev: e.old ?? null
103
+ } : {
104
+ table: e.table,
105
+ op: "delete",
106
+ prev: e.old ?? null
107
+ }, s = async (e, t) => {
108
+ switch (t.op) {
109
+ case "delete":
110
+ await e.delete(t.table, t.id);
111
+ return;
112
+ case "update":
113
+ await e.update(t.table, t.id, t.values);
114
+ return;
115
+ case "undelete":
116
+ await e.update(t.table, t.id, {
117
+ deletedAt: null,
118
+ deletedBy: null
119
+ });
120
+ return;
121
+ case "insert":
122
+ await e.insert(t.table, t.row);
123
+ return;
124
+ }
125
+ }, c = async (e, t) => {
126
+ for (let n of t) await s(e, n);
127
+ };
128
+ //#endregion
129
+ export { r as UndoBoundaryError, a as UndoStack, s as applyInverse, c as applyInverses, o as forwardChangeFromCdc, t as invertChange, i as isSafeToApply, n as synthesizeInverse };
@@ -0,0 +1,69 @@
1
+ import { applyInverses as e, isSafeToApply as t, synthesizeInverse as n } from "./undo.js";
2
+ import { eq as r } from "@voltro/database";
3
+ import { UndoConflict as i, UndoForbidden as a, UndoNotFound as o } from "@voltro/protocol";
4
+ //#region src/undoBuiltins.ts
5
+ var s = (e) => e.request?.subject?.id ?? null, c = (e) => e === !0 || e === 1, l = async (e, t, n) => (await e.query({
6
+ table: t,
7
+ predicate: r("id", n),
8
+ order: []
9
+ }))[0] ?? null, u = (e) => {
10
+ try {
11
+ let t = JSON.parse(String(e ?? "[]"));
12
+ return Array.isArray(t) ? t : [];
13
+ } catch {
14
+ return [];
15
+ }
16
+ }, d = (e) => e.id ?? e.next?.id ?? e.prev?.id, f = async (e, t) => {
17
+ let n = s(t);
18
+ return [...await t.store.query({
19
+ table: "_voltro_undo_log",
20
+ predicate: r("subjectId", n),
21
+ order: []
22
+ })].sort((e, t) => String(t.id).localeCompare(String(e.id))).slice(0, e.limit ?? 50).map((e) => ({
23
+ id: String(e.id),
24
+ tag: String(e.tag ?? ""),
25
+ label: e.label ?? null,
26
+ undone: c(e.undone),
27
+ crossesAction: c(e.crossesAction),
28
+ createdAt: String(e.createdAt ?? "")
29
+ }));
30
+ }, p = async (e, t) => {
31
+ let n = await l(t.store, "_voltro_undo_log", e.invocationId);
32
+ if (!n) throw new o({ invocationId: e.invocationId });
33
+ if ((n.subjectId ?? null) !== s(t)) throw new a({ invocationId: e.invocationId });
34
+ return n;
35
+ }, m = async (r, a) => {
36
+ let o = await p(r, a);
37
+ if (c(o.undone)) return { ok: !0 };
38
+ if (c(o.crossesAction)) throw new i({
39
+ invocationId: r.invocationId,
40
+ reason: "action"
41
+ });
42
+ let f = u(o.changes);
43
+ for (let e of f) {
44
+ let n = d(e);
45
+ if (n === void 0) continue;
46
+ let o = await l(a.store, e.table, n);
47
+ if (!t(o, e.next ?? null)) throw new i({
48
+ invocationId: r.invocationId,
49
+ reason: "conflict"
50
+ });
51
+ }
52
+ let m = {
53
+ invocationId: r.invocationId,
54
+ subject: s(a),
55
+ changes: f,
56
+ crossesAction: !1
57
+ };
58
+ return await e(a.store, n(m)), await a.store.update("_voltro_undo_log", r.invocationId, { undone: !0 }), { ok: !0 };
59
+ }, h = async (e, t) => {
60
+ let n = await p(e, t);
61
+ if (!c(n.undone)) return { ok: !0 };
62
+ for (let e of u(n.changes)) {
63
+ let n = d(e);
64
+ e.op === "insert" && e.next ? await t.store.insert(e.table, e.next) : e.op === "update" && n !== void 0 && e.next ? await t.store.update(e.table, n, e.next) : e.op === "delete" && n !== void 0 && await t.store.delete(e.table, n);
65
+ }
66
+ return await t.store.update("_voltro_undo_log", e.invocationId, { undone: !1 }), { ok: !0 };
67
+ };
68
+ //#endregion
69
+ export { m as applyUndoInvocation, h as redoUndoInvocation, f as undoLogExecutor };