@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,410 @@
1
+ import { decryptField as e, encryptField as t, getFieldCipher as n } from "./fieldCipher.js";
2
+ import { and as r, eq as i, fieldBinding as a, generateId as o, id as s, json as c, lte as l, table as u, text as d, timestamp as f } from "@voltro/database";
3
+ import { ConnectionHandshakeFailed as p } from "@voltro/protocol";
4
+ import { createLogger as m } from "@voltro/logger";
5
+ //#region src/connectionVault.ts
6
+ var h = m({ scope: "voltro:connections" }), g = "_voltro_connections", _ = {
7
+ kind: "typeid",
8
+ prefix: "conn"
9
+ }, v = u(g, {
10
+ id: s({ prefix: "conn" }),
11
+ connectionId: d(),
12
+ subjectId: d(),
13
+ tenantId: d().nullable(),
14
+ kind: d().oneOf(["oauth2", "pat"]),
15
+ status: d().oneOf([
16
+ "connected",
17
+ "expired",
18
+ "revoked",
19
+ "error"
20
+ ]),
21
+ accessToken: d(),
22
+ refreshToken: d().nullable(),
23
+ expiresAt: f().nullable(),
24
+ scopes: d().nullable(),
25
+ accountId: d().nullable(),
26
+ accountLabel: d().nullable(),
27
+ lastError: d().nullable(),
28
+ refreshLockedUntil: f(),
29
+ metadata: c().nullable(),
30
+ connectedAt: f(),
31
+ updatedAt: f()
32
+ }).unique("byConnectionSubject", ["connectionId", "subjectId"]).index(["subjectId"]), y = "_voltro_connection_grants", ee = {
33
+ kind: "typeid",
34
+ prefix: "cgrant"
35
+ }, b = u(y, {
36
+ id: s({ prefix: "cgrant" }),
37
+ state: d().unique(),
38
+ connectionId: d(),
39
+ subjectId: d(),
40
+ tenantId: d().nullable(),
41
+ codeVerifier: d().nullable(),
42
+ redirectTo: d(),
43
+ createdAt: f(),
44
+ expiresAt: f()
45
+ }).index(["expiresAt"]), x = (e) => typeof e == "function" ? e() : e, te = /^[a-z][a-z0-9-]*$/, ne = (e) => {
46
+ if (!te.test(e.id)) throw Error(`defineConnection: id "${e.id}" must be lowercase kebab-case (it becomes a URL path segment).`);
47
+ if (e.kind === "oauth2") {
48
+ for (let t of ["authorizeUrl", "tokenUrl"]) if (typeof e[t] != "string" || e[t].length === 0) throw Error(`defineConnection("${e.id}"): \`${t}\` is required for kind 'oauth2'.`);
49
+ for (let t of ["clientId", "clientSecret"]) {
50
+ let n = e[t];
51
+ if (typeof n != "function" && (typeof n != "string" || n.length === 0)) throw Error(`defineConnection("${e.id}"): \`${t}\` is required for kind 'oauth2' — a string, or a thunk (\`() => serverEnv.X\`) read when the connection is used.`);
52
+ }
53
+ }
54
+ return e;
55
+ }, S = (e) => e.label ?? e.id, C = (e) => {
56
+ if (e.length !== 0 && n() === void 0) throw Error(`connections: ${e.length} connection(s) declared (${e.map((e) => e.id).join(", ")}) but no field cipher is registered. Connection tokens are stored encrypted and there is no plaintext fallback. Set VOLTRO_FIELD_ENCRYPTION_KEY (\`voltro secret generate field-encryption\`; \`voltro dev\` mints one), or call setFieldCipher at boot.`);
57
+ }, w = (e) => {
58
+ let t = /* @__PURE__ */ new Map();
59
+ for (let n of e) {
60
+ if (t.has(n.id)) throw Error(`connections: duplicate connection id "${n.id}" — each *.connection.ts must declare a distinct id.`);
61
+ t.set(n.id, n);
62
+ }
63
+ return {
64
+ get: (e) => t.get(e),
65
+ list: () => [...t.values()]
66
+ };
67
+ }, T = (e) => {
68
+ if (e == null) return null;
69
+ let t = e instanceof Date ? e : new Date(String(e));
70
+ return Number.isNaN(t.getTime()) ? null : t;
71
+ }, E = (e) => T(e)?.toISOString() ?? null, D = (e) => String(e ?? "").match(/[^ ]+/g) ?? [], O = (e, t) => t === void 0 ? {
72
+ connectionId: e.id,
73
+ kind: e.kind,
74
+ label: S(e),
75
+ status: "disconnected",
76
+ accountId: null,
77
+ accountLabel: null,
78
+ scopes: [],
79
+ expiresAt: null,
80
+ lastError: null,
81
+ connectedAt: null
82
+ } : {
83
+ connectionId: e.id,
84
+ kind: e.kind,
85
+ label: S(e),
86
+ status: String(t.status ?? "connected"),
87
+ accountId: t.accountId ?? null,
88
+ accountLabel: t.accountLabel ?? null,
89
+ scopes: D(t.scopes),
90
+ expiresAt: E(t.expiresAt),
91
+ lastError: t.lastError ?? null,
92
+ connectedAt: E(t.connectedAt)
93
+ }, k = async (e, t, n) => (await e.query({
94
+ table: g,
95
+ predicate: r(i("connectionId", t), i("subjectId", n)),
96
+ order: [],
97
+ take: 1,
98
+ skip: void 0,
99
+ projection: void 0
100
+ }))[0], A = async (e) => {
101
+ let n = (e.now ?? (() => /* @__PURE__ */ new Date()))(), s = e.tokens.expiresAt === void 0 ? null : new Date(e.tokens.expiresAt), c = await e.store.query({
102
+ table: g,
103
+ predicate: r(i("connectionId", e.definition.id), i("subjectId", e.subjectId)),
104
+ order: [],
105
+ take: 1,
106
+ skip: void 0,
107
+ projection: ["id"]
108
+ }), l = typeof c[0]?.id == "string" ? c[0].id : o(_, g), u = {
109
+ id: l,
110
+ connectionId: e.definition.id,
111
+ subjectId: e.subjectId,
112
+ tenantId: e.tenantId,
113
+ kind: e.definition.kind,
114
+ status: "connected",
115
+ accessToken: t(e.tokens.accessToken, a(g, "accessToken", l)),
116
+ refreshToken: e.tokens.refreshToken === void 0 ? null : t(e.tokens.refreshToken, a(g, "refreshToken", l)),
117
+ expiresAt: s,
118
+ scopes: e.tokens.scopes.length > 0 ? e.tokens.scopes.join(" ") : null,
119
+ accountId: e.account?.accountId ?? null,
120
+ accountLabel: e.account?.accountLabel ?? null,
121
+ lastError: null,
122
+ refreshLockedUntil: /* @__PURE__ */ new Date(0),
123
+ metadata: e.account?.metadata ?? null,
124
+ connectedAt: n,
125
+ updatedAt: n
126
+ };
127
+ return re(e.store, u);
128
+ }, re = (e, t) => e.upsert(g, t, {
129
+ conflictColumns: ["connectionId", "subjectId"],
130
+ update: [
131
+ "kind",
132
+ "status",
133
+ "accessToken",
134
+ "refreshToken",
135
+ "expiresAt",
136
+ "scopes",
137
+ "accountId",
138
+ "accountLabel",
139
+ "lastError",
140
+ "refreshLockedUntil",
141
+ "metadata",
142
+ "updatedAt"
143
+ ]
144
+ }), j = async (e, t, n) => {
145
+ let r = await k(e, t, n);
146
+ return r !== void 0 && e.delete(g, r.id);
147
+ }, M = async (e, t, n) => {
148
+ let r = await e.query({
149
+ table: g,
150
+ predicate: i("subjectId", n),
151
+ order: [],
152
+ take: void 0,
153
+ skip: void 0,
154
+ projection: void 0
155
+ }), a = new Map(r.map((e) => [String(e.connectionId), e]));
156
+ return t.list().map((e) => O(e, a.get(e.id)));
157
+ }, N = (e, t) => globalThis.fetch(e, t), P = async (e, t, n) => {
158
+ let r = e.fetchImpl ?? N, i, a;
159
+ try {
160
+ let n = await r(e.tokenUrl, {
161
+ method: "POST",
162
+ headers: {
163
+ "content-type": "application/x-www-form-urlencoded",
164
+ accept: "application/json"
165
+ },
166
+ body: new URLSearchParams(t).toString()
167
+ });
168
+ i = n.status, a = await n.text();
169
+ } catch (t) {
170
+ throw new p({
171
+ connectionId: e.id,
172
+ reason: `token endpoint unreachable: ${String(t)}`,
173
+ transient: !0
174
+ });
175
+ }
176
+ let o;
177
+ try {
178
+ o = JSON.parse(a);
179
+ } catch {
180
+ o = {};
181
+ }
182
+ if (i < 200 || i >= 300) throw new p({
183
+ connectionId: e.id,
184
+ reason: `token endpoint ${i}: ${o.error ?? ""} ${o.error_description ?? a.slice(0, 200)}`.trim(),
185
+ transient: i >= 500
186
+ });
187
+ if (typeof o.access_token != "string" || o.access_token.length === 0) throw new p({
188
+ connectionId: e.id,
189
+ reason: "token response carried no access_token",
190
+ transient: !1
191
+ });
192
+ return {
193
+ accessToken: o.access_token,
194
+ ...typeof o.refresh_token == "string" ? { refreshToken: o.refresh_token } : {},
195
+ ...Number.isFinite(o.expires_in) ? { expiresAt: n() + o.expires_in * 1e3 } : {},
196
+ scopes: (o.scope ?? e.scopes?.join(" ") ?? "").match(/[^ ]+/g) ?? []
197
+ };
198
+ }, F = (e, t, n = Date.now) => P(e, {
199
+ grant_type: "authorization_code",
200
+ client_id: x(e.clientId),
201
+ client_secret: x(e.clientSecret),
202
+ code: t.code,
203
+ redirect_uri: t.redirectUri,
204
+ ...t.codeVerifier ? { code_verifier: t.codeVerifier } : {}
205
+ }, n), I = (e, t, n = Date.now) => P(e, {
206
+ grant_type: "refresh_token",
207
+ client_id: x(e.clientId),
208
+ client_secret: x(e.clientSecret),
209
+ refresh_token: t
210
+ }, n), L = 6e4, R = 3e4, z = class extends Error {
211
+ connectionId;
212
+ subjectId;
213
+ _tag = "ConnectionNotConnected";
214
+ constructor(e, t) {
215
+ super(`connection "${e}" is not connected for this subject`), this.connectionId = e, this.subjectId = t, this.name = "ConnectionNotConnected";
216
+ }
217
+ }, B = (e) => new Promise((t) => {
218
+ setTimeout(t, e);
219
+ }), V = /* @__PURE__ */ new Map(), H = (e, t) => `${e}\u0000${t}`, U = () => {
220
+ V.clear();
221
+ }, W = (t, n) => ({
222
+ connectionId: t.id,
223
+ kind: t.kind,
224
+ accessToken: e(String(n.accessToken), a(g, "accessToken", n.id)),
225
+ expiresAt: T(n.expiresAt),
226
+ scopes: D(n.scopes),
227
+ accountId: n.accountId ?? null,
228
+ accountLabel: n.accountLabel ?? null
229
+ }), G = (e, t, n) => {
230
+ let r = T(e.expiresAt);
231
+ return r !== null && r.getTime() - n < t;
232
+ }, K = async (e) => {
233
+ let t = e.now ?? Date.now, { store: n, definition: r, subjectId: i } = e, a = await k(n, r.id, i);
234
+ if (a === void 0) throw new z(r.id, i);
235
+ if (r.kind === "pat" || !G(a, r.refreshSkewMs ?? 6e4, t())) return W(r, a);
236
+ let o = H(r.id, i), s = V.get(o);
237
+ if (s !== void 0) return s;
238
+ let c = q({
239
+ ...e,
240
+ now: t,
241
+ row: a
242
+ }).finally(() => {
243
+ V.delete(o);
244
+ });
245
+ return V.set(o, c), c;
246
+ }, q = async (n) => {
247
+ let { store: o, subjectId: s, now: c } = n, u = n.definition, d = n.sleep ?? B, f = u.refreshSkewMs ?? 6e4, m = n.row.id;
248
+ if (await o.updateMany("_voltro_connections", { refreshLockedUntil: new Date(c() + 3e4) }, { where: r(i("id", m), l("refreshLockedUntil", new Date(c()))) }) === 0) {
249
+ for (let e = 0; e < 10; e++) {
250
+ await d(200);
251
+ let e = await k(o, u.id, s);
252
+ if (e === void 0) throw new z(u.id, s);
253
+ if (!G(e, f, c())) return W(u, e);
254
+ }
255
+ throw new p({
256
+ connectionId: u.id,
257
+ reason: "another replica held the refresh lease and did not publish a fresh token in time",
258
+ transient: !0
259
+ });
260
+ }
261
+ let _ = n.row.refreshToken;
262
+ if (_ == null) throw await o.update(g, m, {
263
+ status: "expired",
264
+ lastError: "access token expired and no refresh token is on file — reconnect required",
265
+ refreshLockedUntil: /* @__PURE__ */ new Date(0),
266
+ updatedAt: new Date(c())
267
+ }), new p({
268
+ connectionId: u.id,
269
+ reason: "access token expired and no refresh token is on file — reconnect required",
270
+ transient: !1
271
+ });
272
+ let v;
273
+ try {
274
+ v = await I(u, e(String(_), a(g, "refreshToken", m)), c);
275
+ } catch (e) {
276
+ let n = e instanceof p ? e : new p({
277
+ connectionId: u.id,
278
+ reason: String(e),
279
+ transient: !0
280
+ });
281
+ throw await o.update(g, m, {
282
+ status: n.transient ? "error" : "revoked",
283
+ lastError: n.reason,
284
+ refreshLockedUntil: /* @__PURE__ */ new Date(0),
285
+ updatedAt: new Date(c()),
286
+ ...n.transient ? {} : {
287
+ accessToken: t("", a(g, "accessToken", m)),
288
+ refreshToken: null,
289
+ expiresAt: null
290
+ }
291
+ }), h.warn("connection refresh failed", {
292
+ connectionId: u.id,
293
+ transient: n.transient,
294
+ reason: n.reason
295
+ }), n;
296
+ }
297
+ let y = {
298
+ status: "connected",
299
+ accessToken: t(v.accessToken, a(g, "accessToken", m)),
300
+ ...v.refreshToken === void 0 ? {} : { refreshToken: t(v.refreshToken, a(g, "refreshToken", m)) },
301
+ expiresAt: v.expiresAt === void 0 ? null : new Date(v.expiresAt),
302
+ ...v.scopes.length > 0 ? { scopes: v.scopes.join(" ") } : {},
303
+ lastError: null,
304
+ refreshLockedUntil: /* @__PURE__ */ new Date(0),
305
+ updatedAt: new Date(c())
306
+ };
307
+ return W(u, await o.update(g, m, y) ?? {
308
+ ...n.row,
309
+ ...y
310
+ });
311
+ }, J = (e = 32) => {
312
+ let t = new Uint8Array(e);
313
+ return globalThis.crypto.getRandomValues(t), Array.from(t, (e) => e.toString(16).padStart(2, "0")).join("");
314
+ }, Y = (e) => {
315
+ let t = "";
316
+ for (let n of e) t += String.fromCharCode(n);
317
+ return btoa(t).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
318
+ }, ie = async (e) => {
319
+ let t = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(e));
320
+ return Y(new Uint8Array(t));
321
+ }, X = 6e5, Z = (e) => `/_voltro/connections/${e}/callback`, Q = (e) => e === void 0 || e.length === 0 || !e.startsWith("/") || e.startsWith("//") || e.startsWith("/\\") ? "/" : e, $ = (e, t) => e.redirectUri ?? `${t.replace(/\/+$/, "")}${Z(e.id)}`, ae = async (e) => {
322
+ let n = (e.now ?? (() => /* @__PURE__ */ new Date()))(), { definition: r } = e, i = J(), s = r.pkce === !1 ? null : J(48), c = $(r, e.publicUrl), l = o(ee, y);
323
+ await e.store.insert(y, {
324
+ id: l,
325
+ state: i,
326
+ connectionId: r.id,
327
+ subjectId: e.subjectId,
328
+ tenantId: e.tenantId,
329
+ codeVerifier: s === null ? null : t(s, a(y, "codeVerifier", l)),
330
+ redirectTo: Q(e.redirectTo),
331
+ createdAt: n,
332
+ expiresAt: new Date(n.getTime() + X)
333
+ });
334
+ let u = new URLSearchParams({
335
+ response_type: "code",
336
+ client_id: x(r.clientId),
337
+ redirect_uri: c,
338
+ state: i,
339
+ ...r.scopes && r.scopes.length > 0 ? { scope: r.scopes.join(" ") } : {},
340
+ ...r.authorizeParams ?? {}
341
+ });
342
+ s !== null && (u.set("code_challenge", await ie(s)), u.set("code_challenge_method", "S256"));
343
+ let d = r.authorizeUrl.includes("?") ? "&" : "?";
344
+ return {
345
+ authorizeUrl: `${r.authorizeUrl}${d}${u.toString()}`,
346
+ state: i
347
+ };
348
+ }, oe = async (t) => {
349
+ let n = (t.now ?? (() => /* @__PURE__ */ new Date()))(), r = (await t.store.query({
350
+ table: y,
351
+ predicate: i("state", t.state),
352
+ order: [],
353
+ take: 1,
354
+ skip: void 0,
355
+ projection: void 0
356
+ }))[0];
357
+ if (r === void 0) throw new p({
358
+ connectionId: "unknown",
359
+ reason: "unknown or already-used handshake state",
360
+ transient: !1
361
+ });
362
+ await t.store.delete(y, r.id);
363
+ let o = String(r.connectionId), s = T(r.expiresAt);
364
+ if (s !== null && s.getTime() < n.getTime()) throw new p({
365
+ connectionId: o,
366
+ reason: "handshake expired — start again",
367
+ transient: !1
368
+ });
369
+ let c = t.registry.get(o);
370
+ if (c === void 0 || c.kind !== "oauth2") throw new p({
371
+ connectionId: o,
372
+ reason: "no oauth2 connection is declared under that id",
373
+ transient: !1
374
+ });
375
+ let l = r.codeVerifier === null || r.codeVerifier === void 0 ? null : e(String(r.codeVerifier), a(y, "codeVerifier", r.id)), u = await F(c, {
376
+ code: t.code,
377
+ redirectUri: $(c, t.publicUrl),
378
+ codeVerifier: l
379
+ }, () => n.getTime()), d = c.identify === void 0 ? void 0 : await c.identify(u).catch((e) => {
380
+ h.warn("connection identify failed; storing without account details", {
381
+ connectionId: o,
382
+ reason: String(e)
383
+ });
384
+ });
385
+ return await A({
386
+ store: t.store,
387
+ definition: c,
388
+ subjectId: String(r.subjectId),
389
+ tenantId: r.tenantId ?? null,
390
+ tokens: u,
391
+ ...d === void 0 ? {} : { account: d },
392
+ now: () => n
393
+ }), {
394
+ connectionId: o,
395
+ redirectTo: Q(String(r.redirectTo ?? "/"))
396
+ };
397
+ }, se = async (e, t = /* @__PURE__ */ new Date()) => {
398
+ let n = await e.query({
399
+ table: y,
400
+ predicate: l("expiresAt", t),
401
+ order: [],
402
+ take: void 0,
403
+ skip: void 0,
404
+ projection: ["id"]
405
+ }), r = 0;
406
+ for (let t of n) await e.delete("_voltro_connection_grants", t.id) && r++;
407
+ return r;
408
+ };
409
+ //#endregion
410
+ export { g as CONNECTIONS_TABLE, y as CONNECTION_GRANTS_TABLE, z as ConnectionNotConnected, L as DEFAULT_REFRESH_SKEW_MS, X as GRANT_TTL_MS, R as REFRESH_LEASE_MS, b as _voltroConnectionGrantsTable, v as _voltroConnectionsTable, C as assertConnectionCipherConfigured, ae as beginOAuthConnect, U as clearConnectionRefreshFlights, oe as completeOAuthConnect, Z as connectionCallbackPath, S as connectionLabel, ne as defineConnection, F as exchangeAuthorizationCode, j as forgetCredential, M as listConnectionStates, w as makeConnectionRegistry, x as oauthCredentialValue, O as projectConnectionState, I as refreshAccessToken, K as resolveConnection, $ as resolveRedirectUri, Q as sanitizeRedirectTo, A as storeCredential, se as sweepExpiredGrants };
@@ -0,0 +1,56 @@
1
+ import { noteCacheVolatile as e } from "./computedQueryCache.js";
2
+ import { ConnectionNotConnected as t, listConnectionStates as n, resolveConnection as r } from "./connectionVault.js";
3
+ //#region src/connectionsFacade.ts
4
+ var i, a = (e) => {
5
+ i = e;
6
+ }, o = () => i, s = (e, t) => {
7
+ let n = i;
8
+ return n === void 0 ? Promise.reject(/* @__PURE__ */ Error(`resolveConnectionForSubject("${t}"): no connection vault is registered — the app declares no \`*.connection.ts\`, or this call ran before boot completed.`)) : n(e, t);
9
+ }, c = (e) => async (n, i) => {
10
+ let a = e.registry.get(i);
11
+ if (a === void 0) throw Error(`connection "${i}" is not declared. Declared: ${e.registry.list().map((e) => e.id).join(", ") || "(none)"}.`);
12
+ let o = n.id ?? null;
13
+ if (o === null || o === "") throw new t(i, "<anonymous>");
14
+ return r({
15
+ store: e.store,
16
+ definition: a,
17
+ subjectId: o
18
+ });
19
+ }, l = (i) => {
20
+ let a = i.subject.id ?? null, o = async (n) => {
21
+ e("ctx.connections");
22
+ let o = i.registry.get(n);
23
+ if (o === void 0) throw Error(`ctx.connections.get("${n}"): no such connection is declared. Declared: ${i.registry.list().map((e) => e.id).join(", ") || "(none)"}.`);
24
+ if (a === null || a === "") throw new t(n, "<anonymous>");
25
+ return r({
26
+ store: i.store,
27
+ definition: o,
28
+ subjectId: a
29
+ });
30
+ };
31
+ return {
32
+ get: o,
33
+ tryGet: async (e) => {
34
+ try {
35
+ return await o(e);
36
+ } catch (e) {
37
+ if (e instanceof t) return null;
38
+ throw e;
39
+ }
40
+ },
41
+ list: async () => a === null || a === "" ? i.registry.list().map((e) => ({
42
+ connectionId: e.id,
43
+ kind: e.kind,
44
+ label: e.label ?? e.id,
45
+ status: "disconnected",
46
+ accountId: null,
47
+ accountLabel: null,
48
+ scopes: [],
49
+ expiresAt: null,
50
+ lastError: null,
51
+ connectedAt: null
52
+ })) : n(i.store, i.registry, a)
53
+ };
54
+ };
55
+ //#endregion
56
+ export { o as getConnectionResolver, c as makeConnectionResolver, l as makeConnectionsFacade, s as resolveConnectionForSubject, a as setConnectionResolver };
@@ -0,0 +1,145 @@
1
+ import { cronForInterval as e, externalizationRefusal as t, registerExternalizedTask as n } from "./externalizedTasks.js";
2
+ //#region src/coordinatedSchedule.ts
3
+ var r = (e) => {
4
+ if (e === void 0) return null;
5
+ let t = e.nextDueInMs;
6
+ return {
7
+ idle: e.idle === !0,
8
+ ...t === null ? { nextDueInMs: null } : typeof t == "number" && Number.isFinite(t) ? { nextDueInMs: t } : {}
9
+ };
10
+ }, i = /* @__PURE__ */ new WeakMap(), a = (e) => i.get(e)?.() ?? null, o = 3e4, s = (e = process.env) => {
11
+ let t = e.VOLTRO_POLL_CEILING_MS;
12
+ if (t === void 0) return o;
13
+ let n = Number(t);
14
+ return Number.isFinite(n) && n > 0 ? n : o;
15
+ }, c = (a, o, c, u) => {
16
+ let d = Math.max(o, u.maxIdleIntervalMs ?? s()), f = !1, p, m = /* @__PURE__ */ new Set(), h = (e) => {
17
+ let t = Promise.resolve().then(e);
18
+ return m.add(t), t.then(() => m.delete(t), () => m.delete(t)), t;
19
+ }, g, _ = !1, v = o, y = !1, b = !1, x = 0, S = null, C = {
20
+ ticks: 0,
21
+ claimWins: 0,
22
+ claimLosses: 0,
23
+ claimFailures: 0,
24
+ taskFailures: 0,
25
+ wakes: 0,
26
+ coalescedWakes: 0,
27
+ lastTickAt: null,
28
+ lastWakeAt: null,
29
+ lastDecision: "starting",
30
+ lastOutcome: null
31
+ }, w = (e) => {
32
+ f || (g !== void 0 && clearTimeout(g), g = void 0, S = null, e === "disarm" ? _ = !0 : (_ = !1, v = Math.max(0, Math.min(e, d)), S = Date.now() + v, g = setTimeout(() => {
33
+ E();
34
+ }, v), typeof g.unref == "function" && g.unref()));
35
+ }, T = async () => {
36
+ if (f) return;
37
+ if (y) {
38
+ b = !0;
39
+ return;
40
+ }
41
+ C.ticks++, C.lastTickAt = Date.now(), C.lastOutcome = null, S = null, y = !0;
42
+ let e = new Date(Math.floor(Date.now() / o) * o);
43
+ try {
44
+ let t = !1;
45
+ try {
46
+ t = await u.coordinator.tryClaim(a, e, o);
47
+ } catch (e) {
48
+ C.claimFailures++, C.lastDecision = "claim-failed", u.log?.warn("coordinated schedule: claim check failed — skipping this tick", {
49
+ task: a,
50
+ reason: e?.message ?? String(e)
51
+ }), w(o);
52
+ return;
53
+ }
54
+ if (!t) {
55
+ C.claimLosses++, C.lastDecision = "claim-lost", w(o);
56
+ return;
57
+ }
58
+ if (f) return;
59
+ let n;
60
+ C.claimWins++;
61
+ try {
62
+ n = await c();
63
+ } catch (e) {
64
+ C.taskFailures++, C.lastDecision = "task-failed", u.log?.warn("coordinated schedule: task threw", {
65
+ task: a,
66
+ reason: e?.message ?? String(e)
67
+ }), w(o);
68
+ return;
69
+ }
70
+ C.lastOutcome = r(n);
71
+ let i = l(C.lastOutcome ?? void 0, o, v, d, u.disarmWhenIdle ?? !1);
72
+ C.lastDecision = i === "disarm" ? "idle-disarmed" : n?.idle ? "idle-backoff" : "active", w(i);
73
+ } finally {
74
+ y = !1, b && !f && (b = !1, w(0));
75
+ }
76
+ }, E = () => f ? Promise.resolve() : h(T), D = async () => {
77
+ if (f) throw Error("Coordinated schedule is stopped");
78
+ C.ticks++, C.lastTickAt = Date.now(), C.lastOutcome = null;
79
+ let e = new Date(Math.floor(Date.now() / o) * o), t;
80
+ try {
81
+ t = await (u.externalClaim ?? u.coordinator).tryClaim(a, e, o);
82
+ } catch (e) {
83
+ throw C.claimFailures++, C.lastDecision = "claim-failed", e;
84
+ }
85
+ if (!t) return C.claimLosses++, C.lastDecision = "claim-lost", "deduplicated";
86
+ if (f) throw Error("Coordinated schedule is stopped");
87
+ C.claimWins++;
88
+ try {
89
+ let e = await c();
90
+ C.lastOutcome = r(e), C.lastDecision = "active";
91
+ } catch (e) {
92
+ throw C.taskFailures++, C.lastDecision = "task-failed", e;
93
+ }
94
+ return "ran";
95
+ }, O = () => f ? Promise.reject(/* @__PURE__ */ Error("Coordinated schedule is stopped")) : h(D), k = u.externalize === !0 ? t(a, o) : void 0, A = u.externalize === !0 && k === void 0;
96
+ k !== void 0 && u.log?.warn(`coordinated schedule: REFUSING to externalise this task — ${k}`, {
97
+ task: a,
98
+ intervalMs: o
99
+ });
100
+ let j;
101
+ A ? (j = (u.externalTasks?.register ?? n)({
102
+ name: a,
103
+ intervalMs: o,
104
+ cron: e(o),
105
+ run: O
106
+ }), u.log?.info("coordinated schedule: externalised — no in-process timer", {
107
+ task: a,
108
+ intervalMs: o
109
+ })) : w(o);
110
+ let M = {
111
+ name: a,
112
+ currentIntervalMs: () => v,
113
+ isArmed: () => !f && !_ && !A,
114
+ wake: () => {
115
+ if (f) return;
116
+ C.wakes++, C.lastWakeAt = Date.now();
117
+ let e = Date.now();
118
+ !_ && e - x < o ? C.coalescedWakes++ : (C.lastDecision = "wake", x = e, y ? b = !0 : w(0));
119
+ },
120
+ stop: () => (p === void 0 && (f = !0, g !== void 0 && clearTimeout(g), g = void 0, S = null, j?.(), p = Promise.allSettled([...m]).then(() => {})), p)
121
+ };
122
+ return i.set(M, () => ({
123
+ ...C,
124
+ lastOutcome: C.lastOutcome === null ? null : { ...C.lastOutcome },
125
+ task: a,
126
+ state: f ? "stopped" : A ? "externalized" : y ? "running" : _ ? "disarmed" : "armed",
127
+ baseIntervalMs: o,
128
+ currentIntervalMs: v,
129
+ maxIdleIntervalMs: d,
130
+ nextTickAt: S,
131
+ disarmWhenIdle: u.disarmWhenIdle ?? !1
132
+ })), M;
133
+ }, l = (e, t, n, r, i = !1) => {
134
+ if (e === void 0 || !e.idle) return t;
135
+ let a = typeof e.nextDueInMs == "number" && Number.isFinite(e.nextDueInMs) ? e.nextDueInMs : void 0;
136
+ if (a === void 0 && e.nextDueInMs === null && i) return "disarm";
137
+ let o = Math.min(Math.max(n, t) * 2, r);
138
+ return a === void 0 ? o : Math.max(t, i ? a : Math.min(o, a));
139
+ }, u = (e) => (t, n, r, i) => c(t, n, r, {
140
+ ...e,
141
+ ...e.externalizeTask === void 0 ? {} : { externalize: e.externalizeTask(t) },
142
+ ...i?.disarmWhenIdle === void 0 ? {} : { disarmWhenIdle: i.disarmWhenIdle }
143
+ });
144
+ //#endregion
145
+ export { o as DEFAULT_MAX_IDLE_INTERVAL_MS, a as inspectCoordinatedSchedule, u as makeCoordinatedScheduler, l as nextDelay, s as readPollCeilingMs, c as scheduleCoordinated };