@voltro/runtime 0.85.0 → 0.87.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/CHANGELOG.md +5987 -0
  2. package/README.md +43 -0
  3. package/THIRD-PARTY-NOTICES.md +864 -253
  4. package/dist/aggregate.js +42 -0
  5. package/dist/aggregateExtrema.js +80 -0
  6. package/dist/aggregateIvm.js +115 -0
  7. package/dist/analyticsMirror.js +228 -0
  8. package/dist/analyticsSink.js +32 -0
  9. package/dist/apiKeyUsage.js +33 -0
  10. package/dist/apiKeys.js +227 -0
  11. package/dist/apiKeysWiring.js +114 -0
  12. package/dist/apiRoleBucketSampler.js +64 -0
  13. package/dist/apiRoleWorkloadHistory.js +56 -0
  14. package/dist/apiRoleWorkloadMetrics.js +78 -0
  15. package/dist/apiRoleWorkloadStorage.js +95 -0
  16. package/dist/approvalBuiltins.js +10 -0
  17. package/dist/approvals.js +262 -0
  18. package/dist/authMiddlewareLayer.js +48 -0
  19. package/dist/bindEvent.js +92 -0
  20. package/dist/bootListener.js +143 -0
  21. package/dist/bootRefusal.js +7 -0
  22. package/dist/bootStoreCodec.js +71 -0
  23. package/dist/businessRules.js +67 -0
  24. package/dist/changeClaimsTable.js +12 -0
  25. package/dist/circuitBreaker.js +1 -0
  26. package/dist/columnOnUpdate.js +23 -0
  27. package/dist/composeAnalytics.js +129 -0
  28. package/dist/computedQuery.js +4 -0
  29. package/dist/computedQueryCache.js +231 -0
  30. package/dist/connectionBuiltins.js +81 -0
  31. package/dist/connectionCredentials.js +66 -0
  32. package/dist/connectionState.js +34 -0
  33. package/dist/connectionVault.js +410 -0
  34. package/dist/connectionsFacade.js +56 -0
  35. package/dist/coordinatedSchedule.js +145 -0
  36. package/dist/crdtHistory.js +138 -0
  37. package/dist/crdtMerge.js +179 -0
  38. package/dist/crdtVersionsTable.js +25 -0
  39. package/dist/credentialBound.js +12 -0
  40. package/dist/crud.js +72 -0
  41. package/dist/dataStore.js +712 -0
  42. package/dist/dataloader.js +61 -0
  43. package/dist/declaredTablesGuard.js +49 -0
  44. package/dist/defineExecutor.js +4 -0
  45. package/dist/dependencyGraph.js +32 -0
  46. package/dist/devices.js +228 -0
  47. package/dist/devicesWiring.js +168 -0
  48. package/dist/dialectReplicationAdapter.js +29 -0
  49. package/dist/dispatcher.js +522 -0
  50. package/dist/effectStore.js +71 -0
  51. package/dist/engineClients.js +24 -0
  52. package/dist/eventBus.js +260 -0
  53. package/dist/eventPublish.js +49 -0
  54. package/dist/events.js +193 -0
  55. package/dist/expectation.js +52 -0
  56. package/dist/expectationEvaluator.js +308 -0
  57. package/dist/experiment.js +96 -0
  58. package/dist/experimentEvaluator.js +245 -0
  59. package/dist/externalizedTasks.js +38 -0
  60. package/dist/fieldCipher.js +130 -0
  61. package/dist/fieldKeyRegistry.js +295 -0
  62. package/dist/fieldKeyRekey.js +286 -0
  63. package/dist/fieldKeyScan.js +136 -0
  64. package/dist/fieldKeyring.js +77 -0
  65. package/dist/fingerprint.js +21 -0
  66. package/dist/finops.js +42 -0
  67. package/dist/finopsAccountant.js +216 -0
  68. package/dist/handlerBody.js +8 -0
  69. package/dist/healthProbes.js +55 -0
  70. package/dist/hot.d.ts +56 -0
  71. package/dist/hot.js +3 -0
  72. package/dist/hotDataStore.js +148 -0
  73. package/dist/hotDataStoreSnapshot.js +98 -0
  74. package/dist/hotDataStoreTables.js +18 -0
  75. package/dist/http-range.d.ts +53 -0
  76. package/dist/http-range.js +52 -0
  77. package/dist/httpClientSsrf.js +24 -0
  78. package/dist/httpClientTracing.js +14 -0
  79. package/dist/httpCompression.js +76 -0
  80. package/dist/httpInputStream.js +82 -0
  81. package/dist/httpKeepAlive.js +12 -0
  82. package/dist/httpRouteMounts.js +48 -0
  83. package/dist/idempotencyStore.js +110 -0
  84. package/dist/idleDetector.js +21 -0
  85. package/dist/index.d.ts +1249 -517
  86. package/dist/index.js +155 -15292
  87. package/dist/insertRequiredColumns.js +19 -0
  88. package/dist/inspectWorkflow.js +1 -0
  89. package/dist/instanceMembership.js +98 -0
  90. package/dist/ivm.js +125 -0
  91. package/dist/kvDataStore.js +86 -0
  92. package/dist/kvFacade.js +38 -0
  93. package/dist/matcher.js +304 -0
  94. package/dist/memoryColumnDefaults.js +11 -0
  95. package/dist/memoryQueryJoins.js +114 -0
  96. package/dist/memorySetOperations.js +43 -0
  97. package/dist/metrics.js +175 -0
  98. package/dist/observability.js +123 -0
  99. package/dist/observedGraph.js +52 -0
  100. package/dist/originGuard.js +116 -0
  101. package/dist/outbox.js +352 -0
  102. package/dist/pluginRefRules.js +65 -0
  103. package/dist/policyGuardResolver.js +55 -0
  104. package/dist/preview.js +62 -0
  105. package/dist/projectedRows.js +12 -0
  106. package/dist/provenance.js +63 -0
  107. package/dist/rangeIndex.js +89 -0
  108. package/dist/rawReadScope.js +17 -0
  109. package/dist/reaction.js +85 -0
  110. package/dist/reactiveConfig.js +11 -0
  111. package/dist/readClassification.js +57 -0
  112. package/dist/readRecorder.js +67 -0
  113. package/dist/readScopePredicate.js +12 -0
  114. package/dist/rebac.js +104 -0
  115. package/dist/rebacInterceptor.js +81 -0
  116. package/dist/registry.js +29 -0
  117. package/dist/relevantFields.js +65 -0
  118. package/dist/replicaHealth.js +42 -0
  119. package/dist/replicaLocality.js +44 -0
  120. package/dist/replicaObservations.js +121 -0
  121. package/dist/replicaSelection.js +32 -0
  122. package/dist/replicatedDataStore.js +257 -0
  123. package/dist/requestLoader.js +7 -0
  124. package/dist/resumeCensus.js +28 -0
  125. package/dist/revalidation.js +22 -0
  126. package/dist/rowFilter.js +82 -0
  127. package/dist/rowStateBytes.js +45 -0
  128. package/dist/rpcBodyCap.js +63 -0
  129. package/dist/rpcConnections.js +83 -0
  130. package/dist/rpcDefectDiagnostics.js +15 -0
  131. package/dist/rpcServer.js +1065 -0
  132. package/dist/rpcWorkloadAdmission.js +50 -0
  133. package/dist/runAsSystem.js +23 -0
  134. package/dist/rywPositionStore.js +119 -0
  135. package/dist/schedule.js +23 -0
  136. package/dist/scheduleBackfillRequests.js +151 -0
  137. package/dist/scheduleClaimsTable.js +11 -0
  138. package/dist/scheduleRunsTable.js +37 -0
  139. package/dist/scheduler.js +840 -0
  140. package/dist/schemaRegistry.js +65 -0
  141. package/dist/secretsResolver.js +35 -0
  142. package/dist/securityHeaders.js +33 -0
  143. package/dist/servePipeline.js +374 -0
  144. package/dist/serverOnlyAudit.js +66 -0
  145. package/dist/serverTracer.js +15 -0
  146. package/dist/serviceCredentials.js +40 -0
  147. package/dist/setReadScopes.js +28 -0
  148. package/dist/singleNodeOrchestrator.js +91 -0
  149. package/dist/snapshotValueCodec.js +46 -0
  150. package/dist/sourceResolution.js +52 -0
  151. package/dist/storeMiddleware.js +882 -0
  152. package/dist/storeQuery.js +204 -0
  153. package/dist/storeRead.js +10 -0
  154. package/dist/subscriber.js +8 -0
  155. package/dist/subscriptionOutbox.js +139 -0
  156. package/dist/subscriptionRegistry.js +82 -0
  157. package/dist/subscriptionResume.js +132 -0
  158. package/dist/subscriptionSocketMetrics.js +65 -0
  159. package/dist/systemSubject.js +5 -0
  160. package/dist/tenantScope.js +11 -0
  161. package/dist/tenantSelection.js +27 -0
  162. package/dist/timeline.js +105 -0
  163. package/dist/traceContext.js +5 -0
  164. package/dist/traceSink.js +63 -0
  165. package/dist/traceSpansTable.js +29 -0
  166. package/dist/traceparent.js +23 -0
  167. package/dist/transactionSubject.js +12 -0
  168. package/dist/trustedProxy.js +155 -0
  169. package/dist/undo.js +129 -0
  170. package/dist/undoBuiltins.js +69 -0
  171. package/dist/undoCapture.js +74 -0
  172. package/dist/undoLogTable.js +16 -0
  173. package/dist/upsertBinding.js +20 -0
  174. package/dist/wakeOrchestrator.js +34 -0
  175. package/dist/wakeRegistry.js +71 -0
  176. package/dist/wakeRouter.js +63 -0
  177. package/dist/wakeSupervisor.js +62 -0
  178. package/dist/wakeupsTable.js +12 -0
  179. package/dist/webSocketGatewayRuntime.js +154 -0
  180. package/dist/webhookVerification.js +18 -0
  181. package/dist/wireColumnStrip.js +84 -0
  182. package/dist/workflowReadBuiltins.js +87 -0
  183. package/dist/workflowRuntime.js +708 -0
  184. package/package.json +19 -7
