@voltro/runtime 0.86.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 (70) hide show
  1. package/CHANGELOG.md +5321 -0
  2. package/README.md +43 -0
  3. package/THIRD-PARTY-NOTICES.md +860 -221
  4. package/dist/analyticsSink.js +8 -14
  5. package/dist/apiKeys.js +82 -78
  6. package/dist/apiRoleBucketSampler.js +64 -0
  7. package/dist/apiRoleWorkloadHistory.js +56 -0
  8. package/dist/apiRoleWorkloadMetrics.js +78 -0
  9. package/dist/apiRoleWorkloadStorage.js +95 -0
  10. package/dist/approvals.js +67 -74
  11. package/dist/authMiddlewareLayer.js +7 -6
  12. package/dist/bindEvent.js +48 -28
  13. package/dist/bootListener.js +106 -39
  14. package/dist/bootStoreCodec.js +38 -37
  15. package/dist/circuitBreaker.js +1 -72
  16. package/dist/composeAnalytics.js +31 -10
  17. package/dist/computedQueryCache.js +231 -0
  18. package/dist/connectionsFacade.js +32 -30
  19. package/dist/coordinatedSchedule.js +84 -78
  20. package/dist/dataStore.js +154 -121
  21. package/dist/declaredTablesGuard.js +2 -1
  22. package/dist/dispatcher.js +403 -328
  23. package/dist/effectStore.js +2 -2
  24. package/dist/engineClients.js +24 -0
  25. package/dist/externalizedTasks.js +19 -6
  26. package/dist/fieldCipher.js +109 -40
  27. package/dist/fieldKeyRegistry.js +295 -0
  28. package/dist/fieldKeyRekey.js +286 -0
  29. package/dist/fieldKeyScan.js +136 -0
  30. package/dist/fieldKeyring.js +77 -0
  31. package/dist/hot.d.ts +56 -0
  32. package/dist/hot.js +3 -0
  33. package/dist/hotDataStore.js +148 -0
  34. package/dist/hotDataStoreSnapshot.js +98 -0
  35. package/dist/hotDataStoreTables.js +18 -0
  36. package/dist/http-range.d.ts +53 -0
  37. package/dist/http-range.js +52 -0
  38. package/dist/httpClientSsrf.js +12 -37
  39. package/dist/httpClientTracing.js +12 -9
  40. package/dist/httpCompression.js +17 -17
  41. package/dist/httpInputStream.js +82 -0
  42. package/dist/httpRouteMounts.js +1 -0
  43. package/dist/idempotencyStore.js +92 -52
  44. package/dist/index.d.ts +1164 -451
  45. package/dist/index.js +143 -132
  46. package/dist/kvFacade.js +23 -17
  47. package/dist/memoryQueryJoins.js +16 -16
  48. package/dist/memorySetOperations.js +43 -0
  49. package/dist/metrics.js +38 -24
  50. package/dist/outbox.js +1 -1
  51. package/dist/readRecorder.js +35 -34
  52. package/dist/readScopePredicate.js +12 -0
  53. package/dist/replicatedDataStore.js +22 -4
  54. package/dist/rowFilter.js +56 -39
  55. package/dist/rpcConnections.js +8 -8
  56. package/dist/rpcServer.js +522 -449
  57. package/dist/rpcWorkloadAdmission.js +50 -0
  58. package/dist/rywPositionStore.js +82 -47
  59. package/dist/scheduler.js +6 -1
  60. package/dist/servePipeline.js +215 -146
  61. package/dist/setReadScopes.js +28 -0
  62. package/dist/snapshotValueCodec.js +46 -0
  63. package/dist/sourceResolution.js +34 -24
  64. package/dist/storeMiddleware.js +211 -209
  65. package/dist/subscriber.js +1 -0
  66. package/dist/tenantScope.js +8 -20
  67. package/dist/traceparent.js +8 -19
  68. package/dist/webSocketGatewayRuntime.js +111 -71
  69. package/dist/workflowRuntime.js +9 -9
  70. package/package.json +19 -7
