@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,65 @@
1
+ //#region src/schemaRegistry.ts
2
+ var e = "voltro/audit", t = "voltro/softDelete", n = "voltro/expires", r = "voltro/tenant", i = "voltro/localFirst", a = (e) => {
3
+ let t = /* @__PURE__ */ new Map();
4
+ for (let n of e) {
5
+ let e = /* @__PURE__ */ new Set();
6
+ for (let t of n.appliedMixins ?? []) t.id !== void 0 && e.add(t.id);
7
+ let r = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map(), s, c = /* @__PURE__ */ new Set(), l = /* @__PURE__ */ new Set(), u = /* @__PURE__ */ new Set(), d;
8
+ for (let [e, t] of Object.entries(n.fields)) t.unique && r.add(e), t.type === "id" && (r.add(e), t.idScheme !== void 0 && (d = t.idScheme)), t.defaultFactory !== void 0 && i.set(e, t.defaultFactory), t.computed !== void 0 && a.set(e, t.computed), t.onUpdate !== void 0 && o.set(e, t.onUpdate), t.versionColumn === !0 && (s = e), t.generatedAs !== void 0 && c.add(e), t.crdtManaged === !0 && l.add(e), t.crdtSealed === !0 && u.add(e);
9
+ let f = n.appliedIndexes ?? [];
10
+ for (let e of f) {
11
+ let t = e.fields[0];
12
+ t !== void 0 && r.add(t);
13
+ }
14
+ let p = {
15
+ tableName: n.tableName,
16
+ mixinIds: e,
17
+ fields: new Set(Object.keys(n.fields)),
18
+ indexedFields: r,
19
+ compositeIndexes: f.map((e) => ({
20
+ name: e.name,
21
+ fields: [...e.fields]
22
+ })),
23
+ defaultFactories: i,
24
+ computedFields: a,
25
+ ...o.size > 0 ? { onUpdateFields: o } : {},
26
+ ...s === void 0 ? {} : { versionColumn: s },
27
+ generatedColumns: c,
28
+ crdtColumns: l,
29
+ sealedColumns: u,
30
+ ...d === void 0 ? {} : { idScheme: d },
31
+ declaredPrimaryKey: [...n.appliedPrimaryKey ?? []],
32
+ ...n.insertSchema === void 0 ? {} : { insertSchema: n.insertSchema },
33
+ ...n.validatePatchSchema === void 0 ? {} : { validatePatchSchema: n.validatePatchSchema }
34
+ };
35
+ t.set(n.tableName, p);
36
+ }
37
+ return {
38
+ tables: t,
39
+ hasMixin: (e, n) => t.get(e)?.mixinIds.has(n) ?? !1,
40
+ hasAudit: (e) => t.get(e)?.mixinIds.has("voltro/audit") ?? !1,
41
+ hasSoftDelete: (e) => t.get(e)?.mixinIds.has("voltro/softDelete") ?? !1,
42
+ hasExpires: (e) => t.get(e)?.mixinIds.has("voltro/expires") ?? !1,
43
+ hasTenant: (e) => t.get(e)?.mixinIds.has("voltro/tenant") ?? !1,
44
+ hasLocalFirst: (e) => t.get(e)?.mixinIds.has("voltro/localFirst") ?? !1,
45
+ hasField: (e, n) => t.get(e)?.fields.has(n) ?? !1,
46
+ anyFieldIndexed: (e, n) => {
47
+ let r = t.get(e);
48
+ if (r === void 0) return !1;
49
+ for (let e of n) if (r.indexedFields.has(e)) return !0;
50
+ return !1;
51
+ },
52
+ idScheme: (e) => t.get(e)?.idScheme,
53
+ declaredPrimaryKey: (e) => t.get(e)?.declaredPrimaryKey ?? c,
54
+ defaultFactories: (e) => t.get(e)?.defaultFactories ?? o,
55
+ computedFields: (e) => t.get(e)?.computedFields ?? s,
56
+ versionColumn: (e) => t.get(e)?.versionColumn,
57
+ generatedColumns: (e) => t.get(e)?.generatedColumns ?? l,
58
+ crdtColumns: (e) => t.get(e)?.crdtColumns ?? u,
59
+ sealedColumns: (e) => t.get(e)?.sealedColumns ?? u,
60
+ insertSchema: (e) => t.get(e)?.insertSchema,
61
+ validatePatchSchema: (e) => t.get(e)?.validatePatchSchema
62
+ };
63
+ }, o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map(), c = [], l = /* @__PURE__ */ new Set(), u = /* @__PURE__ */ new Set(), d = a([]);
64
+ //#endregion
65
+ export { e as VOLTRO_AUDIT_MIXIN_ID, n as VOLTRO_EXPIRES_MIXIN_ID, i as VOLTRO_LOCAL_FIRST_MIXIN_ID, t as VOLTRO_SOFT_DELETE_MIXIN_ID, r as VOLTRO_TENANT_MIXIN_ID, d as emptySchemaRegistry, a as makeSchemaRegistry };
@@ -0,0 +1,35 @@
1
+ //#region src/secretsResolver.ts
2
+ var e = { get: (e) => Promise.resolve(process.env[e]) }, t = e, n = (e) => {
3
+ t = e;
4
+ }, r = () => {
5
+ t = e;
6
+ }, i = async (e) => await t.get(e) ?? process.env[e], a = (e) => process.env[e], o = (e, t = 6e4, n = () => Date.now()) => {
7
+ let r = /* @__PURE__ */ new Map();
8
+ return { get: async (i) => {
9
+ let a = r.get(i);
10
+ if (a && n() - a.at < t) return a.value;
11
+ let o = await e.get(i);
12
+ return r.set(i, {
13
+ value: o,
14
+ at: n()
15
+ }), o;
16
+ } };
17
+ }, s = (e) => {
18
+ let t = {
19
+ accept: "application/json",
20
+ ...e.headers ?? {}
21
+ };
22
+ if (e.perKey) return o({ get: async (n) => {
23
+ let r = await fetch(`${e.url.replace(/\/$/, "")}/${encodeURIComponent(n)}`, { headers: t });
24
+ if (!r.ok) return;
25
+ let i = await r.json();
26
+ return typeof i.value == "string" ? i.value : void 0;
27
+ } }, e.ttlMs);
28
+ let n, r = 0, i = e.ttlMs ?? 6e4, a = () => (n !== void 0 && Date.now() - r < i || (r = Date.now(), n = fetch(e.url, { headers: t }).then((e) => e.ok ? e.json() : {}).catch(() => ({}))), n);
29
+ return { get: async (e) => {
30
+ let t = (await a())[e];
31
+ return typeof t == "string" ? t : void 0;
32
+ } };
33
+ }, c = (t) => t === void 0 || t === "env" || typeof t == "object" && t.kind === "env" ? e : t.kind === "custom" ? t.backend : t.kind === "http" ? s(t) : e;
34
+ //#endregion
35
+ export { o as cachedBackend, e as envSecretsBackend, s as httpSecretsBackend, r as resetSecretsBackend, i as resolveSecret, a as resolveSecretSync, c as resolveSecretsBackend, n as setSecretsBackend };
@@ -0,0 +1,33 @@
1
+ //#region src/securityHeaders.ts
2
+ var e = "default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'", t = "frame-ancestors 'none'; base-uri 'none'; object-src 'none'", n = "max-age=15552000; includeSubDomains", r = /* @__PURE__ */ new Set(["no-referrer"]), i = (e) => {
3
+ if (e === !1) return !0;
4
+ let t = e.split(",").map((e) => e.trim().toLowerCase()).filter((e) => e !== ""), n = t[t.length - 1];
5
+ return n === void 0 || !r.has(n);
6
+ }, a = (e) => {
7
+ let t = e?.trim().toLowerCase();
8
+ return t === "off" || t === "default" || t === "strict" ? t : void 0;
9
+ }, o = (e) => {
10
+ if (e === void 0) return;
11
+ let t = e.trim();
12
+ if (t !== "") return t.toLowerCase() !== "off" && t;
13
+ }, s = (e, t = process.env) => {
14
+ let n = e?.mode ?? a(t.VOLTRO_SECURITY_HEADERS) ?? "default", r = e?.csp ?? o(t.VOLTRO_CSP) ?? "default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'";
15
+ return {
16
+ mode: n,
17
+ csp: r,
18
+ cspHtml: e?.cspHtml ?? o(t.VOLTRO_CSP_HTML) ?? (n === "strict" ? r : "frame-ancestors 'none'; base-uri 'none'; object-src 'none'"),
19
+ hsts: e?.hsts ?? o(t.VOLTRO_HSTS) ?? "max-age=15552000; includeSubDomains",
20
+ frameOptions: e?.frameOptions ?? "DENY",
21
+ referrerPolicy: e?.referrerPolicy ?? "strict-origin-when-cross-origin",
22
+ contentTypeOptions: e?.contentTypeOptions ?? "nosniff",
23
+ extra: e?.extra ?? {}
24
+ };
25
+ }, c = (e, t) => {
26
+ if (e.mode === "off") return {};
27
+ let n = (t.contentType ?? "").toLowerCase().includes("text/html") ? e.cspHtml : e.csp, r = {};
28
+ n !== !1 && (r["content-security-policy"] = n), e.frameOptions !== !1 && (r["x-frame-options"] = e.frameOptions), e.referrerPolicy !== !1 && (r["referrer-policy"] = e.referrerPolicy), e.contentTypeOptions !== !1 && (r["x-content-type-options"] = e.contentTypeOptions), e.hsts !== !1 && t.https && (r["strict-transport-security"] = e.hsts);
29
+ for (let [t, n] of Object.entries(e.extra)) r[t.toLowerCase()] = n;
30
+ return r;
31
+ };
32
+ //#endregion
33
+ export { e as API_CSP, n as DEFAULT_HSTS, t as HTML_CSP, r as ORIGIN_NULLING_REFERRER_POLICIES, i as preservesSameOriginHeader, s as resolveSecurityHeaders, c as securityHeadersFor };
@@ -0,0 +1,305 @@
1
+ import { enterReadRecording as e } from "./readRecorder.js";
2
+ import { getRowFilter as t, makeDefaultRefilter as n, resolveRowFilterScope as r } from "./rowFilter.js";
3
+ import { isComputedQuery as i } from "./computedQuery.js";
4
+ import { TX_READ_VERSIONS_KEY as a, isTransactionReadConflict as o } from "./storeMiddleware.js";
5
+ import { evaluateTouchedRules as s, makeRuleCapture as c } from "./businessRules.js";
6
+ import { enforceApproval as l } from "./approvals.js";
7
+ import { Cause as u, Effect as d, Exit as f, Option as p } from "effect";
8
+ import { isTransientContention as m } from "@voltro/database";
9
+ import { SubjectService as h, checkGuardsEffect as g, currentSubjectAuthority as _, recheckGuardsEffect as v } from "@voltro/protocol";
10
+ import { createLogger as y } from "@voltro/logger";
11
+ //#region src/servePipeline.ts
12
+ var b = (e, t) => d.runPromiseExit(e, t).then((e) => {
13
+ if (f.isSuccess(e)) return e.value;
14
+ let t = u.findErrorOption(e.cause);
15
+ throw p.isSome(t) ? t.value : u.squash(e.cause);
16
+ }), x = (e) => b(e), S = (e, t) => e.pipe(d.provideService(h, t)), C = async (e, t, n, r) => {
17
+ let i = await d.runPromise(g(t, e.guards, n, {
18
+ defaultDeny: r === !0,
19
+ procedure: e.name
20
+ }));
21
+ if (i) throw i;
22
+ }, w = async (e, t, n, r) => {
23
+ e.requiresApproval !== void 0 && await l({
24
+ procedure: e.name,
25
+ kind: t,
26
+ policy: e.requiresApproval,
27
+ subject: n.subject,
28
+ input: r,
29
+ traceId: n.traceId
30
+ });
31
+ }, T = y({ scope: "voltro:rpc" }), E = async (e) => {
32
+ if (t() === void 0) return e;
33
+ let n = await x(r(e.subject, (e) => {
34
+ T.error("row filter failed to load", {}, e instanceof Error ? e : Error(String(e)));
35
+ }));
36
+ return {
37
+ ...e,
38
+ rowFilter: n
39
+ };
40
+ }, D = (e) => ({
41
+ ...e.surface === void 0 ? {} : { surface: e.surface },
42
+ ...e.publicApi === void 0 ? {} : { publicApi: e.publicApi },
43
+ ...e.remoteAddr === void 0 ? {} : { remoteAddr: e.remoteAddr }
44
+ }), O = (e) => {
45
+ let t = e.descriptor.target;
46
+ if (t == null) return [];
47
+ let n = Array.isArray(t) ? t : [t], r = /* @__PURE__ */ new Set();
48
+ for (let e of n) {
49
+ let t = e.table;
50
+ typeof t == "string" && t.length > 0 && r.add(t);
51
+ }
52
+ return [...r];
53
+ }, k = async (e) => {
54
+ let { store: t, target: n, procedure: r, input: i, output: a } = e;
55
+ if (n == null) return;
56
+ let o = Array.isArray(n) ? n : [n];
57
+ for (let e of o) {
58
+ let n = e.relations;
59
+ if (n !== void 0) for (let [o, s] of Object.entries(n)) {
60
+ let n = s.junction, c = i?.[o];
61
+ if (c === void 0) continue;
62
+ if (!Array.isArray(c)) throw Error(`${r}: declared relation '${o}' → '${n}' expects an ARRAY of target ids in input.${o}, got ${typeof c}. Omit the field to leave the links untouched; send [] to clear them.`);
63
+ let l = a?.id ?? i?.id;
64
+ if (typeof l != "string" || l.length === 0) throw Error(`${r}: declared relation '${o}' → '${n}' needs the written row's id — neither output.id nor input.id is a string. Return the row (or its id) from the executor.`);
65
+ let u = t.relationLinks(n, e.table, l), d = u.columns;
66
+ if (d.anchorColumn !== s.anchorColumn || d.targetColumn !== s.targetColumn) throw Error(`${r}: declared relation '${o}' → '${n}' names columns the junction does not have. Declared { anchorColumn: '${s.anchorColumn}', targetColumn: '${s.targetColumn}' }; '${n}' resolves to { anchorColumn: '${d.anchorColumn}', targetColumn: '${d.targetColumn}' } for anchor table '${e.table}'. The client patches the junction optimistically from THIS declaration, so a wrong pair would show links the write never made — fix the declaration to the pair named here.`);
67
+ await u.set(c.map((e) => String(e)));
68
+ }
69
+ }
70
+ }, A = (e, t) => e === void 0 ? {} : { enqueueOutcome: async (n, r, i) => {
71
+ let a = e(t);
72
+ if (a === void 0) throw TypeError("Invocation outcome outbox is unavailable");
73
+ return a.enqueue(n, r, i);
74
+ } }, j = async (e) => {
75
+ let t = `${e.kind}.${e.name}`, n = Date.now(), r = "ok";
76
+ try {
77
+ if (e.interceptor) {
78
+ let t = d.tryPromise({
79
+ try: () => e.runExecutor(),
80
+ catch: (e) => e
81
+ }), n = e.interceptor(t, {
82
+ tag: e.name,
83
+ kind: e.kind,
84
+ input: e.input,
85
+ subject: e.requestContext.subject,
86
+ traceId: e.requestContext.traceId,
87
+ ...e.enqueueOutcome === void 0 ? {} : { enqueueOutcome: e.enqueueOutcome },
88
+ ...e.requestContext.spanId === void 0 ? {} : { spanId: e.requestContext.spanId },
89
+ ...e.target !== void 0 && e.target.length > 0 ? { target: e.target } : {},
90
+ ...D(e.requestContext)
91
+ }), r = await d.runPromiseExit(n, e.requestContext.signal === void 0 ? void 0 : { signal: e.requestContext.signal });
92
+ if (f.isSuccess(r)) return r.value;
93
+ throw u.squash(r.cause);
94
+ }
95
+ return await e.runExecutor();
96
+ } catch (e) {
97
+ throw r = "error", e;
98
+ } finally {
99
+ e.recordMetric?.({
100
+ kind: "rpc",
101
+ tag: t,
102
+ durationMs: Date.now() - n,
103
+ status: r
104
+ });
105
+ }
106
+ }, M = (e) => m(e) || o(e), N = 3, P = (e) => new Promise((t) => setTimeout(t, e)), F = async (e, t) => {
107
+ let n = t?.delay ?? ((e) => P(e * 20 + Math.floor(Math.random() * 30))), r = t?.attempts ?? 3;
108
+ for (let t = 1;; t++) try {
109
+ return await e();
110
+ } catch (e) {
111
+ if (t >= r || !M(e)) throw o(e) ? e.conflict : e;
112
+ await n(t);
113
+ }
114
+ }, I = (e) => (t, n, r) => j({
115
+ name: t.descriptor.name,
116
+ kind: "mutation",
117
+ input: n,
118
+ requestContext: r,
119
+ ...A(e.outcomeOutbox, r),
120
+ target: O(t),
121
+ ...e.interceptor ? { interceptor: e.interceptor } : {},
122
+ ...e.recordMetric ? { recordMetric: e.recordMetric } : {},
123
+ runExecutor: async () => {
124
+ let i = Date.now();
125
+ await C(t.descriptor, r.subject, n, e.defaultDeny), await w(t.descriptor, "mutation", r, n);
126
+ let o = await E(r), l = e.resolveStore === void 0 ? e.store : await e.resolveStore(o), u = [], f = [], p = await F(async () => l.transactional(async (p) => {
127
+ u.length = 0, f.length = 0, p[Symbol.for("@voltro/runtime:tx-fold-releases")] = f, p[a] = /* @__PURE__ */ new Map();
128
+ let m = e.undoCapture?.(p), h = m ? m.tx : p, g = c(h), _ = e.buildContext(o, g.tx, {
129
+ committedStore: l,
130
+ afterCommit: (e) => {
131
+ u.push(e);
132
+ }
133
+ }), v = t.executor(n, _), y = d.isEffect(v) ? await e.runEffect(S(v, r.subject), _.store) : await v;
134
+ if (await k({
135
+ store: _.store,
136
+ target: t.descriptor.target,
137
+ procedure: t.descriptor.name,
138
+ input: n,
139
+ output: y
140
+ }), m && await m.persist(_.store, {
141
+ tag: t.descriptor.name,
142
+ subject: r.subject,
143
+ traceId: r.traceId
144
+ }), await s({
145
+ touched: g.drain(),
146
+ store: _.store,
147
+ subject: r.subject
148
+ }), e.beforeMutationCommit !== void 0) {
149
+ let a = O(t), s = e.beforeMutationCommit({
150
+ tag: t.descriptor.name,
151
+ kind: "mutation",
152
+ input: n,
153
+ output: y,
154
+ subject: o.subject,
155
+ traceId: o.traceId,
156
+ store: _.store,
157
+ ..._.outbox === void 0 ? {} : { outbox: _.outbox },
158
+ startedAt: i,
159
+ durationMs: Math.max(0, Date.now() - i),
160
+ ...o.spanId === void 0 ? {} : { spanId: o.spanId },
161
+ ...a.length > 0 ? { target: a } : {},
162
+ ...D(o)
163
+ });
164
+ await e.runEffect(S(s, r.subject), _.store);
165
+ }
166
+ return y;
167
+ }), { ...e.replayAttempts === void 0 ? {} : { attempts: e.replayAttempts } });
168
+ if (f.length > 0) {
169
+ let t = e.buildContext(o, l, {
170
+ committedStore: l,
171
+ afterCommit: () => {}
172
+ }).store;
173
+ for (let e of f.splice(0)) await t.update(e.table, e.pk, e.patch);
174
+ }
175
+ for (let e of u) await e();
176
+ return p;
177
+ }
178
+ }), L = (e) => (t, n, r, i) => j({
179
+ name: t.name,
180
+ kind: "workflow",
181
+ input: n,
182
+ requestContext: r,
183
+ ...A(e.outcomeOutbox, r),
184
+ ...e.interceptor ? { interceptor: e.interceptor } : {},
185
+ ...e.recordMetric ? { recordMetric: e.recordMetric } : {},
186
+ runExecutor: async () => (await C(t, r.subject, n, e.defaultDeny), i())
187
+ }), R = (e) => e && typeof e == "object" && "descriptor" in e ? e.descriptor : e, z = (e) => typeof e == "object" && !!e && !Array.isArray(e) && "table" in e && "order" in e, B = (e) => {
188
+ let t = e.descriptor.source;
189
+ return t == null ? [] : typeof t == "string" ? [t] : [...t];
190
+ }, V = (e, t, n) => (r) => () => d.runPromise(v(r, e.descriptor.guards, t, {
191
+ defaultDeny: n === !0,
192
+ procedure: e.descriptor.name
193
+ })), H = (e, n) => e.rowFilter !== void 0 || t() === void 0 ? n(e) : d.promise(() => E(e)).pipe(d.flatMap((e) => {
194
+ let t = n(e);
195
+ return d.isEffect(t) ? t : d.succeed(t);
196
+ })), U = (n) => (r, a) => (o) => {
197
+ if (o.rowFilter === void 0 && t() !== void 0) return H(o, (e) => U(n)(r, a)(e));
198
+ let s = n.buildContext(o), c = V(r, a, n.defaultDeny)(o.subject), l = async () => {
199
+ let e = await c();
200
+ if (e) throw e;
201
+ let t = {
202
+ ...o,
203
+ subject: _(o.subject)
204
+ }, i = n.buildContext(await E(t)), s = r.executor(a, i), l = d.isEffect(s) ? await b(n.provideEffect(S(s, t.subject), i.store), o.signal === void 0 ? void 0 : { signal: o.signal }) : R(await Promise.resolve(s)), u = await c();
205
+ if (u) throw u;
206
+ return l;
207
+ }, u = (e) => {
208
+ let t = R(e);
209
+ if (z(t)) {
210
+ let e = n.finalize(t, o), i = B(r);
211
+ if (i.length === 0) return e;
212
+ let a = e.dependentTables ?? [];
213
+ return {
214
+ ...e,
215
+ dependentTables: [.../* @__PURE__ */ new Set([...a, ...i])]
216
+ };
217
+ }
218
+ return {
219
+ __voltroComputed: !0,
220
+ value: t,
221
+ dependentTables: B(r),
222
+ tenantId: o.subject?.tenantId ?? null,
223
+ recompute: l
224
+ };
225
+ }, f = (e) => {
226
+ let t = u(e);
227
+ return i(t) ? d.tryPromise({
228
+ try: c,
229
+ catch: (e) => e
230
+ }).pipe(d.flatMap((e) => e ? d.fail(e) : d.succeed(t))) : t;
231
+ }, p = (e) => {
232
+ let t = f(e);
233
+ return d.isEffect(t) ? t : d.succeed(t);
234
+ }, m = () => {
235
+ e(r.descriptor.name, B(r));
236
+ let t = r.executor(a, s);
237
+ return d.isEffect(t) ? n.provideEffect(S(t, o.subject), s.store).pipe(d.flatMap(p)) : t instanceof Promise ? d.promise(() => t).pipe(d.flatMap(p)) : f(t);
238
+ }, h = () => {
239
+ let e = r.descriptor.guards;
240
+ return (!e || e.length === 0) && n.defaultDeny !== !0 && o.subject.type !== "service" ? m() : g(o.subject, e, a, {
241
+ defaultDeny: n.defaultDeny === !0,
242
+ procedure: r.descriptor.name
243
+ }).pipe(d.flatMap((e) => {
244
+ if (e) return d.fail(e);
245
+ let t = m();
246
+ return d.isEffect(t) ? t : d.succeed(t);
247
+ }));
248
+ };
249
+ if (!n.interceptor) return h();
250
+ let v = d.suspend(() => {
251
+ let e = h();
252
+ return d.isEffect(e) ? e : d.succeed(e);
253
+ });
254
+ return n.interceptor(v, {
255
+ tag: r.descriptor.name,
256
+ kind: "query",
257
+ input: a,
258
+ subject: o.subject,
259
+ traceId: o.traceId,
260
+ ...o.spanId === void 0 ? {} : { spanId: o.spanId },
261
+ ...D(o)
262
+ });
263
+ }, W = (e) => async (t, n, r) => {
264
+ let a = await E(r), o = U(e)(t, n)(a), s = d.isEffect(o) ? await x(o) : o;
265
+ if (i(s)) return s.value;
266
+ let c = e.buildContext(a), l = await e.queryRows(s, a, c.store), u = await V(t, n, e.defaultDeny)(a.subject)();
267
+ if (u) throw u;
268
+ return l;
269
+ }, G = (e) => (t, r, a, o) => {
270
+ let s = !1, c;
271
+ return (async () => {
272
+ try {
273
+ let l = await E(a), u = U(e)(t, r)(l), f = d.isEffect(u) ? await x(u) : u, p = (e) => o(e), m = i(f) ? await e.subscribeComputed(f, p, l) : await e.subscribeDescriptor(f, p, l, V(t, r, e.defaultDeny)(l.subject), n(l.subject));
274
+ s ? m() : c = m;
275
+ } catch (e) {
276
+ o({
277
+ _tag: "error",
278
+ error: e instanceof Error ? {
279
+ message: e.message,
280
+ ..."_tag" in e ? { _tag: e._tag } : {}
281
+ } : e
282
+ });
283
+ }
284
+ })(), () => {
285
+ s = !0, c?.();
286
+ };
287
+ }, K = (t) => (n, r, i) => j({
288
+ name: n.descriptor.name,
289
+ kind: "action",
290
+ input: r,
291
+ requestContext: i,
292
+ ...A(t.outcomeOutbox, i),
293
+ target: O(n),
294
+ ...t.interceptor ? { interceptor: t.interceptor } : {},
295
+ ...t.recordMetric ? { recordMetric: t.recordMetric } : {},
296
+ runExecutor: async () => {
297
+ await C(n.descriptor, i.subject, r, t.defaultDeny), await w(n.descriptor, "action", i, r);
298
+ let a = t.buildContext(await E(i));
299
+ e(n.descriptor.name, B(n));
300
+ let o = n.executor(r, a);
301
+ return d.isEffect(o) ? t.runEffect(S(o, i.subject), a.store) : o;
302
+ }
303
+ });
304
+ //#endregion
305
+ export { N as DEFAULT_MUTATION_REPLAY_ATTEMPTS, k as applyDeclaredRelations, K as makeActionRunner, I as makeMutationRunner, W as makeOneShotQueryRunner, U as makeQueryDescriptorProducer, V as makeQueryReauthorizer, G as makeQuerySubscriber, L as makeWorkflowStartRunner, x as runProvidedEffect, F as runWithDeadlockRetry, E as withRowFilter, H as withScopedRequest };
@@ -0,0 +1,66 @@
1
+ import { SchemaAST as e } from "effect";
2
+ //#region src/serverOnlyAudit.ts
3
+ var t = (t) => {
4
+ let n = /* @__PURE__ */ new Set(), r = /* @__PURE__ */ new Set(), i = (t) => {
5
+ if (!r.has(t)) switch (r.add(t), t.encoding !== void 0 && i(e.toEncoded(t)), t._tag) {
6
+ case "Objects":
7
+ for (let e of t.propertySignatures) typeof e.name == "string" && n.add(e.name), i(e.type);
8
+ for (let e of t.indexSignatures) i(e.type);
9
+ break;
10
+ case "Arrays":
11
+ for (let e of t.elements) i(e);
12
+ for (let e of t.rest) i(e);
13
+ break;
14
+ case "Union":
15
+ for (let e of t.types) i(e);
16
+ break;
17
+ case "Suspend": i(t.thunk());
18
+ }
19
+ };
20
+ return i(t.ast), n;
21
+ }, n = (t) => {
22
+ let n = /* @__PURE__ */ new Set(), r = /* @__PURE__ */ new Set(), i = (t) => {
23
+ if (t.encoding !== void 0) return i(e.toEncoded(t));
24
+ switch (t._tag) {
25
+ case "Null": return !0;
26
+ case "Literal": return t.literal === null;
27
+ case "Union": return t.types.length > 0 && t.types.every(i);
28
+ case "Suspend": return i(t.thunk());
29
+ default: return !1;
30
+ }
31
+ }, a = (t) => {
32
+ if (!r.has(t)) switch (r.add(t), t.encoding !== void 0 && a(e.toEncoded(t)), t._tag) {
33
+ case "Objects":
34
+ for (let e of t.propertySignatures) typeof e.name == "string" && i(e.type) && n.add(e.name), a(e.type);
35
+ break;
36
+ case "Arrays":
37
+ for (let e of t.elements) a(e);
38
+ for (let e of t.rest) a(e);
39
+ break;
40
+ case "Union":
41
+ for (let e of t.types) a(e);
42
+ break;
43
+ case "Suspend": a(t.thunk());
44
+ }
45
+ };
46
+ return a(t.ast), n;
47
+ }, r = (e, r) => {
48
+ let i = e.source === void 0 ? [] : Array.isArray(e.source) ? e.source : [e.source];
49
+ if (i.length === 0) return [];
50
+ let a = t(e.output), o = n(e.output), s = [];
51
+ for (let t of i) for (let n of r.get(t) ?? []) a.has(n) && !o.has(n) && s.push({
52
+ query: e.name,
53
+ table: t,
54
+ column: n
55
+ });
56
+ return s;
57
+ }, i = (e) => {
58
+ if (e.length !== 0) return `A wire-reachable query exposes a server-only column:\n${e.map((e) => ` • query '${e.query}' output declares '${e.column}' — a .serverOnly() column of '${e.table}'`).join("\n")}\nThree ways out, in order of preference:\n 1. Omit the column from the query's output schema. The crud.* read helpers strip these automatically.\n 2. Keep the KEY but type it \`Schema.Null\` — for a view that must stay wire-compatible (an importer that breaks if the key vanishes) while never sending a value. The audit accepts that, because the schema then PROVES no value can flow instead of promising it. Pair it with a \`hasX\` boolean.\n 3. Drop the .serverOnly() marker, if the column may in fact reach a client.\nNote the audit reads the DECLARED shape, not what your handler emits: a field typed \`string\` that you always set to null still fails, and should — the next edit to that handler is one line away from sending it.`;
59
+ }, a = (e, t) => e.cache?.scope === "global" ? (e.source === void 0 ? [] : Array.isArray(e.source) ? e.source : [e.source]).filter((e) => t.has(e)).map((t) => ({
60
+ query: e.name,
61
+ table: t
62
+ })) : [], o = (e) => {
63
+ if (e.length !== 0) return `A cached query shares one entry across tenants:\n${e.map((e) => ` • query '${e.query}' caches scope:'global' while reading '${e.table}', a tenant()-scoped table`).join("\n")}\n\nscope:'global' stores ONE entry for every caller. On a tenant-scoped table the\nrows depend on who asked, so the first caller's rows are served to the next one.\n\nUse scope:'tenant' — one entry per org, none shared across orgs. Use 'subject'\nif the result also depends on WHICH person is asking, and 'global' only for data\nthat depends on neither.`;
64
+ };
65
+ //#endregion
66
+ export { a as cacheScopeLeaks, o as formatCacheScopeLeaks, i as formatServerOnlyLeaks, t as schemaPropertyNames, r as serverOnlyLeaks };
@@ -0,0 +1,15 @@
1
+ import { Effect as e, Layer as t } from "effect";
2
+ //#region src/serverTracer.ts
3
+ var n = Symbol.for("@voltro/runtime:serverTracer"), r = (() => {
4
+ let e = globalThis;
5
+ return e[n] ??= {};
6
+ })(), i = (e) => {
7
+ e === void 0 ? delete r.tracer : r.tracer = e;
8
+ }, a = () => r.tracer, o = t.effectDiscard(e.flatMap(e.tracer, (t) => e.sync(() => {
9
+ i(t);
10
+ }))), s = (t) => {
11
+ let n = r.tracer;
12
+ return n === void 0 ? t : e.withTracer(t, n);
13
+ };
14
+ //#endregion
15
+ export { o as publishServerTracerLayer, a as serverTracer, i as setServerTracer, s as withServerTracer };
@@ -0,0 +1,40 @@
1
+ import { createHash as e, randomBytes as t, timingSafeEqual as n } from "node:crypto";
2
+ import { SERVICE_NAME_PATTERN as r, serviceSubject as i } from "@voltro/protocol";
3
+ //#region src/serviceCredentials.ts
4
+ var a = "vsvc_", o = /^vsvc_[A-Za-z0-9_-]{43}$/, s = () => `${a}${t(32).toString("base64url")}`, c = (e) => o.test(e), l = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/-/g, "_").toUpperCase(), u = (e) => `VOLTRO_SERVICE_CALLER_${l(e)}_TOKEN`, d = (e) => `${u(e)}_NEXT`, f = (e) => `VOLTRO_API_${l(e)}_TOKEN`, p = (e) => `VOLTRO_API_${l(e)}_URL`, m = (e) => {
5
+ for (let t of Object.keys(e)) if (!r.test(t)) throw Error(`serviceCallers: ${JSON.stringify(t)} is not an app name — letters, digits and dashes, starting with a letter, at most 56 characters. It is the name the calling app binds this api under and the name its procedures list in \`service: { callers }\`.`);
6
+ }, h = (t) => e("sha256").update(t).digest(), g = (e, t) => n(e, h(t)), _ = (e, t) => [e[u(t)], e[d(t)]].filter((e) => typeof e == "string" && e !== ""), v = (e) => {
7
+ m(e.callers);
8
+ let t = Object.keys(e.callers), n = e.env ?? (() => process.env);
9
+ return {
10
+ id: "voltro-service",
11
+ claimsBearerPrefix: a,
12
+ verifyConfig: () => {
13
+ let e = n(), r = [], i = [];
14
+ for (let n of t) {
15
+ let t = _(e, n);
16
+ t.length === 0 && r.push(u(n));
17
+ for (let [e, r] of t.entries()) c(r) || i.push(e === 0 ? u(n) : d(n));
18
+ }
19
+ if (r.length > 0) throw Error(`serviceCallers: no token for ${r.join(", ")}. Every app listed in \`serviceCallers\` needs the credential it presents. \`voltro dev\` mints one per caller into both apps' \`.env.local\`; in production set the variable to the same value as the caller's \`VOLTRO_API_<NAME>_TOKEN\` (\`voltro secret generate service\` prints a fresh one).`);
20
+ if (i.length > 0) throw Error(`serviceCallers: ${i.join(", ")} ${i.length === 1 ? "is" : "are"} not a service token (\`${a}\` followed by 43 base64url characters). Generate one with \`voltro secret generate service\` rather than choosing a value by hand.`);
21
+ },
22
+ resolve: (e) => {
23
+ let r = e.headers.authorization ?? e.headers.Authorization;
24
+ if (r === void 0) return { kind: "skip" };
25
+ let a = /^Bearer\s+(\S+)$/i.exec(r)?.[1];
26
+ if (a === void 0 || !a.startsWith("vsvc_")) return { kind: "skip" };
27
+ let o = h(a), s = n(), c;
28
+ for (let e of t) for (let t of _(s, e)) g(o, t) && c === void 0 && (c = e);
29
+ return c === void 0 ? {
30
+ kind: "failed",
31
+ reason: "service credential matches no caller in `serviceCallers`"
32
+ } : {
33
+ kind: "matched",
34
+ subject: i(c)
35
+ };
36
+ }
37
+ };
38
+ };
39
+ //#endregion
40
+ export { a as SERVICE_TOKEN_PREFIX, f as apiBindingTokenEnv, p as apiBindingUrlEnv, m as assertServiceCallersConfig, c as isWellFormedServiceToken, s as mintServiceToken, d as serviceCallerNextTokenEnv, v as serviceCallerStrategy, u as serviceCallerTokenEnv, l as serviceEnvSegment };
@@ -0,0 +1,91 @@
1
+ import { pruneWakeups as e } from "./wakeRegistry.js";
2
+ import { wakeDueOnce as t } from "./wakeOrchestrator.js";
3
+ import { makeProcessAdapter as n } from "./wakeSupervisor.js";
4
+ import { isIdleNow as r, makeIdleGate as i } from "./idleDetector.js";
5
+ import { makeRouterActivity as a, makeWakeProxyServer as o } from "./wakeRouter.js";
6
+ //#region src/singleNodeOrchestrator.ts
7
+ var s = async (e) => {
8
+ try {
9
+ return (await e.query({
10
+ table: "_voltro_workflow_runs",
11
+ predicate: {
12
+ column: "status",
13
+ op: "eq",
14
+ value: "running"
15
+ },
16
+ order: [],
17
+ take: void 0,
18
+ skip: void 0,
19
+ projection: ["id"]
20
+ })).length;
21
+ } catch {
22
+ return 0;
23
+ }
24
+ }, c = async (e) => {
25
+ let i = n(e.supervisor), a = await t({
26
+ store: e.store,
27
+ adapter: i,
28
+ ...e.now === void 0 ? {} : { now: e.now },
29
+ ...e.log === void 0 ? {} : { log: e.log }
30
+ });
31
+ a > 0 && e.idleGate.reset();
32
+ let o = await r({
33
+ store: e.store,
34
+ signals: e.signals,
35
+ ...e.wakeLookaheadMs === void 0 ? {} : { wakeLookaheadMs: e.wakeLookaheadMs },
36
+ ...e.now === void 0 ? {} : { now: e.now }
37
+ }), s = !1;
38
+ return e.idleGate.update(o) && e.supervisor.isRunning() && (e.log?.info("orchestrator: idle grace elapsed — scaling app to zero"), await e.supervisor.stop(), e.idleGate.reset(), s = !0), {
39
+ woke: a,
40
+ stopped: s
41
+ };
42
+ }, l = async (t) => {
43
+ let n = i(t.idleGraceMs ?? 6e4), r = t.tickMs ?? 1e3, l = a(), u = 0, d = t.signals === void 0, f = t.signals ?? {
44
+ connectedClients: () => l.openConnections(),
45
+ inFlightRequests: () => l.inFlightRequests(),
46
+ activeWorkflowExecutions: () => u
47
+ }, p = o({
48
+ supervisor: t.supervisor,
49
+ targetHost: t.targetHost,
50
+ targetPort: t.targetPort,
51
+ onActivity: () => {
52
+ n.reset();
53
+ },
54
+ activity: l,
55
+ ...t.log === void 0 ? {} : { log: t.log }
56
+ }), m = await new Promise((e, n) => {
57
+ p.once("error", n), p.listen(t.publicPort, t.publicHost ?? "0.0.0.0", () => {
58
+ let n = p.address();
59
+ e(typeof n == "object" && n ? n.port : t.publicPort);
60
+ });
61
+ });
62
+ t.log?.info("orchestrator: router listening", { port: m });
63
+ let h = !1, g, _ = t.firedRetentionMs ?? 6e5, v = 0, y = async () => {
64
+ if (!h) {
65
+ try {
66
+ if (d && (u = await s(t.store)), await c({
67
+ store: t.store,
68
+ supervisor: t.supervisor,
69
+ signals: f,
70
+ idleGate: n,
71
+ ...t.wakeLookaheadMs === void 0 ? {} : { wakeLookaheadMs: t.wakeLookaheadMs },
72
+ ...t.log === void 0 ? {} : { log: t.log }
73
+ }), v++ % 60 == 0) {
74
+ let n = await e(t.store, { firedOlderThan: new Date(Date.now() - _) });
75
+ n > 0 && t.log?.info("orchestrator: pruned fired wakeups", { count: n });
76
+ }
77
+ } catch (e) {
78
+ t.log?.warn("orchestrator: tick failed", { reason: e instanceof Error ? e.message : String(e) });
79
+ }
80
+ h || (g = setTimeout(() => void y(), r));
81
+ }
82
+ };
83
+ return y(), {
84
+ port: m,
85
+ stop: async () => {
86
+ h = !0, g !== void 0 && clearTimeout(g), await new Promise((e) => p.close(() => e()));
87
+ }
88
+ };
89
+ };
90
+ //#endregion
91
+ export { s as countRunningWorkflows, c as runOrchestratorTick, l as runSingleNodeOrchestrator };