@voltro/runtime 0.85.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 +666 -0
  2. package/THIRD-PARTY-NOTICES.md +5 -33
  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 +88 -69
  67. package/dist/index.js +144 -15292
  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 +7 -7
@@ -0,0 +1,61 @@
1
+ import { inSet as e } from "@voltro/database";
2
+ //#region src/dataloader.ts
3
+ var t = () => {
4
+ let e, t;
5
+ return {
6
+ promise: new Promise((n, r) => {
7
+ e = n, t = r;
8
+ }),
9
+ resolve: e,
10
+ reject: t
11
+ };
12
+ }, n = (e) => {
13
+ Promise.resolve().then(e);
14
+ }, r = (r) => {
15
+ let i = r.schedule ?? n, a = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map(), c = async (t) => {
16
+ let n = s.get(t);
17
+ if (n === void 0) return;
18
+ s.delete(t);
19
+ let i = [...n.keys()];
20
+ try {
21
+ let s = await r.store.query({
22
+ table: t,
23
+ predicate: e("id", i),
24
+ order: [],
25
+ take: void 0,
26
+ skip: void 0,
27
+ projection: void 0
28
+ }), c = new Map(s.map((e) => [String(e.id), e])), l = a.get(t) ?? /* @__PURE__ */ new Map();
29
+ a.set(t, l);
30
+ for (let [e, r] of n) {
31
+ let n = c.get(e) ?? null;
32
+ l.set(e, n), o.get(t)?.delete(e), r.resolve(n);
33
+ }
34
+ } catch (e) {
35
+ for (let [r, i] of n) o.get(t)?.delete(r), i.reject(e);
36
+ } finally {
37
+ o.get(t)?.size === 0 && o.delete(t);
38
+ }
39
+ }, l = (e, n) => {
40
+ if (n.length === 0) return Promise.resolve([]);
41
+ let r = a.get(e), l = !1, u = n.map((n) => {
42
+ if (r?.has(n)) return Promise.resolve(r.get(n) ?? null);
43
+ let i = o.get(e), a = i?.get(n);
44
+ if (a !== void 0) return a.promise;
45
+ i === void 0 && (i = /* @__PURE__ */ new Map(), o.set(e, i));
46
+ let c = t();
47
+ i.set(n, c);
48
+ let u = s.get(e);
49
+ return u === void 0 && (u = /* @__PURE__ */ new Map(), s.set(e, u), l = !0), u.set(n, c), c.promise;
50
+ }), d = Promise.all(u);
51
+ return l && i(() => {
52
+ c(e);
53
+ }), d;
54
+ };
55
+ return {
56
+ load: async (e, t) => (await l(e, [t]))[0] ?? null,
57
+ loadMany: (e, t) => l(e, t)
58
+ };
59
+ };
60
+ //#endregion
61
+ export { r as makeDataLoader };
@@ -0,0 +1,48 @@
1
+ //#region src/declaredTablesGuard.ts
2
+ var e = class extends Error {
3
+ table;
4
+ operation;
5
+ _tag = "UndeclaredTableAccess";
6
+ constructor(e, t) {
7
+ super(`store.${t}('${e}'): this app does not declare a table '${e}'. A table another app owns is reached through that app — bind it under \`apis:\` and call its \`service:\` procedures — never through its tables. If the table is this app's own, declare it with \`table()\` (or add the plugin that declares it).`), this.table = e, this.operation = t, this.name = "UndeclaredTableAccess";
8
+ }
9
+ }, t = (n, r) => {
10
+ let i = (t, n) => {
11
+ if (!r(t)) throw new e(t, n);
12
+ }, a = (e, t) => {
13
+ for (let n of e) i(n, t);
14
+ };
15
+ return {
16
+ query: async (e) => (i(e.table, "query"), n.query(e)),
17
+ insert: async (e, t) => (i(e, "insert"), n.insert(e, t)),
18
+ insertMany: async (e, t) => (i(e, "insertMany"), n.insertMany(e, t)),
19
+ patchJson: async (e, t, r, a, o) => (i(e, "patchJson"), n.patchJson(e, t, r, a, o)),
20
+ update: async (e, t, r) => (i(e, "update"), n.update(e, t, r)),
21
+ updateMany: async (e, t, r) => (i(e, "updateMany"), n.updateMany(e, t, r)),
22
+ delete: async (e, t) => (i(e, "delete"), n.delete(e, t)),
23
+ deleteMany: async (e, t) => (i(e, "deleteMany"), n.deleteMany(e, t)),
24
+ upsert: async (e, t, r) => (i(e, "upsert"), n.upsert(e, t, r)),
25
+ insertIgnore: async (e, t, r) => (i(e, "insertIgnore"), n.insertIgnore(e, t, r)),
26
+ insertIgnoreWithOutcome: async (e, t, r) => (i(e, "insertIgnoreWithOutcome"), n.insertIgnoreWithOutcome(e, t, r)),
27
+ transactional: (e) => n.transactional((n) => e(t(n, r))),
28
+ onChange: (e) => n.onChange(e),
29
+ injectExternalChange: (e) => {
30
+ n.injectExternalChange(e);
31
+ },
32
+ ...n.changeScope === void 0 ? {} : { changeScope: n.changeScope },
33
+ ...n.onChangeStreamGap === void 0 ? {} : { onChangeStreamGap: (e) => n.onChangeStreamGap(e) },
34
+ ...n.upsertWithOutcome === void 0 ? {} : { upsertWithOutcome: async (e, t, r) => (i(e, "upsertWithOutcome"), n.upsertWithOutcome(e, t, r)) },
35
+ ...n.withNamespace === void 0 ? {} : { withNamespace: (e) => t(n.withNamespace(e), r) },
36
+ ...n.raw === void 0 ? {} : { raw: ((...e) => n.raw(...e)) },
37
+ ...n.close === void 0 ? {} : { close: (e) => n.close(e) },
38
+ ...n.ping === void 0 ? {} : { ping: () => n.ping() },
39
+ ...n.emptyTables === void 0 ? {} : { emptyTables: async (e) => (a(e, "emptyTables"), n.emptyTables(e)) },
40
+ ...n.incomingForeignKeys === void 0 ? {} : { incomingForeignKeys: (e) => n.incomingForeignKeys(e) },
41
+ ...n.savepoint === void 0 ? {} : { savepoint: ((e) => n.savepoint(e)) },
42
+ ...n.refreshChangeCaptureExclusions === void 0 ? {} : { refreshChangeCaptureExclusions: () => n.refreshChangeCaptureExclusions() },
43
+ ...n.run === void 0 ? {} : { run: ((e) => n.run(e)) },
44
+ ...n.storedTables === void 0 ? {} : { storedTables: () => n.storedTables() }
45
+ };
46
+ };
47
+ //#endregion
48
+ export { e as UndeclaredTableAccess, t as guardUndeclaredTables };
@@ -0,0 +1,4 @@
1
+ //#region src/defineExecutor.ts
2
+ var e = (e, t) => t;
3
+ //#endregion
4
+ export { e as defineExecutor };
@@ -0,0 +1,32 @@
1
+ import { collectSubqueries as e, getRelations as t } from "@voltro/database";
2
+ //#region src/dependencyGraph.ts
3
+ var n = (e) => {
4
+ let t = /* @__PURE__ */ new Set();
5
+ return r(e, t), t;
6
+ }, r = (t, n) => {
7
+ n.add(t.table);
8
+ for (let e of t.dependentTables ?? []) n.add(e);
9
+ for (let i of t.joins ?? []) {
10
+ n.add(i.table);
11
+ for (let t of e(i.on)) r(t.descriptor, n);
12
+ }
13
+ for (let i of [t.predicate, t.having]) if (i) for (let t of e(i)) r(t.descriptor, n);
14
+ if (t.eager !== void 0 && i(t.table, t.eager, n), t.setOp !== void 0) for (let e of t.setOp.queries) r(e, n);
15
+ if (t.ctes !== void 0) for (let e of t.ctes) r(e.descriptor, n);
16
+ }, i = (e, n, r) => {
17
+ let a = t(e);
18
+ if (a !== void 0) for (let [e, t] of Object.entries(n)) {
19
+ let n = a[e];
20
+ if (n === void 0) continue;
21
+ let o = t === !0 ? {} : t;
22
+ if (n.kind === "manyToMany") {
23
+ let e = n.through().tableName, t = n.target().tableName;
24
+ r.add(e), r.add(t), o.with !== void 0 && i(t, o.with, r);
25
+ } else {
26
+ let e = n.target().tableName;
27
+ r.add(e), o.with !== void 0 && i(e, o.with, r);
28
+ }
29
+ }
30
+ };
31
+ //#endregion
32
+ export { n as resolveDependentTables };
@@ -0,0 +1,228 @@
1
+ import { and as e, eq as t, generateId as n } from "@voltro/database";
2
+ import { createHash as r, randomBytes as i } from "node:crypto";
3
+ //#region src/devices.ts
4
+ var a = "_voltro_devices", o = "actors", s = (e) => r("sha256").update(e, "utf8").digest("hex"), c = (e = "dev_") => {
5
+ let t = e.replace(/_+$/, "");
6
+ return {
7
+ access: `${t}_`,
8
+ refresh: `${t}r_`,
9
+ pairing: `${t}p_`
10
+ };
11
+ }, l = 900, u = 2592e3, d = 600, f = (e) => ({
12
+ id: e.id,
13
+ name: e.name,
14
+ tenantId: e.tenantId,
15
+ scopes: e.scopes ?? [],
16
+ generation: e.generation,
17
+ pairedAt: e.pairedAt,
18
+ lastSeenAt: e.lastSeenAt,
19
+ accessExpiresAt: e.accessExpiresAt,
20
+ revokedAt: e.revokedAt,
21
+ revokedReason: e.revokedReason,
22
+ createdAt: e.createdAt,
23
+ createdBy: e.createdBy,
24
+ ...e.metadata == null ? {} : { metadata: e.metadata }
25
+ }), p = (e, t = {}) => {
26
+ let r = c(t.prefix), o = (t.accessTtlSeconds ?? l) * 1e3, p = (t.refreshTtlSeconds ?? u) * 1e3, m = (t.pairingTtlSeconds ?? d) * 1e3, h = (e, t) => `${e}${i(t).toString("base64url")}`, g = async (t, n, i, a, c) => {
27
+ let l = h(r.access, 30), u = h(r.refresh, 40), d = t.generation + 1, f = a + o, m = a + p;
28
+ return await e.claim(t.id, n, i, {
29
+ accessHash: s(l),
30
+ accessExpiresAt: f,
31
+ refreshHash: s(u),
32
+ refreshExpiresAt: m,
33
+ generation: d,
34
+ lastSeenAt: a,
35
+ ...c
36
+ }) ? {
37
+ deviceId: t.id,
38
+ accessToken: l,
39
+ accessExpiresAt: f,
40
+ refreshToken: u,
41
+ refreshExpiresAt: m,
42
+ generation: d
43
+ } : null;
44
+ };
45
+ return {
46
+ pair: async (t) => {
47
+ let i = n({
48
+ kind: "typeid",
49
+ prefix: "device"
50
+ }, a), o = h(r.pairing, 12), c = Date.now(), l = c + m, u = t.scopes ?? [];
51
+ return await e.ensureActor({
52
+ id: i,
53
+ displayName: t.name
54
+ }), await e.insert({
55
+ id: i,
56
+ tenantId: t.tenantId,
57
+ name: t.name,
58
+ scopes: u,
59
+ pairingHash: s(o),
60
+ pairingExpiresAt: l,
61
+ accessHash: null,
62
+ accessExpiresAt: null,
63
+ refreshHash: null,
64
+ refreshExpiresAt: null,
65
+ previousRefreshHash: null,
66
+ generation: 0,
67
+ pairedAt: null,
68
+ lastSeenAt: null,
69
+ revokedAt: null,
70
+ revokedReason: null,
71
+ createdBy: t.createdBy ?? null,
72
+ createdAt: c,
73
+ ...t.metadata == null ? {} : { metadata: t.metadata }
74
+ }), {
75
+ id: i,
76
+ name: t.name,
77
+ tenantId: t.tenantId,
78
+ scopes: u,
79
+ pairingCode: o,
80
+ pairingExpiresAt: l
81
+ };
82
+ },
83
+ exchange: async (t, n = Date.now()) => {
84
+ if (!t.startsWith(r.pairing)) return null;
85
+ let i = s(t), a = await e.findByPairingHash(i);
86
+ return a === null || a.revokedAt !== null || a.pairedAt !== null || a.pairingExpiresAt === null || a.pairingExpiresAt <= n ? null : g(a, "pairingHash", i, n, {
87
+ pairingHash: null,
88
+ pairingExpiresAt: null,
89
+ pairedAt: n
90
+ });
91
+ },
92
+ rotate: async (t, n = Date.now()) => {
93
+ if (!t.startsWith(r.refresh)) return null;
94
+ let i = s(t), a = await e.findByRefreshHash(i);
95
+ if (a === null) {
96
+ let t = await e.findByPreviousRefreshHash(i);
97
+ return t !== null && t.revokedAt === null && await e.patch(t.id, {
98
+ revokedAt: n,
99
+ revokedReason: "refresh-replay"
100
+ }), null;
101
+ }
102
+ return a.revokedAt !== null || a.pairedAt === null || a.refreshExpiresAt === null || a.refreshExpiresAt <= n ? null : g(a, "refreshHash", i, n, { previousRefreshHash: i });
103
+ },
104
+ resolveAccess: async (t, n = Date.now()) => {
105
+ let r = await e.findByAccessHash(t);
106
+ return r === null || r.revokedAt !== null || r.pairedAt === null || r.accessExpiresAt === null || r.accessExpiresAt <= n ? null : {
107
+ id: r.id,
108
+ tenantId: r.tenantId,
109
+ name: r.name,
110
+ scopes: r.scopes ?? [],
111
+ generation: r.generation,
112
+ accessExpiresAt: r.accessExpiresAt,
113
+ ...r.metadata == null ? {} : { metadata: r.metadata }
114
+ };
115
+ },
116
+ revoke: async (t, n, r = Date.now()) => {
117
+ let i = await e.findById(t);
118
+ return i === null || i.tenantId !== n ? !1 : (i.revokedAt === null && await e.patch(t, {
119
+ revokedAt: r,
120
+ revokedReason: "revoked"
121
+ }), !0);
122
+ },
123
+ list: async (t) => (await e.listByTenant(t)).map(f)
124
+ };
125
+ }, m = () => {
126
+ let e = /* @__PURE__ */ new Map(), t = /* @__PURE__ */ new Map(), n = (t) => async (n) => [...e.values()].find((e) => e[t] === n) ?? null;
127
+ return {
128
+ actors: t,
129
+ insert: async (t) => {
130
+ e.set(t.id, t);
131
+ },
132
+ findById: async (t) => e.get(t) ?? null,
133
+ findByPairingHash: n("pairingHash"),
134
+ findByAccessHash: n("accessHash"),
135
+ findByRefreshHash: n("refreshHash"),
136
+ findByPreviousRefreshHash: n("previousRefreshHash"),
137
+ listByTenant: async (t) => {
138
+ let n = [...e.values()], r = 0;
139
+ for (let e of n) e.tenantId === t && (n[r++] = e);
140
+ return n.length = r, n;
141
+ },
142
+ patch: async (t, n) => {
143
+ let r = e.get(t);
144
+ r && e.set(t, {
145
+ ...r,
146
+ ...n
147
+ });
148
+ },
149
+ claim: async (t, n, r, i) => {
150
+ let a = e.get(t);
151
+ return !a || a[n] !== r ? !1 : (e.set(t, {
152
+ ...a,
153
+ ...i
154
+ }), !0);
155
+ },
156
+ ensureActor: async (e) => {
157
+ t.has(e.id) || t.set(e.id, e);
158
+ }
159
+ };
160
+ }, h = (e) => e == null ? null : e instanceof Date ? e.getTime() : typeof e == "number" ? e : new Date(String(e)).getTime(), g = (e) => ({
161
+ id: String(e.id),
162
+ tenantId: String(e.tenantId),
163
+ name: String(e.name),
164
+ scopes: e.scopes ?? null,
165
+ pairingHash: e.pairingHash ?? null,
166
+ pairingExpiresAt: h(e.pairingExpiresAt),
167
+ accessHash: e.accessHash ?? null,
168
+ accessExpiresAt: h(e.accessExpiresAt),
169
+ refreshHash: e.refreshHash ?? null,
170
+ refreshExpiresAt: h(e.refreshExpiresAt),
171
+ previousRefreshHash: e.previousRefreshHash ?? null,
172
+ generation: Number(e.generation ?? 0),
173
+ pairedAt: h(e.pairedAt),
174
+ lastSeenAt: h(e.lastSeenAt),
175
+ revokedAt: h(e.revokedAt),
176
+ revokedReason: e.revokedReason ?? null,
177
+ createdBy: e.createdBy ?? null,
178
+ createdAt: h(e.createdAt) ?? 0,
179
+ ...e.metadata == null ? {} : { metadata: e.metadata }
180
+ }), _ = [
181
+ "pairingExpiresAt",
182
+ "accessExpiresAt",
183
+ "refreshExpiresAt",
184
+ "pairedAt",
185
+ "lastSeenAt",
186
+ "revokedAt",
187
+ "createdAt"
188
+ ], v = (e) => {
189
+ let t = { ...e };
190
+ for (let e of _) typeof t[e] == "number" && (t[e] = new Date(t[e]));
191
+ return t;
192
+ }, y = (n) => {
193
+ let r = async (e) => await n.query({
194
+ table: a,
195
+ predicate: e,
196
+ order: [],
197
+ take: void 0,
198
+ skip: void 0,
199
+ projection: void 0
200
+ }), i = async (e) => {
201
+ let t = await r(e);
202
+ return t[0] ? g(t[0]) : null;
203
+ };
204
+ return {
205
+ ensureActor: async (e) => {
206
+ await n.insertIgnore(o, {
207
+ id: e.id,
208
+ kind: "serviceAccount",
209
+ displayName: e.displayName
210
+ }, { conflictColumns: ["id"] });
211
+ },
212
+ insert: async (e) => {
213
+ await n.insert(a, v(e));
214
+ },
215
+ findById: (e) => i(t("id", e)),
216
+ findByPairingHash: (e) => i(t("pairingHash", e)),
217
+ findByAccessHash: (e) => i(t("accessHash", e)),
218
+ findByRefreshHash: (e) => i(t("refreshHash", e)),
219
+ findByPreviousRefreshHash: (e) => i(t("previousRefreshHash", e)),
220
+ listByTenant: async (e) => (await r(t("tenantId", e))).map(g),
221
+ patch: async (e, t) => {
222
+ await n.update(a, e, v(t));
223
+ },
224
+ claim: async (r, i, o, s) => await n.updateMany(a, v(s), { where: e(t("id", r), t(i, o)) }) === 1
225
+ };
226
+ };
227
+ //#endregion
228
+ export { a as DEVICES_TABLE, y as dataStoreDeviceStore, c as deviceTokenPrefixes, p as makeDeviceService, m as memoryDeviceStore, s as sha256HexOf };
@@ -0,0 +1,168 @@
1
+ import { deviceTokenPrefixes as e, sha256HexOf as t } from "./devices.js";
2
+ import { Schema as n } from "effect";
3
+ import { ADMIN_SCOPE as r, DEVICES_MANAGE_SCOPE as i, ScopeError as a, Unauthenticated as o } from "@voltro/protocol";
4
+ import { defineRestRoute as s, requireAnyScope as c } from "@voltro/protocol/rest";
5
+ //#region src/devicesWiring.ts
6
+ var l = (n, r = {}) => {
7
+ let i = e(r.prefix);
8
+ return {
9
+ id: "voltro-device",
10
+ claimsBearerPrefix: i.access,
11
+ resolve: async (e) => {
12
+ let r = e.headers.authorization ?? e.headers.Authorization;
13
+ if (!r) return { kind: "skip" };
14
+ let a = /^Bearer\s+(.+)$/i.exec(r.trim());
15
+ if (!a) return { kind: "skip" };
16
+ let o = a[1].trim();
17
+ if (!o.startsWith(i.access)) return { kind: "skip" };
18
+ let s = await n.resolveAccess(t(o), Date.now());
19
+ return s === null ? {
20
+ kind: "failed",
21
+ reason: "unknown, expired or revoked device token"
22
+ } : {
23
+ kind: "matched",
24
+ subject: {
25
+ type: "serviceAccount",
26
+ id: s.id,
27
+ tenantId: s.tenantId,
28
+ auditActorId: null,
29
+ scopes: s.scopes,
30
+ metadata: {
31
+ ...s.metadata ?? {},
32
+ provider: "voltro-device",
33
+ device: {
34
+ name: s.name,
35
+ generation: s.generation
36
+ }
37
+ }
38
+ },
39
+ credentialExpiresAt: s.accessExpiresAt
40
+ };
41
+ }
42
+ };
43
+ }, u = (e) => {
44
+ let t = e.subject;
45
+ return {
46
+ id: t.id ?? null,
47
+ tenantId: t.tenantId ?? null,
48
+ scopes: t.scopes ?? []
49
+ };
50
+ }, d = n.Struct({
51
+ deviceId: n.String,
52
+ accessToken: n.String,
53
+ accessExpiresAt: n.Number,
54
+ refreshToken: n.String,
55
+ refreshExpiresAt: n.Number,
56
+ generation: n.Number
57
+ }), f = (e, t = "/v1/devices") => {
58
+ let l = n.Struct({
59
+ id: n.String,
60
+ name: n.String,
61
+ tenantId: n.String,
62
+ scopes: n.Array(n.String),
63
+ generation: n.Number,
64
+ pairedAt: n.NullOr(n.Number),
65
+ lastSeenAt: n.NullOr(n.Number),
66
+ accessExpiresAt: n.NullOr(n.Number),
67
+ revokedAt: n.NullOr(n.Number),
68
+ revokedReason: n.NullOr(n.String),
69
+ createdAt: n.Number,
70
+ createdBy: n.NullOr(n.String),
71
+ metadata: n.optional(n.NullOr(n.Record(n.String, n.Unknown)))
72
+ }), f = c([r, i]);
73
+ return [
74
+ s({
75
+ method: "POST",
76
+ path: `${t}/pair`,
77
+ summary: "Pair a device: create it and return its one-time pairing code.",
78
+ guards: [f],
79
+ input: n.Struct({ body: n.Struct({
80
+ name: n.String,
81
+ scopes: n.optional(n.Array(n.String)),
82
+ metadata: n.optional(n.NullOr(n.Record(n.String, n.Unknown)))
83
+ }) }),
84
+ output: n.Struct({
85
+ id: n.String,
86
+ pairingCode: n.String,
87
+ pairingExpiresAt: n.Number
88
+ }),
89
+ handler: async (t, n) => {
90
+ let o = t.body, s = u(n);
91
+ if (s.tenantId === null || s.tenantId === "") throw new a({
92
+ required: i,
93
+ message: "a device is paired into the caller's tenant, and this caller has none"
94
+ });
95
+ let c = new Set(s.scopes), l = o.scopes ?? [];
96
+ if (!c.has(r)) {
97
+ let e = l.filter((e) => !c.has(e));
98
+ if (e.length > 0) throw new a({
99
+ required: String(e[0]),
100
+ message: `cannot pair a device with scopes you do not hold: ${e.join(", ")}`
101
+ });
102
+ }
103
+ let d = await e.pair({
104
+ tenantId: s.tenantId,
105
+ name: o.name,
106
+ scopes: l,
107
+ createdBy: s.id,
108
+ ...o.metadata === void 0 ? {} : { metadata: o.metadata }
109
+ });
110
+ return {
111
+ id: d.id,
112
+ pairingCode: d.pairingCode,
113
+ pairingExpiresAt: d.pairingExpiresAt
114
+ };
115
+ }
116
+ }),
117
+ s({
118
+ method: "POST",
119
+ path: `${t}/exchange`,
120
+ summary: "Exchange a pairing code for the first access + refresh token pair (once).",
121
+ input: n.Struct({ body: n.Struct({ pairingCode: n.String }) }),
122
+ output: d,
123
+ handler: async (t) => {
124
+ let n = await e.exchange(t.body.pairingCode);
125
+ if (n === null) throw new o({ reason: "pairing code unknown, expired or already exchanged" });
126
+ return n;
127
+ }
128
+ }),
129
+ s({
130
+ method: "POST",
131
+ path: `${t}/rotate`,
132
+ summary: "Rotate: a refresh token for a new access + refresh pair. A replayed refresh token revokes the device.",
133
+ input: n.Struct({ body: n.Struct({ refreshToken: n.String }) }),
134
+ output: d,
135
+ handler: async (t) => {
136
+ let n = await e.rotate(t.body.refreshToken);
137
+ if (n === null) throw new o({ reason: "refresh token unknown, expired, revoked or replayed" });
138
+ return n;
139
+ }
140
+ }),
141
+ s({
142
+ method: "GET",
143
+ path: t,
144
+ summary: "List the caller tenant's devices (no secrets).",
145
+ guards: [f],
146
+ input: n.Struct({}),
147
+ output: n.Struct({ devices: n.Array(l) }),
148
+ handler: async (t, n) => {
149
+ let r = u(n).tenantId;
150
+ return { devices: r === null || r === "" ? [] : [...await e.list(r)] };
151
+ }
152
+ }),
153
+ s({
154
+ method: "POST",
155
+ path: `${t}/revoke`,
156
+ summary: "Revoke a device by id.",
157
+ guards: [f],
158
+ input: n.Struct({ body: n.Struct({ id: n.String }) }),
159
+ output: n.Struct({ ok: n.Boolean }),
160
+ handler: async (t, n) => {
161
+ let r = u(n).tenantId;
162
+ return { ok: r === null || r === "" ? !1 : await e.revoke(t.body.id, r) };
163
+ }
164
+ })
165
+ ];
166
+ };
167
+ //#endregion
168
+ export { f as deviceManagementRoutes, l as frameworkDeviceStrategy };
@@ -0,0 +1,29 @@
1
+ //#region src/dialectReplicationAdapter.ts
2
+ var e = () => {
3
+ let e = {
4
+ primary: 0,
5
+ lagByReplica: /* @__PURE__ */ new Map()
6
+ };
7
+ return {
8
+ adapter: {
9
+ async capturePrimaryPosition(t) {
10
+ return String(e.primary);
11
+ },
12
+ async probeReplicaPosition(t) {
13
+ let n = e.lagByReplica.get(t) ?? 0;
14
+ return String(Math.max(0, e.primary - n));
15
+ },
16
+ compare(e, t) {
17
+ return Number(t) >= Number(e) ? "caught-up" : "behind";
18
+ }
19
+ },
20
+ bumpPrimary() {
21
+ e.primary += 1;
22
+ },
23
+ setLag(t, n) {
24
+ e.lagByReplica.set(t, n);
25
+ }
26
+ };
27
+ };
28
+ //#endregion
29
+ export { e as memoryReplicationAdapter };