@@ -1,70 +1,71 @@
1
1
  import { getFieldCipher as e } from "./fieldCipher.js";
2
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, serializeArraysForWrite as l, upsertUpdateValues as u } from "@voltro/database";
4
- import { createLogger as d } from "@voltro/logger";
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
5
  //#region src/bootStoreCodec.ts
6
- var f = d({ scope: "store.fieldEncryption" }), p = (d, m) => {
7
- let h = m, g = process.env.VOLTRO_FIELD_DECRYPT_ON_ERROR === "null" ? "null" : "throw", _ = /* @__PURE__ */ new Set(), v = (e) => {
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
8
  let t = `${e.table}.${e.column}`;
9
- _.has(t) || (_.add(t), f.warn("encrypted column degraded to null — could not decrypt with the active key", {
9
+ v.has(t) || (v.add(t), p.warn("encrypted column degraded to null — could not decrypt with the active key", {
10
10
  table: e.table,
11
11
  column: e.column,
12
12
  reason: e.reason,
13
13
  hint: "VOLTRO_FIELD_DECRYPT_ON_ERROR=null is set — this row was likely encrypted under a different key"
14
14
  }));
15
- }, y = (t, n, r) => {
16
- let a = s(t), o = h ? l(n, a, h) : n;
15
+ }, b = (t, n, r) => {
16
+ let a = s(t), o = g ? u(n, a, g) : n;
17
17
  return i(o, a, e(), r);
18
- }, b = (t, i, a) => {
18
+ }, x = (t, i, a) => {
19
19
  let o = s(t), c = n(i, o, e(), {
20
- onError: g,
21
- ...g === "null" ? { warn: v } : {},
20
+ onError: _,
21
+ ..._ === "null" ? { warn: y } : {},
22
22
  ...a === void 0 ? {} : { rowId: a }
23
23
  });
24
- return h ? r(c, o, h) : c;
25
- }, x = (e, t, n) => t === null ? null : b(e, [t], n)[0];
24
+ return g ? r(c, o, g) : c;
25
+ }, S = (e, t, n) => t === null ? null : x(e, [t], n)[0];
26
26
  return {
27
- delete: (e, t) => d.delete(e, t),
28
- deleteMany: (e, t) => d.deleteMany(e, t),
29
- onChange: (e) => d.onChange(e),
27
+ delete: (e, t) => f.delete(e, t),
28
+ deleteMany: (e, t) => f.deleteMany(e, t),
29
+ onChange: (e) => f.onChange(e),
30
30
  injectExternalChange: (e) => {
31
- d.injectExternalChange(e);
31
+ f.injectExternalChange(e);
32
32
  },
33
- ...d.changeScope === void 0 ? {} : { changeScope: d.changeScope },
34
- ...d.withNamespace === void 0 ? {} : { withNamespace: (e) => p(d.withNamespace(e), m) },
35
- ...d.raw === void 0 ? {} : { raw: ((...e) => d.raw(...e)) },
36
- ...d.close === void 0 ? {} : { close: (e) => d.close(e) },
37
- ...d.ping === void 0 ? {} : { ping: () => d.ping() },
38
- query: async (e) => b(e.table, await d.query(e)),
39
- insert: async (e, t) => x(e, await d.insert(e, y(e, t))),
40
- insertMany: async (t, n) => {
41
- let r = s(t), i = h ? n.map((e) => l(e, r, h)) : n, o = a(i, r, e());
42
- return b(t, await d.insertMany(t, o));
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));
43
44
  },
44
- update: async (e, t, n) => x(e, await d.update(e, t, y(e, n, t)), t),
45
+ update: async (e, t, n) => S(e, await f.update(e, t, (l(n, s(e), t), b(e, n, t))), t),
45
46
  updateMany: async (e, t, n) => {
46
47
  let r = s(e), i = (r === void 0 ? [] : o(r)).filter((e) => t[e] !== void 0 && t[e] !== null);
47
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).`);
48
- return d.updateMany(e, y(e, t), n);
49
+ return f.updateMany(e, b(e, t), n);
49
50
  },
50
51
  upsert: async (e, n, r) => {
51
- let i = u(r), a = await t(d, e, n, r.conflictColumns);
52
- return x(e, await d.upsert(e, y(e, n, a), {
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), {
53
54
  ...r,
54
- ...r.updateValues === void 0 ? {} : { updateValues: y(e, i, a) }
55
+ ...r.updateValues === void 0 ? {} : { updateValues: b(e, i, a) }
55
56
  }), a);
56
57
  },
57
- insertIgnore: async (e, t, n) => x(e, await d.insertIgnore(e, y(e, t), n)),
58
+ insertIgnore: async (e, t, n) => S(e, await f.insertIgnore(e, b(e, t), n)),
58
59
  insertIgnoreWithOutcome: async (e, t, n) => {
59
- let r = await d.insertIgnoreWithOutcome(e, y(e, t), n);
60
+ let r = await f.insertIgnoreWithOutcome(e, b(e, t), n);
60
61
  return {
61
- row: x(e, r.row),
62
+ row: S(e, r.row),
62
63
  outcome: r.outcome
63
64
  };
64
65
  },
65
- patchJson: async (e, t, n, r, i) => x(e, await d.patchJson(e, t, n, r, y(e, c(t, n, i), t)), t),
66
- transactional: (e) => d.transactional((t) => e(p(t, m)))
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)))
67
68
  };
68
69
  };
69
70
  //#endregion
70
- export { p as wrapStoreWithBootCodec };
71
+ export { m as wrapStoreWithBootCodec };
@@ -1,72 +1 @@
1
- import { Cause as e, Clock as t, Duration as n, Effect as r, Ref as i, Schema as a } from "effect";
2
- //#region src/circuitBreaker.ts
3
- var o = class extends a.TaggedError()("CircuitOpen", {
4
- name: a.String,
5
- retryAfterMs: a.Number
6
- }) {}, s = {
7
- tag: "closed",
8
- failureCount: 0,
9
- halfOpenSuccesses: 0,
10
- openedAt: 0
11
- }, c = { make: (a) => r.gen(function* () {
12
- let c = a.name ?? "circuit", l = n.toMillis(n.fromInputUnsafe(a.resetTimeout)), u = a.halfOpenSuccessThreshold ?? 1, d = a.isFailure ?? (() => !0), f = yield* i.make(s), p = r.gen(function* () {
13
- let e = yield* i.get(f);
14
- if (e.tag === "half-open") {
15
- let t = e.halfOpenSuccesses + 1;
16
- yield* i.set(f, t >= u ? s : {
17
- ...e,
18
- halfOpenSuccesses: t
19
- });
20
- } else e.failureCount !== 0 && (yield* i.set(f, s));
21
- }), m = r.gen(function* () {
22
- let e = yield* t.currentTimeMillis, n = yield* i.get(f);
23
- if (n.tag === "half-open" || n.tag === "open") {
24
- yield* i.set(f, {
25
- tag: "open",
26
- failureCount: a.failureThreshold,
27
- halfOpenSuccesses: 0,
28
- openedAt: e
29
- });
30
- return;
31
- }
32
- let r = n.failureCount + 1;
33
- yield* i.set(f, r >= a.failureThreshold ? {
34
- tag: "open",
35
- failureCount: r,
36
- halfOpenSuccesses: 0,
37
- openedAt: e
38
- } : {
39
- ...n,
40
- failureCount: r
41
- });
42
- }), h = r.gen(function* () {
43
- let e = yield* i.get(f);
44
- if (e.tag !== "open") return null;
45
- let n = (yield* t.currentTimeMillis) - e.openedAt;
46
- return n >= l ? (yield* i.set(f, {
47
- tag: "half-open",
48
- failureCount: 0,
49
- halfOpenSuccesses: 0,
50
- openedAt: e.openedAt
51
- }), null) : l - n;
52
- }), g = (t) => r.gen(function* () {
53
- let n = yield* h;
54
- return n === null ? yield* t.pipe(r.tap(() => p), r.tapCause((t) => {
55
- if (e.hasInterruptsOnly(t)) return r.void;
56
- let n = e.findErrorOption(t), i = n._tag === "Some" ? n.value : void 0;
57
- return d(i) ? m : r.void;
58
- })) : yield* r.fail(new o({
59
- name: c,
60
- retryAfterMs: n
61
- }));
62
- }), _ = ((e) => g(e));
63
- return Object.defineProperty(_, "state", {
64
- value: i.get(f).pipe(r.map((e) => e.tag)),
65
- enumerable: !0
66
- }), Object.defineProperty(_, "reset", {
67
- value: i.set(f, s),
68
- enumerable: !0
69
- }), _;
70
- }) };
71
- //#endregion
72
- export { c as CircuitBreaker, o as CircuitOpen };
1
+ export { CircuitBreaker, CircuitOpen } from "@voltro/shared";
@@ -86,22 +86,43 @@ var n = "noop", r = () => {
86
86
  ...d === void 0 ? {} : { mirror: d }
87
87
  };
88
88
  }, a = (e) => {
89
- let n = e.filter((e) => e.dispose !== void 0);
90
- return {
89
+ let n = [...e], r, a, o = () => t.suspend(() => t.forEach([...r ?? []].reverse(), (e) => e.released ? t.void : (e.released = !0, e.spec.dispose === void 0 ? t.void : t.suspend(() => e.spec.dispose(e.sink)).pipe(t.catchCause(() => t.logWarning("analytics member teardown failed")))), {
90
+ concurrency: 1,
91
+ discard: !0
92
+ })), s = () => t.promise(() => a ??= t.runPromise(o())), c = (e) => {
93
+ if (r !== void 0) throw Error("An analytics composite belongs to one app owner");
94
+ r = [];
95
+ for (let t of n) {
96
+ if (t.sql === "required" && e === void 0) throw Error("Analytics member requires SQL");
97
+ let n = t.sql === "required" ? t.build(e) : t.build();
98
+ r.push({
99
+ spec: t,
100
+ sink: n,
101
+ released: !1
102
+ });
103
+ }
104
+ return i(r.map((e) => e.sink));
105
+ }, l = {
91
106
  kind: "compose",
92
- build: (t) => i(e.map((e) => e.build(t))),
93
- tables: e.flatMap((e) => e.tables ?? []),
94
- initialize: (n) => t.forEach(e, (e) => e.initialize ? e.initialize(n) : t.void, {
107
+ tables: n.flatMap((e) => e.tables ?? []),
108
+ initialize: () => t.suspend(() => r === void 0 ? t.die(/* @__PURE__ */ Error("Build analytics members before initialization")) : t.forEach(r, (e) => e.spec.initialize === void 0 ? t.void : t.suspend(() => e.spec.initialize(e.sink)), {
95
109
  concurrency: 1,
96
110
  discard: !0
97
- }),
98
- ...n.length > 0 ? { dispose: (e) => t.forEach(n, (t) => t.dispose(e), {
99
- concurrency: 1,
100
- discard: !0
101
- }) } : {}
111
+ }).pipe(t.onError(s))),
112
+ ...n.some((e) => e.dispose !== void 0) ? { dispose: s } : {}
113
+ };
114
+ return n.some((e) => e.sql === "required") ? {
115
+ ...l,
116
+ sql: "required",
117
+ build: (e) => c(e)
118
+ } : {
119
+ ...l,
120
+ sql: "none",
121
+ build: () => c()
102
122
  };
103
123
  }, o = {
104
124
  kind: "noop",
125
+ sql: "none",
105
126
  build: () => r()
106
127
  };
107
128
  //#endregion
@@ -0,0 +1,231 @@
1
+ import { canonicalize as e } from "./fingerprint.js";
2
+ import { currentRawReads as t, runWithRawReadScope as n } from "./rawReadScope.js";
3
+ import { classifyRead as r } from "./readClassification.js";
4
+ import { buildSubjectKey as i } from "./rywPositionStore.js";
5
+ import { runWithRoutingContext as a } from "./replicatedDataStore.js";
6
+ import { Effect as o, Option as s, Schema as c } from "effect";
7
+ import { createHash as l } from "node:crypto";
8
+ import { parseRevalidateMs as u, schemaShape as d, shapeDigest as f } from "@voltro/protocol";
9
+ import { AsyncLocalStorage as p } from "node:async_hooks";
10
+ //#region src/computedQueryCache.ts
11
+ var m = Symbol.for("@voltro/runtime:computed-query-cache-capture"), h = () => {
12
+ let e = globalThis, t = e[m];
13
+ return t === void 0 && (t = {
14
+ storage: new p(),
15
+ installed: !1
16
+ }, e[m] = t), t;
17
+ }, g = () => {
18
+ h().installed = !0;
19
+ }, _ = () => h().installed, v = () => {
20
+ h().installed = !1;
21
+ }, y = (e) => {
22
+ let t = h().storage.getStore();
23
+ if (t === void 0) return;
24
+ let { composed: n, restricting: i } = r(e, !1);
25
+ for (let e of n) t.tables.add(e);
26
+ for (let e of i) t.tables.add(e);
27
+ }, b = Symbol.for("@voltro/runtime:query-dependencies"), x = () => {
28
+ let e = globalThis, t = e[b];
29
+ return t === void 0 && (t = { storage: new p() }, e[b] = t), t;
30
+ }, S = () => {
31
+ x().storage.enterWith(/* @__PURE__ */ new Set());
32
+ }, C = () => [...x().storage.getStore() ?? []], w = (e) => {
33
+ x().storage.getStore()?.add(e), h().storage.getStore()?.tables.add(e);
34
+ }, T = (e) => {
35
+ h().storage.getStore()?.volatile.add(e);
36
+ }, E = (e, t) => h().storage.run(e, t), D = () => ({
37
+ tables: /* @__PURE__ */ new Set(),
38
+ caller: /* @__PURE__ */ new Set(),
39
+ volatile: /* @__PURE__ */ new Set()
40
+ }), O = /* @__PURE__ */ new Set([
41
+ "connections",
42
+ "apis",
43
+ "kv",
44
+ "cache",
45
+ "webhooks",
46
+ "workflows",
47
+ "events",
48
+ "outbox",
49
+ "transaction",
50
+ "storeForTenant"
51
+ ]), k = /* @__PURE__ */ new Set(["access", "connectionState"]), A = (e, t, n) => new Proxy(e, { get(e, r) {
52
+ return typeof r == "string" && !t.has(r) && n.caller.add(`subject.${r}`), Reflect.get(e, r, e);
53
+ } }), j = (e, t, n) => {
54
+ let r = e.request, i = r === void 0 ? void 0 : new Proxy(r, { get(e, r) {
55
+ return r === "subject" ? t : ((r === "clientId" || r === "cookieHeader") && n.caller.add(`request.${r}`), Reflect.get(e, r, e));
56
+ } });
57
+ return new Proxy(e, { get(e, t) {
58
+ if (t === "request" && i !== void 0) return i;
59
+ typeof t == "string" && (O.has(t) && n.volatile.add(`ctx.${t}`), k.has(t) && n.caller.add(`ctx.${t}`));
60
+ let r = Reflect.get(e, t, e);
61
+ return typeof r == "function" ? r.bind(e) : r;
62
+ } });
63
+ }, M = /* @__PURE__ */ new WeakMap(), N = (e) => {
64
+ let t = M.get(e);
65
+ return t === void 0 && (t = c.toCodecJson(e), M.set(e, t)), t;
66
+ }, P = /* @__PURE__ */ new WeakMap(), F = (e) => {
67
+ if (e === void 0) return "none";
68
+ let t = P.get(e);
69
+ return t === void 0 && (t = f(d(e.ast)), P.set(e, t)), t;
70
+ }, I = (t) => {
71
+ let n = t.input === void 0 ? s.some(t.decodedInput) : c.encodeUnknownOption(N(t.input))(t.decodedInput);
72
+ if (s.isNone(n)) return { bypass: "input-not-encodable" };
73
+ let r;
74
+ if (t.scope === "global") r = "g";
75
+ else if (t.scope === "tenant") {
76
+ if (t.subject.tenantId == null) return { bypass: "no-tenant" };
77
+ r = `t:${t.subject.tenantId}`;
78
+ } else {
79
+ if (t.subject.type === "anonymous" || t.subject.id == null || t.subject.id === "") return { bypass: "anonymous-subject" };
80
+ r = `s:${t.subject.id}`;
81
+ }
82
+ let i = t.rowFilter === void 0 ? [] : [...t.declaredSources].sort().map((n) => [n, e(t.rowFilter(n) ?? null)]), a;
83
+ try {
84
+ a = JSON.stringify([
85
+ t.codeIdentity,
86
+ F(t.output),
87
+ e(n.value),
88
+ i
89
+ ]);
90
+ } catch {
91
+ return { bypass: "input-not-encodable" };
92
+ }
93
+ let o = l("sha256").update(a).digest("base64url");
94
+ return { key: `cq:${t.name}:${o}:${r}` };
95
+ }, L = (e) => {
96
+ if (e.aborted) return { refused: {
97
+ reason: "aborted",
98
+ detail: []
99
+ } };
100
+ if (e.cacheScope !== "subject" && e.scope.caller.size > 0) return { refused: {
101
+ reason: "caller",
102
+ detail: [...e.scope.caller].sort()
103
+ } };
104
+ if (!e.volatileAllowed && e.scope.volatile.size > 0) return { refused: {
105
+ reason: "volatile",
106
+ detail: [...e.scope.volatile].sort()
107
+ } };
108
+ let t = e.rawReads.filter((e) => e.dependsOn.length === 0).map((e) => e.sql);
109
+ if (t.length > 0) return { refused: {
110
+ reason: "undeclared-raw-read",
111
+ detail: t
112
+ } };
113
+ let n = new Set(e.declaredSources);
114
+ for (let t of e.scope.tables) n.add(t);
115
+ for (let t of e.rawReads) for (let e of t.dependsOn) n.add(e);
116
+ if (e.rowFilter !== void 0) {
117
+ let t = new Set(e.declaredSources), r = [...n].filter((n) => !t.has(n) && e.rowFilter(n) !== void 0).sort();
118
+ if (r.length > 0) return { refused: {
119
+ reason: "row-filter-outside-source",
120
+ detail: r
121
+ } };
122
+ }
123
+ let r = e.output === void 0 ? s.some(e.value) : c.encodeUnknownOption(N(e.output))(e.value);
124
+ if (s.isNone(r)) return { refused: {
125
+ reason: "output-not-encodable",
126
+ detail: []
127
+ } };
128
+ let i;
129
+ try {
130
+ i = Buffer.byteLength(JSON.stringify(r.value) ?? "", "utf8");
131
+ } catch {
132
+ return { refused: {
133
+ reason: "output-not-encodable",
134
+ detail: []
135
+ } };
136
+ }
137
+ return i > e.maxEntryBytes ? { refused: {
138
+ reason: "oversized",
139
+ detail: [`${i} bytes`]
140
+ } } : { store: {
141
+ stored: r.value,
142
+ tags: [...n].sort()
143
+ } };
144
+ }, R = (e, t) => e === void 0 ? s.some(t) : c.decodeUnknownOption(N(e))(t), z = (e, t) => {
145
+ let n = t.detail.join(", ");
146
+ switch (t.reason) {
147
+ case "caller": return `${e}: \`cache:\` result NOT kept — the handler read ${n}, which its cache scope does not key on, so the value is specific to this caller. Use \`scope: 'subject'\`, or derive the result without reading the caller (declare access in \`guards:\`; a membership the handler READS through ctx.store is fine — it becomes a dependency).`;
148
+ case "volatile": return `${e}: \`cache:\` result NOT kept — the handler used ${n}, which no table write invalidates. Declare \`cache: { …, volatile: true }\` to accept the ttl as the bound on that staleness, or drop \`cache:\`.`;
149
+ case "undeclared-raw-read": return `${e}: \`cache:\` result NOT kept — a raw read declares no tables, so no write could invalidate it: ${n}. Declare them: ctx.store.raw(fragment, { dependsOn: ['…'] }).`;
150
+ case "row-filter-outside-source": return `${e}: \`cache:\` result NOT kept — the registered row filter restricts ${n}, which the query reads but does not declare in \`source:\`. Add it to \`source:\` so the entry is keyed by what this caller may see there.`;
151
+ case "output-not-encodable": return `${e}: \`cache:\` result NOT kept — the value does not encode through the query's \`output\` schema.`;
152
+ case "oversized": return `${e}: \`cache:\` result NOT kept — ${n} exceeds the cache's maxEntryBytes.`;
153
+ case "aborted": return `${e}: \`cache:\` result NOT kept — the subscription closed while it was computed.`;
154
+ }
155
+ }, B = (e) => {
156
+ let r = o.suspend(() => {
157
+ let r = D(), s = new Set(e.policy.scope === "tenant" ? ["tenantId"] : []), c = e.policy.scope === "subject" ? e.subject : A(e.subject, s, r), l = j(e.ctx, c, r), u = [], d = e.subject.id ?? "anonymous", f = {
158
+ subjectKey: i(d, void 0),
159
+ forcePrimary: !0
160
+ };
161
+ return o.tryPromise({
162
+ try: () => E(r, () => a(f, () => n(async () => {
163
+ let n = await e.execute(l, c);
164
+ return u = t(), n;
165
+ }))),
166
+ catch: (e) => e
167
+ }).pipe(o.map((t) => {
168
+ let n = new Set(e.sources), i = (e) => [...new Set(e)].filter((e) => !n.has(e)).sort();
169
+ if (e.isDescriptor(t)) return e.onDescriptor(), {
170
+ value: {
171
+ value: t,
172
+ dependencies: []
173
+ },
174
+ notApplicable: !0
175
+ };
176
+ let a = L({
177
+ scope: r,
178
+ cacheScope: e.policy.scope,
179
+ volatileAllowed: e.policy.volatile,
180
+ rawReads: u,
181
+ declaredSources: e.sources,
182
+ rowFilter: e.rowFilter,
183
+ output: e.output,
184
+ value: e.unwrap(t),
185
+ maxEntryBytes: e.cache.maxEntryBytes,
186
+ aborted: e.signal?.aborted === !0
187
+ });
188
+ if ("refused" in a) return a.refused.reason !== "aborted" && e.cache.notice(e.name, a.refused.reason, z(e.name, a.refused)), { value: {
189
+ value: t,
190
+ dependencies: i(r.tables)
191
+ } };
192
+ let o = i(a.store.tags);
193
+ return {
194
+ value: {
195
+ value: t,
196
+ dependencies: o
197
+ },
198
+ store: {
199
+ stored: {
200
+ v: a.store.stored,
201
+ d: o
202
+ },
203
+ tags: a.store.tags
204
+ }
205
+ };
206
+ }));
207
+ });
208
+ return e.cache.wrapDynamic(e.key, {
209
+ ttlMs: e.policy.ttlMs,
210
+ ...e.policy.swrMs === void 0 ? {} : { swrMs: e.policy.swrMs },
211
+ ...e.cache.leaseMs === void 0 ? {} : { leaseMs: e.cache.leaseMs },
212
+ label: e.name
213
+ }, r, (t) => o.succeed(V(e.output, t))).pipe(o.map(({ value: e }) => e));
214
+ }, V = (e, t) => {
215
+ if (typeof t != "object" || !t || !("v" in t) || !("d" in t)) return s.none();
216
+ let n = t;
217
+ return !Array.isArray(n.d) || !n.d.every((e) => typeof e == "string") ? s.none() : s.map(R(e, n.v), (e) => ({
218
+ value: e,
219
+ dependencies: n.d
220
+ }));
221
+ }, H = (e) => {
222
+ let t = u(e.ttl);
223
+ if (t !== null) return {
224
+ ttlMs: t,
225
+ swrMs: e.swr === void 0 ? void 0 : u(e.swr) ?? void 0,
226
+ scope: e.scope,
227
+ volatile: e.volatile === !0
228
+ };
229
+ };
230
+ //#endregion
231
+ export { B as cachedComputedCall, I as computedCacheKey, _ as computedQueryCacheCaptureInstalled, C as currentQueryDependencies, L as decideComputedStore, R as decodeComputedStored, z as describeComputedRefusal, S as enterQueryDependencies, g as installComputedQueryCacheCapture, D as newComputedCaptureScope, y as noteCacheRead, T as noteCacheVolatile, w as noteDependency, j as observedContext, A as observedSubject, H as queryCachePolicy, v as resetComputedQueryCacheCaptureForTests, E as runWithComputedCapture };
@@ -1,42 +1,44 @@
1
- import { ConnectionNotConnected as e, listConnectionStates as t, resolveConnection as n } from "./connectionVault.js";
1
+ import { noteCacheVolatile as e } from "./computedQueryCache.js";
2
+ import { ConnectionNotConnected as t, listConnectionStates as n, resolveConnection as r } from "./connectionVault.js";
2
3
  //#region src/connectionsFacade.ts