@@ -0,0 +1,262 @@
1
+ import { Effect as e } from "effect";
2
+ import { and as t, eq as n, generateId as r, id as i, inSet as a, registerRetention as o, retentionTtlMsFromEnv as s, table as c, text as l, timestamp as u } from "@voltro/database";
3
+ import { createHash as d, randomUUID as f } from "node:crypto";
4
+ import { ApprovalExpired as p, ApprovalForbidden as m, ApprovalNotFound as h, ApprovalNotPending as g, ApprovalRejected as _, ApprovalRequired as v, ApprovalSelfApproval as y, ApprovalUnavailable as b, canonicalJson as x, checkGuardsEffect as S, isAuthenticatedGuard as C, isPolicyGuard as w, isUpgradedGuard as T, resolvePolicyAction as E, upgradedGuardShapeError as D } from "@voltro/protocol";
5
+ //#region src/approvals.ts
6
+ var O = "_voltro_approvals", k = 864e5, A = c(O, {
7
+ id: i({ prefix: "apv" }),
8
+ intentKey: l().maxLength(191),
9
+ procedure: l(),
10
+ kind: l(),
11
+ requestedBy: l().nullable(),
12
+ tenantId: l().nullable(),
13
+ payload: l(),
14
+ approverGuards: l(),
15
+ approverScopes: l(),
16
+ reason: l().nullable(),
17
+ status: l().default("pending"),
18
+ decidedBy: l().nullable(),
19
+ decidedAt: u().nullable(),
20
+ note: l().nullable(),
21
+ traceId: l().nullable(),
22
+ requestedAt: u().default("now"),
23
+ expiresAt: u(),
24
+ consumedAt: u().nullable()
25
+ }).index(["status", "expiresAt"]).index(["requestedBy"]), j = () => {
26
+ o({
27
+ source: "framework",
28
+ table: O,
29
+ timeColumn: "requestedAt",
30
+ ttlMs: s(process.env.VOLTRO_APPROVALS_TTL_HOURS, 720)
31
+ });
32
+ }, M = (e, t) => e.map((e) => {
33
+ let n = D(e);
34
+ if (n) throw n;
35
+ return C(e) ? { kind: "authenticated" } : T(e) ? { kind: "upgraded" } : w(e) ? {
36
+ kind: "policy",
37
+ action: E(e, t),
38
+ resourceType: e.resourceType,
39
+ resource: e.resource(t) ?? null
40
+ } : {
41
+ kind: "scope",
42
+ scope: typeof e.scope == "string" ? [e.scope] : [...e.scope],
43
+ mode: e.mode ?? "all",
44
+ resource: e.resource?.(t) ?? null
45
+ };
46
+ }), N = (e) => e.map((e) => e.kind === "authenticated" ? { authenticated: !0 } : e.kind === "upgraded" ? { upgraded: !0 } : e.kind === "policy" ? {
47
+ action: e.action,
48
+ resourceType: e.resourceType,
49
+ resource: () => e.resource ?? void 0
50
+ } : {
51
+ scope: e.scope,
52
+ mode: e.mode,
53
+ ...e.resource === null ? {} : { resource: () => e.resource ?? void 0 }
54
+ }), P = (e) => e.flatMap((e) => e.kind === "policy" ? [`${e.resourceType}:${e.action}`] : e.kind === "authenticated" ? ["authenticated"] : e.kind === "upgraded" ? ["upgraded"] : [...e.scope]), F = (e) => {
55
+ let t = [
56
+ e.procedure,
57
+ e.requestedBy ?? "",
58
+ e.tenantId ?? "",
59
+ e.payload,
60
+ e.nonce ?? ""
61
+ ].map((e) => `${e.length}:${e}`).join("");
62
+ return d("sha256").update(t, "utf8").digest("hex");
63
+ }, I = (e) => e instanceof Date ? e.toISOString() : typeof e == "string" ? e : (/* @__PURE__ */ new Date(0)).toISOString(), L = (e) => e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : 0, R = (e) => {
64
+ try {
65
+ let t = JSON.parse(String(e ?? "[]"));
66
+ return Array.isArray(t) ? t : [];
67
+ } catch {
68
+ return [];
69
+ }
70
+ }, z = async (e, t) => (await e.query({
71
+ table: "_voltro_approvals",
72
+ predicate: n("intentKey", t),
73
+ order: []
74
+ }))[0] ?? null, B = async (e, t) => (await e.query({
75
+ table: "_voltro_approvals",
76
+ predicate: n("id", t),
77
+ order: []
78
+ }))[0] ?? null, V = async (e, t, n) => {
79
+ let r = String(t.intentKey ?? ""), i = r.includes("#") ? r : `${r}#${String(t.id)}`;
80
+ await e.update(O, String(t.id), {
81
+ ...n,
82
+ intentKey: i
83
+ });
84
+ }, H = (e) => {
85
+ let t = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)$/.exec(e.trim());
86
+ if (t === null) return null;
87
+ let n = Number(t[1]);
88
+ if (!(Number.isFinite(n) && n > 0)) return null;
89
+ let r = t[2];
90
+ return n * (r === "ms" ? 1 : r === "s" ? 1e3 : r === "m" ? 6e4 : r === "h" ? 36e5 : 864e5);
91
+ }, U = (e) => {
92
+ let i = e.store, a = e.now ?? Date.now, o = e.defaultExpiryMs ?? 864e5;
93
+ j();
94
+ let s = (e) => {
95
+ if (e.expiresIn === void 0) return o;
96
+ let t = H(e.expiresIn);
97
+ if (t === null) throw Error(`requiresApproval.expiresIn must be an interval like '4h' / '30m' (got ${JSON.stringify(e.expiresIn)})`);
98
+ return t;
99
+ }, c = async (e, t, n) => {
100
+ let o = M(e.policy.approvers, e.input), c = r({
101
+ kind: "typeid",
102
+ prefix: "apv"
103
+ }, O), l = a();
104
+ await i.insertIgnore(O, {
105
+ id: c,
106
+ intentKey: t,
107
+ procedure: e.procedure,
108
+ kind: e.kind,
109
+ requestedBy: e.subject?.id ?? null,
110
+ tenantId: e.subject?.tenantId ?? null,
111
+ payload: n,
112
+ approverGuards: JSON.stringify(o),
113
+ approverScopes: JSON.stringify(P(o)),
114
+ reason: e.policy.reason ?? null,
115
+ status: "pending",
116
+ decidedBy: null,
117
+ decidedAt: null,
118
+ note: null,
119
+ traceId: e.traceId ?? null,
120
+ requestedAt: new Date(l),
121
+ expiresAt: new Date(l + s(e.policy)),
122
+ consumedAt: null
123
+ }, { conflictColumns: ["intentKey"] });
124
+ let u = await z(i, t);
125
+ if (u === null) throw new b({
126
+ procedure: e.procedure,
127
+ message: "the approval intent was written and could not be read back"
128
+ });
129
+ return u;
130
+ }, l = (e, t) => new v({
131
+ approvalId: String(e.id),
132
+ procedure: String(e.procedure),
133
+ expiresAt: I(e.expiresAt),
134
+ requiredScopes: P(R(e.approverGuards)),
135
+ reason: e.reason ?? null,
136
+ created: t
137
+ });
138
+ return { admit: async (e) => {
139
+ let r = x(e.input), o = F({
140
+ procedure: e.procedure,
141
+ requestedBy: e.subject?.id ?? null,
142
+ tenantId: e.subject?.tenantId ?? null,
143
+ payload: r,
144
+ nonce: e.nonce
145
+ }), s = await z(i, o);
146
+ if (s === null) throw l(await c(e, o, r), !0);
147
+ let u = String(s.status ?? "pending"), d = L(s.expiresAt);
148
+ if ((u === "pending" || u === "approved") && a() > d) throw await V(i, s, { status: "expired" }), new p({
149
+ approvalId: String(s.id),
150
+ expiredAt: I(s.expiresAt)
151
+ });
152
+ if (u === "pending") throw l(s, !1);
153
+ if (u === "rejected") throw await V(i, s, {}), new _({
154
+ approvalId: String(s.id),
155
+ decidedBy: s.decidedBy ?? null,
156
+ note: s.note ?? null
157
+ });
158
+ if (u === "approved") {
159
+ if (await i.updateMany("_voltro_approvals", {
160
+ status: "consumed",
161
+ consumedAt: new Date(a()),
162
+ intentKey: `${o}#${String(s.id)}`
163
+ }, { where: t(n("id", String(s.id)), n("status", "approved")) }) === 1) return;
164
+ throw s = await z(i, o), s === null ? l(await c(e, o, r), !0) : l(s, !1);
165
+ }
166
+ throw await V(i, s, {}), l(await c(e, o, r), !0);
167
+ } };
168
+ }, W, G = (e) => {
169
+ W = e;
170
+ }, K = () => W, q = () => {
171
+ W = void 0;
172
+ }, J = async (e) => {
173
+ if (e.policy === void 0) return;
174
+ let t = K();
175
+ if (t === void 0) throw new b({
176
+ procedure: e.procedure,
177
+ message: `${e.procedure} declares \`requiresApproval\` and no approvals gate is installed on this boot path, so the framework cannot record the intent or verify a decision. Refusing the call rather than running it unapproved.`
178
+ });
179
+ await t.admit({
180
+ procedure: e.procedure,
181
+ kind: e.kind,
182
+ policy: e.policy,
183
+ subject: e.subject ?? null,
184
+ input: e.input,
185
+ ...e.traceId === void 0 ? {} : { traceId: e.traceId }
186
+ });
187
+ }, Y = async (r, i) => {
188
+ let a = i.now ?? Date.now, o = await B(r, i.approvalId);
189
+ if (o === null) throw new h({ approvalId: i.approvalId });
190
+ let s = String(o.status ?? "pending");
191
+ if (s !== "pending") throw new g({
192
+ approvalId: i.approvalId,
193
+ status: s
194
+ });
195
+ if (a() > L(o.expiresAt)) throw await V(r, o, { status: "expired" }), new p({
196
+ approvalId: i.approvalId,
197
+ expiredAt: I(o.expiresAt)
198
+ });
199
+ let c = i.approver?.id ?? null;
200
+ if (c === null) throw new m({
201
+ approvalId: i.approvalId,
202
+ required: null,
203
+ message: "an approval decision needs an identified subject — an anonymous caller cannot be the second human"
204
+ });
205
+ if (c === (o.requestedBy ?? null)) throw new y({
206
+ approvalId: i.approvalId,
207
+ subjectId: c
208
+ });
209
+ let l = N(R(o.approverGuards)), u = await e.runPromise(S(i.approver, l, JSON.parse(String(o.payload ?? "null"))));
210
+ if (u) throw new m({
211
+ approvalId: i.approvalId,
212
+ required: "_tag" in u && u._tag === "ScopeError" ? u.required : null,
213
+ message: u.message
214
+ });
215
+ let d = i.decision === "approve" ? "approved" : "rejected";
216
+ if (await r.updateMany("_voltro_approvals", {
217
+ status: d,
218
+ decidedBy: c,
219
+ decidedAt: new Date(a()),
220
+ note: i.note ?? null
221
+ }, { where: t(n("id", i.approvalId), n("status", "pending")) }) !== 1) {
222
+ let e = await B(r, i.approvalId);
223
+ throw new g({
224
+ approvalId: i.approvalId,
225
+ status: String(e?.status ?? "unknown")
226
+ });
227
+ }
228
+ return {
229
+ approvalId: i.approvalId,
230
+ status: d
231
+ };
232
+ }, X = async (t, n, r) => {
233
+ let i = n?.id ?? null;
234
+ if (i === null) return [];
235
+ let o = r?.now ?? Date.now, s = await t.query({
236
+ table: O,
237
+ predicate: a("status", ["pending", "approved"]),
238
+ order: []
239
+ }), c = [];
240
+ for (let t of s) {
241
+ if (o() > L(t.expiresAt)) continue;
242
+ let r = R(t.approverGuards), a = (t.requestedBy ?? null) === i, s = a ? "requested" : null;
243
+ a || await e.runPromise(S(n, N(r), JSON.parse(String(t.payload ?? "null")))) === null && String(t.status) === "pending" && (s = "to-decide"), s !== null && c.push({
244
+ id: String(t.id),
245
+ procedure: String(t.procedure),
246
+ kind: String(t.kind) === "action" ? "action" : "mutation",
247
+ requestedBy: t.requestedBy ?? null,
248
+ status: String(t.status ?? "pending"),
249
+ reason: t.reason ?? null,
250
+ requiredScopes: P(r),
251
+ requestedAt: I(t.requestedAt),
252
+ expiresAt: I(t.expiresAt),
253
+ decidedBy: t.decidedBy ?? null,
254
+ decidedAt: t.decidedAt == null ? null : I(t.decidedAt),
255
+ note: t.note ?? null,
256
+ relation: s
257
+ });
258
+ }
259
+ return c.sort((e, t) => t.requestedAt.localeCompare(e.requestedAt)).slice(0, r?.limit ?? 50);
260
+ }, Z = () => f();
261
+ //#endregion
262
+ export { O as APPROVALS_TABLE, k as DEFAULT_APPROVAL_EXPIRY_MS, A as _voltroApprovalsTable, F as approvalIntentKey, Z as approvalNonce, x as canonicalJson, q as clearApprovalGate, Y as decideApproval, J as enforceApproval, K as getApprovalGate, G as installApprovalGate, U as makeApprovalGate, H as parseApprovalInterval, X as pendingApprovalsFor, N as rehydrateGuards, P as scopeStringsOf, M as serializeGuards };
@@ -0,0 +1,48 @@
1
+ import { applyConnectionCredential as e, getCredentialExpiry as t, recordCredentialExpiry as n } from "./connectionCredentials.js";
2
+ import { Context as r, Effect as i, Layer as a, Option as o, Schema as s } from "effect";
3
+ import { AuthMiddleware as c, ConnectionInfo as l, ConnectionInfoMiddleware as u, RequestedTenantRefused as d, SubjectService as f, Unauthenticated as p } from "@voltro/protocol";
4
+ import { Rpc as m } from "effect/rpc";
5
+ import { sessionExpiryFromHeaders as h } from "@voltro/protocol/session";
6
+ //#region src/authMiddlewareLayer.ts
7
+ var g = class extends r.Service()("@voltro/runtime/VerifiedCredentialLifetime") {}, _ = (e) => {
8
+ for (let t of e.requests.values()) if (m.isRpc(t) && [...t.middlewares].some((e) => e.key === c.key || e.key === u.key)) throw Error("withRpcAuthMiddleware requires a group without AuthMiddleware or ConnectionInfoMiddleware; apply the shared pair once");
9
+ return e.middleware(u).middleware(c);
10
+ }, v = (r) => {
11
+ let m = /* @__PURE__ */ new WeakSet(), _ = c.of((t, a) => {
12
+ m.add(a);
13
+ let { headers: o, client: c, rpc: l } = a;
14
+ return i.tryPromise({
15
+ try: async () => {
16
+ let t = await r.resolveSubject({
17
+ headers: e(c.id, o),
18
+ clientId: c.id
19
+ });
20
+ return n(c.id, t.credentialExpiresAt), t;
21
+ },
22
+ catch: (e) => e
23
+ }).pipe(i.catch((e) => s.is(s.Union([p, d]))(e) ? i.fail(e) : i.die(e)), i.flatMap((e) => {
24
+ let t = e.subject;
25
+ if (t.type !== "service") return i.succeed(e);
26
+ let n = r.serviceCallersFor?.(l._tag);
27
+ return n !== void 0 && n.includes(t.service) ? i.succeed(e) : i.fail(new p({ reason: `service '${t.service}' is not a caller of '${l._tag}' — a service credential reaches only the procedures that list it in \`service: { callers }\`` }));
28
+ }), i.flatMap((e) => i.provideService(t, f, e.subject).pipe(i.provideService(g, {
29
+ request: a,
30
+ credentialExpiresAt: e.credentialExpiresAt
31
+ }))));
32
+ });
33
+ return a.mergeAll(a.succeed(c, _), a.succeed(u, u.of((n, r) => i.gen(function* () {
34
+ let { headers: n, client: a, rpc: s } = r, l = yield* i.serviceOption(g), u = o.filter(l, (e) => e.request === r);
35
+ if (o.isNone(u) && m.has(r) && [...s.middlewares].some((e) => e.key === c.key)) throw Error("RPC authentication must run before ConnectionInfoMiddleware; compose the group with withRpcAuthMiddleware");
36
+ let d = o.isSome(u) ? u.value.credentialExpiresAt : t(a.id) ?? h(e(a.id, n)), f = e(a.id, n).cookie, p = n["idempotency-key"], _ = n["voltro-resume-from"], v = n["voltro-reconcile"], y = _ !== void 0 && /^\d+$/.test(_) ? Number(_) : void 0;
37
+ return {
38
+ clientId: a.id,
39
+ ...f === void 0 ? {} : { cookieHeader: f },
40
+ ...p ? { idempotencyKey: p } : {},
41
+ ...d === void 0 ? {} : { credentialExpiresAt: d },
42
+ ...y !== void 0 && Number.isSafeInteger(y) ? { resumeFrom: y } : {},
43
+ ...v !== void 0 && /^[a-zA-Z0-9_-]{1,128}$/.test(v) ? { reconcile: v } : {}
44
+ };
45
+ }).pipe(i.flatMap((e) => i.provideService(n, l, e))))));
46
+ }, y = (e) => a.succeed(u, u.of((t, { headers: n, client: r }) => i.sync(() => (e(n, r.id), { clientId: r.id })).pipe(i.flatMap((e) => i.provideService(t, l, e)))));
47
+ //#endregion
48
+ export { v as makeAuthMiddlewareLayers, y as observeConnectionHeaders, _ as withRpcAuthMiddleware };
@@ -0,0 +1,92 @@
1
+ import { recordEventDelivered as e, recordEventDropped as t } from "./metrics.js";
2
+ import { registerConnectionStream as n } from "./connectionCredentials.js";
3
+ import { boundByCredential as r } from "./credentialBound.js";
4
+ import { Effect as i, Queue as a, Stream as o } from "effect";
5
+ import { ConnectionInfo as s, ScopeError as c, SubjectService as l, checkGuardsEffect as u, isOpenAccess as d, recheckGuardsEffect as f } from "@voltro/protocol";
6
+ //#region src/bindEvent.ts
7
+ var p = 64, m = 1, h = (e) => e.kind === "gap" ? {
8
+ _tag: "gap",
9
+ missed: e.missed,
10
+ reason: e.reason
11
+ } : {
12
+ _tag: "event",
13
+ origin: e.envelope.origin,
14
+ n: e.envelope.n,
15
+ emittedAt: e.envelope.emittedAt,
16
+ payload: e.envelope.payload
17
+ }, g = (e) => {
18
+ let t = new Map(e ?? []);
19
+ return (e, n = 0) => {
20
+ let r = t.get(e.origin) ?? 0;
21
+ return t.set(e.origin, Math.max(r, e.n)), r === 0 && n === 0 ? 0 : Math.max(0, e.n - r - 1);
22
+ };
23
+ }, _ = (p) => o.unwrap(i.gen(function* () {
24
+ let m = yield* l, _ = yield* s, v = m.tenantId ?? null, y = p.guards === void 0 ? void 0 : p.guards.filter((e) => !d(e));
25
+ if (y !== void 0 && y.length > 0 || m.type === "service") {
26
+ let e = yield* u(m, y ?? [], p.key);
27
+ if (e) return yield* i.fail(e);
28
+ }
29
+ let b = p.clientPolicy, x = () => new c({
30
+ required: p.requiredClientPolicy ?? "event:client-policy",
31
+ message: "event client access denied"
32
+ });
33
+ if (p.requiredClientPolicy !== void 0 && (!b || b.id !== p.requiredClientPolicy) || b !== void 0 && p.requiredClientPolicy === void 0) return yield* i.fail(x());
34
+ let S = () => ({
35
+ subject: structuredClone(m),
36
+ event: p.event,
37
+ key: structuredClone(p.key)
38
+ }), C = () => b === void 0 ? i.succeed(!0) : i.suspend(() => b.admit(S())).pipe(i.catch(() => i.succeed(!1)), i.catchDefect(() => i.succeed(!1)), i.map((e) => e === !0)), w = b?.monitor === void 0 ? void 0 : yield* i.suspend(() => b.monitor(S())).pipe(i.catch(() => i.fail(x())), i.catchDefect(() => i.fail(x())));
39
+ if (!(yield* C())) return yield* i.fail(x());
40
+ let T = p.bus.deliveryFor(p.event) === "latest", E = yield* a.sliding(p.queueSize ?? (T ? 1 : 64)), D = /* @__PURE__ */ new Map(), O = p.bus.subscribe({
41
+ tenantId: v,
42
+ event: p.event,
43
+ key: p.key,
44
+ options: p.options,
45
+ onAttach: (e) => {
46
+ D = e;
47
+ },
48
+ listener: (e) => {
49
+ i.runSync(a.offer(E, e));
50
+ }
51
+ });
52
+ if (yield* i.addFinalizer(() => i.sync(() => {
53
+ O();
54
+ })), _.clientId !== void 0) {
55
+ let e = n(_.clientId, () => {
56
+ i.runPromise(a.shutdown(E));
57
+ });
58
+ yield* i.addFinalizer(() => i.sync(() => {
59
+ e();
60
+ }));
61
+ }
62
+ let k = T ? () => 0 : g(D), A = () => f(m, y, p.key), j = o.fromQueue(E).pipe(o.flatMap((e) => o.unwrap(A().pipe(i.map((t) => t ? o.fail(t) : M(e))))), o.ensuring(a.shutdown(E)));
63
+ function M(e) {
64
+ if (b === void 0) return N(e);
65
+ let t = e.kind === "gap" ? C().pipe(i.map((e) => e ? "allow" : "deny")) : i.suspend(() => b.deliver({
66
+ ...S(),
67
+ payload: structuredClone(e.envelope.payload),
68
+ emittedAt: e.envelope.emittedAt
69
+ })).pipe(i.catch(() => i.succeed("deny")), i.catchDefect(() => i.succeed("deny")));
70
+ return o.unwrap(t.pipe(i.map((t) => t === "allow" ? N(e) : t === "drop" && e.kind !== "gap" ? (k(e.envelope, e.prior), o.empty) : o.fail(x()))));
71
+ }
72
+ function N(n) {
73
+ return o.suspend(() => {
74
+ if (n.kind === "gap") return o.make(h(n));
75
+ let r = k(n.envelope, n.prior);
76
+ e(p.event);
77
+ let i = h(n);
78
+ if (r === 0) return o.make(i);
79
+ t(p.event, "buffer", r);
80
+ let a = {
81
+ _tag: "gap",
82
+ missed: r,
83
+ reason: "buffer"
84
+ };
85
+ return o.fromIterable([a, i]);
86
+ });
87
+ }
88
+ let P = { _tag: "attached" }, F = o.concat(o.fromIterable([P]), j), I = w === void 0 ? F : F.pipe(o.interruptWhen(w.pipe(i.catch(() => i.void), i.catchDefect(() => i.void), i.flatMap(() => i.fail(x())))));
89
+ return r(I, _.credentialExpiresAt, o.fromIterable([P]));
90
+ }));
91
+ //#endregion
92
+ export { p as EVENT_CLIENT_QUEUE_SIZE, m as EVENT_LATEST_QUEUE_SIZE, _ as bindEvent, g as makeBufferGapDetector };
@@ -0,0 +1,143 @@
1
+ import { answerProbe as e, probeFor as t, resolveHealth as n } from "./healthProbes.js";
2
+ import { trackEngineClient as r } from "./engineClients.js";
3
+ import { createServer as i } from "node:http";
4
+ //#region src/bootListener.ts
5
+ var a = "no-store", o = "1", s = /* @__PURE__ */ new Set(["ready", "draining"]), c = async (c) => {
6
+ let l = c.now ?? Date.now, u = {
7
+ phase: "booting",
8
+ startedAt: l()
9
+ }, d, f, p = /* @__PURE__ */ new Map(), m = /* @__PURE__ */ new Map(), h, g = () => u.phase === "ready", _ = () => s.has(u.phase), v = n(c.health, () => g()), y = c.health?.readiness === void 0 ? async () => g() && (f === void 0 || await f()) : v.readiness, b = () => {
10
+ if ((p.size > 0 || h !== void 0) && !g()) return !1;
11
+ for (let e of p.values()) try {
12
+ if (e() !== !0) return !1;
13
+ } catch {
14
+ return !1;
15
+ }
16
+ return !0;
17
+ }, x = () => {
18
+ for (let e of m.values()) try {
19
+ if (e() !== !0) return !1;
20
+ } catch {
21
+ return !1;
22
+ }
23
+ return !0;
24
+ }, S = {
25
+ ...v,
26
+ readiness: async () => b() && await y() && b(),
27
+ liveness: async () => x() && await v.liveness() && x()
28
+ }, C = () => ({
29
+ phase: u.phase,
30
+ ready: g(),
31
+ elapsedMs: l() - u.startedAt,
32
+ ...u.tablesTotal === void 0 ? {} : { tablesTotal: u.tablesTotal },
33
+ ...u.error === void 0 ? {} : { error: u.error }
34
+ }), w = i((n, r) => {
35
+ let i = n.url ?? "/", s = i.indexOf("?"), c = s === -1 ? i : i.slice(0, s);
36
+ if (c === "/_voltro/health" && (h !== void 0 || p.size > 0)) {
37
+ if (n.method !== "GET" && n.method !== "HEAD") {
38
+ r.writeHead(405, {
39
+ allow: "GET, HEAD",
40
+ "cache-control": a
41
+ }), r.end();
42
+ return;
43
+ }
44
+ e(S, "readiness", C).then((e) => {
45
+ let t = e.status === 200 && g() && b();
46
+ r.writeHead(t ? 200 : 503, {
47
+ "content-type": "application/json",
48
+ "cache-control": a
49
+ }), r.end(n.method === "HEAD" ? void 0 : JSON.stringify({
50
+ ...h,
51
+ phase: u.phase,
52
+ ready: t
53
+ }));
54
+ });
55
+ return;
56
+ }
57
+ let l = t(S, c);
58
+ l === void 0 ? d !== void 0 && _() ? d.request(n, r) : (r.writeHead(503, {
59
+ "content-type": "text/plain",
60
+ "cache-control": a,
61
+ "retry-after": o
62
+ }), r.end("starting")) : e(S, l, C).then((e) => {
63
+ r.writeHead(e.status, {
64
+ "content-type": e.contentType,
65
+ "cache-control": a
66
+ }), r.end(e.body);
67
+ });
68
+ });
69
+ w.on("upgrade", (e, t, n) => {
70
+ d !== void 0 && _() ? (r(e, t), d.upgrade(e, t, n)) : t.end(`HTTP/1.1 503 Service Unavailable\r
71
+ Retry-After: ${o}\r\nCache-Control: no-store\r
72
+ Connection: close\r
73
+ Content-Length: 0\r
74
+ \r
75
+ `);
76
+ }), await new Promise((e, t) => {
77
+ let n = (e) => {
78
+ t(e);
79
+ };
80
+ w.once("error", n), w.listen(c.host === void 0 ? { port: c.port } : {
81
+ port: c.port,
82
+ host: c.host
83
+ }, () => {
84
+ w.removeListener("error", n), e();
85
+ });
86
+ });
87
+ let T = w.address()?.port ?? c.port;
88
+ return c.log?.info("listening early — probes answer on the app port from here on", { port: T }), {
89
+ server: w,
90
+ port: T,
91
+ get state() {
92
+ return u;
93
+ },
94
+ set: (e) => {
95
+ u = {
96
+ ...u,
97
+ ...e
98
+ };
99
+ },
100
+ isReady: g,
101
+ delegate: (e) => {
102
+ d = e;
103
+ },
104
+ setDependencyPing: (e) => {
105
+ f = e;
106
+ },
107
+ setRoleHealth: (e) => {
108
+ let t = (e) => {
109
+ if (!Array.isArray(e) || e.length === 0 || e.length > 64 || e.some((e) => typeof e != "string" || !/^[a-z][a-z0-9._-]{0,63}$/.test(e)) || new Set(e).size !== e.length) throw Error("Invalid role health identity");
110
+ return Object.freeze([...e]);
111
+ };
112
+ h = Object.freeze({
113
+ roles: t(e.roles),
114
+ workloads: t(e.workloads)
115
+ });
116
+ },
117
+ addReadinessGate: (e, t) => {
118
+ if (!/^[a-z][a-z0-9._-]{0,63}$/.test(e) || typeof t != "function") throw Error("Invalid readiness gate");
119
+ if (p.has(e)) throw Error("Readiness gate is already registered");
120
+ p.set(e, t);
121
+ let n = !1;
122
+ return () => {
123
+ n || (n = !0, p.get(e) === t && p.delete(e));
124
+ };
125
+ },
126
+ addLivenessGate: (e, t) => {
127
+ if (!/^[a-z][a-z0-9._-]{0,63}$/.test(e) || typeof t != "function") throw Error("Invalid liveness gate");
128
+ if (m.has(e)) throw Error("Liveness gate is already registered");
129
+ m.set(e, t);
130
+ let n = !1;
131
+ return () => {
132
+ n || (n = !0, m.get(e) === t && m.delete(e));
133
+ };
134
+ },
135
+ close: () => new Promise((e) => {
136
+ w.listening ? (w.closeAllConnections(), w.close(() => {
137
+ e();
138
+ })) : e();
139
+ })
140
+ };
141
+ };
142
+ //#endregion
143
+ export { c as bindBootListener };
@@ -0,0 +1,7 @@
1
+ //#region src/bootRefusal.ts
2
+ var e = "VoltroBootRefusal", t = (t) => {
3
+ let n = Error(t);
4
+ return n.name = e, n;
5
+ }, n = (e) => typeof e == "object" && !!e && e.name === "VoltroBootRefusal";
6
+ //#endregion
7
+ export { e as BOOT_REFUSAL_NAME, t as bootRefusal, n as isBootRefusal };
@@ -0,0 +1,71 @@
1
+ import { getFieldCipher as e } from "./fieldCipher.js";
2
+ import { upsertDestinationId as t } from "./upsertBinding.js";
3
+ import { decryptFieldsOnRead as n, deserializeArraysOnRead as r, encryptFieldsForWrite as i, encryptFieldsForWriteBatch as a, encryptedColumnsOf as o, getTable as s, jsonPatchAssignments as c, refuseIdChangeOfSealedRow as l, serializeArraysForWrite as u, upsertUpdateValues as d } from "@voltro/database";
4
+ import { createLogger as f } from "@voltro/logger";
5
+ //#region src/bootStoreCodec.ts
6
+ var p = f({ scope: "store.fieldEncryption" }), m = (f, h) => {
7
+ let g = h, _ = process.env.VOLTRO_FIELD_DECRYPT_ON_ERROR === "null" ? "null" : "throw", v = /* @__PURE__ */ new Set(), y = (e) => {
8
+ let t = `${e.table}.${e.column}`;
9
+ v.has(t) || (v.add(t), p.warn("encrypted column degraded to null — could not decrypt with the active key", {
10
+ table: e.table,
11
+ column: e.column,
12
+ reason: e.reason,
13
+ hint: "VOLTRO_FIELD_DECRYPT_ON_ERROR=null is set — this row was likely encrypted under a different key"
14
+ }));
15
+ }, b = (t, n, r) => {
16
+ let a = s(t), o = g ? u(n, a, g) : n;
17
+ return i(o, a, e(), r);
18
+ }, x = (t, i, a) => {
19
+ let o = s(t), c = n(i, o, e(), {
20
+ onError: _,
21
+ ..._ === "null" ? { warn: y } : {},
22
+ ...a === void 0 ? {} : { rowId: a }
23
+ });
24
+ return g ? r(c, o, g) : c;
25
+ }, S = (e, t, n) => t === null ? null : x(e, [t], n)[0];
26
+ return {
27
+ delete: (e, t) => f.delete(e, t),
28
+ deleteMany: (e, t) => f.deleteMany(e, t),
29
+ onChange: (e) => f.onChange(e),
30
+ injectExternalChange: (e) => {
31
+ f.injectExternalChange(e);
32
+ },
33
+ ...f.changeScope === void 0 ? {} : { changeScope: f.changeScope },
34
+ ...f.withNamespace === void 0 ? {} : { withNamespace: (e) => m(f.withNamespace(e), h) },
35
+ ...f.raw === void 0 ? {} : { raw: ((...e) => f.raw(...e)) },
36
+ ...f.close === void 0 ? {} : { close: (e) => f.close(e) },
37
+ ...f.ping === void 0 ? {} : { ping: () => f.ping() },
38
+ query: async (e) => x(e.table, await f.query(e)),
39
+ ...f.integrityQuery === void 0 ? {} : { integrityQuery: async (e) => x(e.table, await f.integrityQuery(e)) },
40
+ insert: async (e, t) => S(e, await f.insert(e, b(e, t))),
41
+ insertMany: async (t, n, r) => {
42
+ let i = s(t), o = g ? n.map((e) => u(e, i, g)) : n, c = a(o, i, e());
43
+ return x(t, await f.insertMany(t, c, r));
44
+ },
45
+ update: async (e, t, n) => S(e, await f.update(e, t, (l(n, s(e), t), b(e, n, t))), t),
46
+ updateMany: async (e, t, n) => {
47
+ let r = s(e), i = (r === void 0 ? [] : o(r)).filter((e) => t[e] !== void 0 && t[e] !== null);
48
+ if (i.length > 0) throw Error(`updateMany on the boot store cannot write the .encrypted() column(s) ${i.join(", ")} on "${e}": an encrypted value is bound to its row and a set-based write has no row. Write them through the request-scoped store, or key the write with update(table, id, patch).`);
49
+ return f.updateMany(e, b(e, t), n);
50
+ },
51
+ upsert: async (e, n, r) => {
52
+ let i = d(r), a = await t(f, e, n, r.conflictColumns);
53
+ return S(e, await f.upsert(e, b(e, n, a), {
54
+ ...r,
55
+ ...r.updateValues === void 0 ? {} : { updateValues: b(e, i, a) }
56
+ }), a);
57
+ },
58
+ insertIgnore: async (e, t, n) => S(e, await f.insertIgnore(e, b(e, t), n)),
59
+ insertIgnoreWithOutcome: async (e, t, n) => {
60
+ let r = await f.insertIgnoreWithOutcome(e, b(e, t), n);
61
+ return {
62
+ row: S(e, r.row),
63
+ outcome: r.outcome
64
+ };
65
+ },
66
+ patchJson: async (e, t, n, r, i) => S(e, await f.patchJson(e, t, n, r, b(e, c(t, n, i), t)), t),
67
+ transactional: (e) => f.transactional((t) => e(m(t, h)))
68
+ };
69
+ };
70
+ //#endregion
71
+ export { m as wrapStoreWithBootCodec };