3
- var r, i = (e) => {
4
- r = e;
5
- }, a = () => r, o = (e, t) => {
6
- let n = r;
4
+ var i, a = (e) => {
5
+ i = e;
6
+ }, o = () => i, s = (e, t) => {
7
+ let n = i;
7
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);
8
- }, s = (t) => async (r, i) => {
9
- let a = t.registry.get(i);
10
- if (a === void 0) throw Error(`connection "${i}" is not declared. Declared: ${t.registry.list().map((e) => e.id).join(", ") || "(none)"}.`);
11
- let o = r.id ?? null;
12
- if (o === null || o === "") throw new e(i, "<anonymous>");
13
- return n({
14
- store: t.store,
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,
15
16
  definition: a,
16
17
  subjectId: o
17
18
  });
18
- }, c = (r) => {
19
- let i = r.subject.id ?? null, a = async (t) => {
20
- let a = r.registry.get(t);
21
- if (a === void 0) throw Error(`ctx.connections.get("${t}"): no such connection is declared. Declared: ${r.registry.list().map((e) => e.id).join(", ") || "(none)"}.`);
22
- if (i === null || i === "") throw new e(t, "<anonymous>");
23
- return n({
24
- store: r.store,
25
- definition: a,
26
- subjectId: i
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
27
29
  });
28
30
  };
29
31
  return {
30
- get: a,
31
- tryGet: async (t) => {
32
+ get: o,
33
+ tryGet: async (e) => {
32
34
  try {
33
- return await a(t);
34
- } catch (t) {
35
- if (t instanceof e) return null;
36
- throw t;
35
+ return await o(e);
36
+ } catch (e) {
37
+ if (e instanceof t) return null;
38
+ throw e;
37
39
  }
38
40
  },
39
- list: async () => i === null || i === "" ? r.registry.list().map((e) => ({
41
+ list: async () => a === null || a === "" ? i.registry.list().map((e) => ({
40
42
  connectionId: e.id,
41
43
  kind: e.kind,
42
44
  label: e.label ?? e.id,
@@ -47,8 +49,8 @@ var r, i = (e) => {
47
49
  expiresAt: null,
48
50
  lastError: null,
49
51
  connectedAt: null
50
- })) : t(r.store, r.registry, i)
52
+ })) : n(i.store, i.registry, a)
51
53
  };
52
54
  };
53
55
  //#endregion
54
- export { a as getConnectionResolver, s as makeConnectionResolver, c as makeConnectionsFacade, o as resolveConnectionForSubject, i as setConnectionResolver };
56
+ export { o as getConnectionResolver, c as makeConnectionResolver, l as makeConnectionsFacade, s as resolveConnectionForSubject, a as setConnectionResolver };