@voltro/testing 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.
package/dist/invoke.js ADDED
@@ -0,0 +1,44 @@
1
+ import { resetAfterCommit as e, restInvocationOf as t, rpcInterceptorFor as n, runAfterCommit as r, runInStoreTransaction as i, serviceLayerFor as a } from "./testContext.js";
2
+ import { Cause as o, Effect as s, Exit as c, Schema as l } from "effect";
3
+ import { SubjectService as u, checkGuardsEffect as d } from "@voltro/protocol";
4
+ import { makeEffectStoreLayer as f, runProvidedEffect as p, runWithDeadlockRetry as m } from "@voltro/runtime";
5
+ //#region src/invoke.ts
6
+ var h = (e, t) => {
7
+ let n = a(t);
8
+ return (n === void 0 ? e : s.provide(e, n)).pipe(s.provide(f(t.store)), s.provideService(u, t.request.subject));
9
+ }, g = async (a, u, f, g) => {
10
+ let _ = a.input, v = await l.decodeUnknownPromise(_)(f), y = a.kind, b = async (e) => {
11
+ let t = u(v, e);
12
+ return s.isEffect(t) ? p(h(t, e)) : t;
13
+ }, x = async () => {
14
+ let t = a.guards;
15
+ if (t !== void 0 && t.length > 0) {
16
+ let e = await s.runPromise(d(g.request.subject, t, v));
17
+ if (e !== null) throw e;
18
+ }
19
+ if (y !== "mutation") return b(g);
20
+ let n = await m(async () => i(g, async (t) => (e(g), b(t))), { delay: () => Promise.resolve() });
21
+ return await r(g), n;
22
+ }, S = y === "mutation" || y === "query" || y === "action" ? y : void 0, C = S === void 0 ? void 0 : n(g, S);
23
+ if (C === void 0 || S === void 0) return await x();
24
+ let w = s.tryPromise({
25
+ try: () => x(),
26
+ catch: (e) => e
27
+ }), T = t(g), E = C(w, {
28
+ tag: a.name ?? "",
29
+ kind: S,
30
+ input: v,
31
+ subject: g.request.subject,
32
+ traceId: g.request.traceId,
33
+ ...g.request.spanId === void 0 ? {} : { spanId: g.request.spanId },
34
+ ...T === void 0 ? {} : {
35
+ surface: "rest",
36
+ publicApi: T.publicApi
37
+ },
38
+ ...T?.remoteAddr === void 0 ? {} : { remoteAddr: T.remoteAddr }
39
+ }), D = await s.runPromiseExit(E);
40
+ if (c.isSuccess(D)) return D.value;
41
+ throw o.squash(D.cause);
42
+ };
43
+ //#endregion
44
+ export { g as invoke };
@@ -0,0 +1,78 @@
1
+ import { Effect as e } from "effect";
2
+ //#region src/mockClock.ts
3
+ var t = Symbol.for("voltro/testing/mockClock.installed"), n = () => globalThis, r = (e, t) => new Proxy(e, {
4
+ construct: (e, n, r) => Reflect.construct(e, n.length === 0 ? [t()] : n, r),
5
+ apply: () => new e(t()).toString(),
6
+ get: (e, n, r) => n === "now" ? () => t() : Reflect.get(e, n, r)
7
+ }), i = (e) => {
8
+ if (typeof e == "number") return e;
9
+ let t = e instanceof Date ? e.getTime() : Date.parse(e);
10
+ if (Number.isNaN(t)) throw Error(`mockClock: cannot read an instant from '${String(e)}'`);
11
+ return t;
12
+ }, a = class {
13
+ currentMs;
14
+ constructor(e = "2026-01-01T00:00:00Z") {
15
+ this.currentMs = i(e);
16
+ }
17
+ now() {
18
+ return this.currentMs;
19
+ }
20
+ date() {
21
+ return new Date(this.currentMs);
22
+ }
23
+ advance(e) {
24
+ this.currentMs += typeof e == "number" ? e : l(e);
25
+ }
26
+ set(e) {
27
+ this.currentMs = i(e);
28
+ }
29
+ get installed() {
30
+ return n()[t]?.clock === this;
31
+ }
32
+ install() {
33
+ let e = n(), i = e[t];
34
+ if (i !== void 0) throw Error("mockClock: global time is already faked" + (i.clock === this ? " by this same clock" : " by another MockClock") + ". Uninstall it before installing again — a nested install restores the outer fake on the way out, which leaves the realm frozen with nothing pointing at why. `withFrozenTime` always restores, including on a throw.");
35
+ let a = globalThis.Date;
36
+ return e[t] = {
37
+ realDate: a,
38
+ clock: this
39
+ }, globalThis.Date = r(a, () => this.currentMs), () => {
40
+ this.uninstall();
41
+ };
42
+ }
43
+ uninstall() {
44
+ let e = n(), r = e[t];
45
+ r !== void 0 && r.clock === this && (globalThis.Date = r.realDate, delete e[t]);
46
+ }
47
+ }, o = (e) => typeof e == "object" && !!e && typeof e.then == "function", s = (e, t) => {
48
+ let n = e instanceof a ? e : new a(e);
49
+ n.install();
50
+ let r = !1;
51
+ try {
52
+ let e = t(n);
53
+ return o(e) ? (r = !0, e.then((e) => (n.uninstall(), e), (e) => {
54
+ throw n.uninstall(), e;
55
+ })) : e;
56
+ } finally {
57
+ r || n.uninstall();
58
+ }
59
+ }, c = (t) => e.acquireRelease(e.sync(() => {
60
+ let e = t instanceof a ? t : new a(t);
61
+ return e.install(), e;
62
+ }), (t) => e.sync(() => {
63
+ t.uninstall();
64
+ })), l = (e) => {
65
+ let t = /^\s*(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)\s*$/.exec(e);
66
+ if (!t) throw Error(`mockClock: cannot parse duration '${e}'`);
67
+ let n = Number(t[1]), r = t[2];
68
+ switch (r) {
69
+ case "ms": return n;
70
+ case "s": return n * 1e3;
71
+ case "m": return n * 6e4;
72
+ case "h": return n * 36e5;
73
+ case "d": return n * 864e5;
74
+ default: throw Error(`mockClock: unknown unit '${r}'`);
75
+ }
76
+ };
77
+ //#endregion
78
+ export { a as MockClock, c as frozenTime, s as withFrozenTime };
@@ -0,0 +1,19 @@
1
+ //#region src/mockLLM.ts
2
+ var e = class {
3
+ queue;
4
+ calls = [];
5
+ constructor(e) {
6
+ this.queue = [...e];
7
+ }
8
+ next(e) {
9
+ this.calls.push(e);
10
+ let t = this.queue.shift();
11
+ if (!t) throw Error("MockLLM: no more responses queued");
12
+ return t;
13
+ }
14
+ remaining() {
15
+ return this.queue.length;
16
+ }
17
+ };
18
+ //#endregion
19
+ export { e as MockLLM };
@@ -0,0 +1,26 @@
1
+ //#region src/mockWebhooks.ts
2
+ var e = class {
3
+ emitted = [];
4
+ async emit(e, t) {
5
+ let n = typeof e == "string" ? e : e.name ?? e.id ?? e.event ?? "(unnamed event)";
6
+ return this.emitted.push({
7
+ event: n,
8
+ payload: t,
9
+ emittedAt: /* @__PURE__ */ new Date()
10
+ }), { delivered: 0 };
11
+ }
12
+ async listTargets(e, t) {
13
+ return [];
14
+ }
15
+ payloadsFor(e) {
16
+ return this.emitted.filter((t) => t.event === e).map((e) => e.payload);
17
+ }
18
+ last(e) {
19
+ for (let t = this.emitted.length - 1; t >= 0; t--) if (this.emitted[t].event === e) return this.emitted[t];
20
+ }
21
+ clear() {
22
+ this.emitted.length = 0;
23
+ }
24
+ };
25
+ //#endregion
26
+ export { e as MockWebhooks };
package/dist/platform.js CHANGED
@@ -10,11 +10,7 @@ var r = (r, i) => {
10
10
  t(Number.isFinite(e.clock.now())).toBe(!0);
11
11
  }), n("storage: the local area is a synchronous store that reads back what it wrote", async () => {
12
12
  let e = await o(), n = e.storage("local");
13
- if (n === null) {
14
- t(e.storage("session")).toBeNull();
15
- return;
16
- }
17
- n.setItem(a, "v1"), t(n.getItem(a)).toBe("v1"), n.removeItem?.(a);
13
+ n === null ? t(e.storage("session")).toBeNull() : (n.setItem(a, "v1"), t(n.getItem(a)).toBe("v1"), n.removeItem?.(a));
18
14
  }), n("secureStorage: honours its declared kind", async () => {
19
15
  let e = await o();
20
16
  e.capabilities.secureTokenStorage ? (t(e.secureStorage.kind).not.toBe("none"), await e.secureStorage.set(a, "secret"), t(await e.secureStorage.get(a)).toBe("secret"), await e.secureStorage.delete(a), t(await e.secureStorage.get(a)).toBeNull()) : (t(e.secureStorage.kind).toBe("none"), t(await e.secureStorage.get(a)).toBeNull(), await t(e.secureStorage.set(a, "secret")).rejects.toThrow());
@@ -0,0 +1,27 @@
1
+ import e from "node:net";
2
+ import { describe as t } from "vitest";
3
+ //#region src/reachable.ts
4
+ var n = (t) => new Promise((n) => {
5
+ let r = new e.Socket(), i = (e) => {
6
+ clearTimeout(a), r.destroy(), n(e);
7
+ }, a = setTimeout(() => i(!1), t.timeoutMs ?? 750);
8
+ r.once("connect", () => i(!0)), r.once("error", () => i(!1)), r.connect(t.port, t.host);
9
+ }), r = async (e, n, r, i) => {
10
+ if (typeof n != "string" || typeof r != "function" || typeof i != "function") throw TypeError(`describeIfAvailable('${e}', ...): expected (label, dependency, probe, suite) — got dependency: ${typeof n}, probe: ${typeof r}, suite: ${typeof i}. Three arguments means the suite body is being used as the probe: it would run at file scope, unconditionally, and the skip would silently do nothing.`);
11
+ let a = !1;
12
+ try {
13
+ a = await r();
14
+ } catch {
15
+ a = !1;
16
+ }
17
+ t.skipIf(!a)(a ? e : `${e} [SKIPPED: needs ${n}]`, i);
18
+ }, i = async (e, t, i) => {
19
+ await r(e, `${t.name} at ${t.host}:${t.port}`, () => n(t), i);
20
+ }, a = async (e, t) => {
21
+ try {
22
+ let n = await t();
23
+ n !== void 0 && n !== "" && console.log(`[engine] ${e}: ${n}`);
24
+ } catch {}
25
+ };
26
+ //#endregion
27
+ export { r as describeIfAvailable, i as describeIfReachable, n as isTcpReachable, a as reportEngineVersion };
@@ -0,0 +1,53 @@
1
+ import { Effect as e, Schema as t } from "effect";
2
+ import { ApiUnavailable as n } from "@voltro/protocol";
3
+ //#region src/serviceApiMock.ts
4
+ var r = (r, i, a = {}) => {
5
+ let o = a.name ?? "mock", s = [], c = /* @__PURE__ */ new Map(), l = (e, t) => `${e}\u0000${JSON.stringify(t)}`, u = async (e, a, c) => {
6
+ let l = r[e];
7
+ if (l === void 0) throw Error(`makeServiceApiMock: '${e}' is not a service procedure of this api`);
8
+ let u = l.input, d = t.encodeUnknownSync(u)(a), f = t.decodeUnknownSync(u)(d);
9
+ s.push({
10
+ tag: e,
11
+ input: f,
12
+ idempotencyKey: c?.idempotencyKey
13
+ });
14
+ let p = i[e];
15
+ if (p === void 0) throw new n({
16
+ api: o,
17
+ tag: e,
18
+ reason: "transport",
19
+ message: "no handler in makeServiceApiMock"
20
+ });
21
+ let m = await p(f), h = l.output;
22
+ return t.decodeUnknownSync(h)(t.encodeUnknownSync(h)(m));
23
+ }, d = (t) => {
24
+ let n = t.join("."), r = Object.assign((e, t) => u(n, e, t), {
25
+ effect: (t, r) => e.tryPromise({
26
+ try: () => u(n, t, r),
27
+ catch: (e) => e
28
+ }),
29
+ subscribe: (e, t) => {
30
+ let r = l(n, e), i = c.get(r) ?? /* @__PURE__ */ new Set();
31
+ return c.set(r, i), i.add(t.onValue), u(n, e).then(t.onValue, (e) => t.onError?.(e)), { unsubscribe: () => {
32
+ i.delete(t.onValue);
33
+ } };
34
+ }
35
+ });
36
+ return new Proxy(r, { get: (e, n) => {
37
+ if (typeof n == "string") {
38
+ if (n === "effect" || n === "subscribe") return e[n];
39
+ if (n !== "then") return d([...t, n]);
40
+ }
41
+ } });
42
+ };
43
+ return {
44
+ api: new Proxy({}, { get: (e, t) => typeof t == "string" && t !== "then" ? d([t]) : void 0 }),
45
+ calls: (e) => e === void 0 ? [...s] : s.filter((t) => t.tag === e),
46
+ emit: (e, n, i) => {
47
+ let a = r[e], o = a === void 0 ? i : t.decodeUnknownSync(a.output)(t.encodeUnknownSync(a.output)(i));
48
+ for (let t of c.get(l(e, n)) ?? []) t(o);
49
+ }
50
+ };
51
+ };
52
+ //#endregion
53
+ export { r as makeServiceApiMock };
@@ -0,0 +1,11 @@
1
+ import { anonymousSubject as e, systemSubject as t } from "@voltro/protocol";
2
+ //#region src/subject.ts
3
+ var n = "test-tenant", r = (e, t, n) => ({
4
+ type: e,
5
+ id: t,
6
+ tenantId: n.tenantId ?? "test-tenant",
7
+ scopes: n.scopes ?? [],
8
+ ...n.metadata === void 0 ? {} : { metadata: n.metadata }
9
+ }), i = (e, t = {}) => r("user", e, t), a = (e, t = {}) => r("apiKey", e, t), o = (e, t = {}) => r("serviceAccount", e, t), s = (t = null) => e(t), c = (e = "job:test", n) => n === void 0 ? t(e) : t(e, n);
10
+ //#endregion
11
+ export { n as TEST_TENANT_ID, s as anonymous, a as apiKey, o as serviceAccount, c as system, i as user };
@@ -0,0 +1,16 @@
1
+ import { changeDelete as e, changeInsert as t, changeSoftDelete as n, changeUpdate as r } from "@voltro/database";
2
+ import { createLogger as i } from "@voltro/logger";
3
+ //#region src/subscribeContext.ts
4
+ var a = (e = {}) => {
5
+ let t = e.id ?? "test", n = e.store ?? new Proxy({}, { get: (e, t) => {
6
+ throw Error(`makeSubscribeContext: this subscriber reached ctx.store.${String(t)} and the test passed no store. Pass \`{ store: makeTestContext().store }\` so the subscriber and the code under test share one — an empty stand-in would let a read of the wrong table pass.`);
7
+ } });
8
+ return {
9
+ id: t,
10
+ log: e.log ?? i({ scope: `subscribe:${t}` }),
11
+ store: n,
12
+ ...e.publish === void 0 ? {} : { publish: e.publish }
13
+ };
14
+ };
15
+ //#endregion
16
+ export { e as changeDelete, t as changeInsert, n as changeSoftDelete, r as changeUpdate, a as makeSubscribeContext };
@@ -0,0 +1,111 @@
1
+ import { apiBindingTokenEnv as e, apiBindingUrlEnv as t, mintServiceToken as n, serviceCallerTokenEnv as r } from "@voltro/runtime";
2
+ import i from "node:net";
3
+ import { randomBytes as a } from "node:crypto";
4
+ import { spawn as o } from "node:child_process";
5
+ import { createRequire as s } from "node:module";
6
+ import { dirname as c, join as l } from "node:path";
7
+ //#region src/testApps.ts
8
+ var u = () => new Promise((e, t) => {
9
+ let n = i.createServer();
10
+ n.once("error", t), n.listen(0, "127.0.0.1", () => {
11
+ let t = n.address(), r = typeof t == "object" && t ? t.port : 0;
12
+ n.close(() => e(r));
13
+ });
14
+ }), d = async (e) => {
15
+ try {
16
+ return (await fetch(`http://127.0.0.1:${e}/internal/readiness`)).status === 200;
17
+ } catch {
18
+ return !1;
19
+ }
20
+ }, f = (e) => l(c(s(l(e, "package.json")).resolve("@voltro/cli/package.json")), "bin", "voltro.mjs"), p = 1e4, m = (e) => new Promise((t) => {
21
+ if (e.exitCode !== null || e.signalCode !== null) return t();
22
+ let n = setTimeout(() => {
23
+ e.kill("SIGKILL");
24
+ }, p);
25
+ e.once("exit", () => {
26
+ clearTimeout(n), t();
27
+ }), e.kill("SIGTERM");
28
+ }), h = async (i) => {
29
+ let s = Object.keys(i.apps);
30
+ if (s.length === 0) throw Error("startTestApps: no apps given");
31
+ let c = i.mode ?? "dev", l = i.bootTimeoutMs ?? 3e5, p = i.cli ?? f(i.apps[s[0]].root), h = /* @__PURE__ */ new Map();
32
+ for (let e of s) h.set(e, await u());
33
+ let g = /* @__PURE__ */ new Map(), _ = new Map(s.map((e) => [e, {}]));
34
+ for (let a of i.edges ?? []) {
35
+ for (let e of [a.caller, a.callee]) if (!s.includes(e)) throw Error(`startTestApps: edge names '${e}', which is not among the apps (${s.join(", ")})`);
36
+ let i = n();
37
+ g.set(`${a.caller}\u0000${a.callee}`, i), Object.assign(_.get(a.caller), {
38
+ [e(a.binding)]: i,
39
+ [t(a.binding)]: `ws://127.0.0.1:${h.get(a.callee)}/ws`
40
+ }), Object.assign(_.get(a.callee), { [r(a.caller)]: i });
41
+ }
42
+ let v = [], y = /* @__PURE__ */ new Set(), b = (e) => {
43
+ if (!v.includes(e)) {
44
+ if (y.has(e)) throw Error(`startTestApps: the edges form a cycle through '${e}' — no app can boot first`);
45
+ y.add(e);
46
+ for (let t of i.edges ?? []) t.caller === e && b(t.callee);
47
+ y.delete(e), v.push(e);
48
+ }
49
+ };
50
+ for (let e of s) b(e);
51
+ let x = [], S = /* @__PURE__ */ new Map(), C = async () => {
52
+ await Promise.all(x.map(m));
53
+ };
54
+ for (let e of v) {
55
+ let t = i.apps[e], n = h.get(e), r = "", s = o(process.execPath, [
56
+ p,
57
+ c,
58
+ ...c === "serve" ? ["."] : []
59
+ ], {
60
+ cwd: t.root,
61
+ env: {
62
+ ...process.env,
63
+ ...c === "serve" ? { VOLTRO_SESSION_SECRET: a(32).toString("base64url") } : {},
64
+ ...t.env,
65
+ ..._.get(e),
66
+ PORT: String(n)
67
+ },
68
+ stdio: [
69
+ "ignore",
70
+ "pipe",
71
+ "pipe"
72
+ ]
73
+ });
74
+ x.push(s), s.stdout?.on("data", (e) => {
75
+ r += e.toString();
76
+ }), s.stderr?.on("data", (e) => {
77
+ r += e.toString();
78
+ }), s.on("exit", (e, t) => {
79
+ r += `\n[exited] code=${String(e)} signal=${String(t)}`;
80
+ });
81
+ let u = {
82
+ name: e,
83
+ port: n,
84
+ url: `http://127.0.0.1:${n}`,
85
+ wsUrl: `ws://127.0.0.1:${n}/ws`,
86
+ log: () => r,
87
+ stop: () => m(s)
88
+ };
89
+ S.set(e, u);
90
+ let f = Date.now() + l;
91
+ for (; !await d(n);) {
92
+ if (s.exitCode !== null || s.signalCode !== null || Date.now() > f) {
93
+ await C();
94
+ let t = s.exitCode !== null || s.signalCode !== null ? "exited before it was ready" : `was not ready within ${l} ms`;
95
+ throw Error(`startTestApps: '${e}' ${t}\n${u.log().slice(-4e3)}`);
96
+ }
97
+ await new Promise((e) => setTimeout(e, 250));
98
+ }
99
+ }
100
+ return {
101
+ apps: Object.fromEntries(S),
102
+ token: (e, t) => {
103
+ let n = g.get(`${e}\u0000${t}`);
104
+ if (n === void 0) throw Error(`startTestApps: no edge ${e} → ${t}`);
105
+ return n;
106
+ },
107
+ stop: C
108
+ };
109
+ };
110
+ //#endregion
111
+ export { h as startTestApps };
@@ -0,0 +1,248 @@
1
+ import { MockClock as e } from "./mockClock.js";
2
+ import { MockWebhooks as t } from "./mockWebhooks.js";
3
+ import { testEventBus as n } from "./testEventBus.js";
4
+ import { MockLLM as r } from "./mockLLM.js";
5
+ import { allRegisteredTables as i, clearRelationsRegistry as a, registerRelations as o } from "@voltro/database";
6
+ import { Layer as s } from "effect";
7
+ import { anonymousSubject as c, composeRpcInterceptors as l, tenantScopedSubject as u } from "@voltro/protocol";
8
+ import { InMemoryDataStore as d, NO_ROW_FILTER as f, appValidation as p, applyRowFilterToDescriptor as m, clearSystemStoreHandle as h, getRowFilter as g, getSystemStoreHandle as _, makeAppAccess as v, makeConnectionState as y, makeDataLoader as b, makeEventPublisher as ee, makeFieldCipher as x, makeOutboxFacade as S, makeSchemaRegistry as C, resolveRowFilterScopeFor as w, resolveTransactionSubject as T, runProvidedEffect as E, setFieldCipher as te, setSystemStoreHandle as D, wrapStoreWithMixinBehaviour as O } from "@voltro/runtime";
9
+ import { installEnvSnapshot as k } from "@voltro/env";
10
+ //#region src/testContext.ts
11
+ var A = (e) => e, j = "00000000000000000000000000000000", M = /* @__PURE__ */ new WeakMap(), N = /* @__PURE__ */ new WeakMap(), P = (e, t) => {
12
+ let n = N.get(e);
13
+ if (n === void 0) throw Error("withRpcConnection requires a context created by makeTestContext");
14
+ return n(t);
15
+ }, F = /* @__PURE__ */ new WeakMap(), I = /* @__PURE__ */ new WeakMap(), L = /* @__PURE__ */ new WeakMap(), R = (e) => L.get(e) ?? [], z = /* @__PURE__ */ new WeakMap(), B = (e, t, n) => {
16
+ z.set(e, {
17
+ publicApi: t,
18
+ ...n === void 0 ? {} : { remoteAddr: n }
19
+ });
20
+ }, V = (e) => z.get(e), H = /* @__PURE__ */ new WeakMap(), U = (e, t) => {
21
+ let n = L.get(e) ?? [], r = t === "mutation" ? "interceptMutation" : t === "query" ? "interceptQuery" : "interceptAction", i = [];
22
+ for (let e of n) {
23
+ let t = e[r];
24
+ typeof t == "function" && i.push(t);
25
+ }
26
+ return l(i);
27
+ }, W = (e) => {
28
+ let t = (L.get(e) ?? []).map((e) => e.services).filter((e) => e !== void 0), n = H.get(e) ?? [], r = [...t, ...n];
29
+ if (r.length !== 0) return s.mergeAll(...r);
30
+ }, G = (e) => {
31
+ let t = F.get(e);
32
+ t !== void 0 && (t.length = 0);
33
+ }, K = async (e) => {
34
+ let t = F.get(e);
35
+ if (t === void 0 || t.length === 0) return;
36
+ let n = [...t];
37
+ t.length = 0;
38
+ for (let e of n) await e();
39
+ }, q = (e) => I.get(e) ?? [], J = async (e, t) => {
40
+ let n = M.get(e);
41
+ return n === void 0 ? e.store.transactional(async (n) => t({
42
+ ...e,
43
+ store: n
44
+ })) : n(t);
45
+ }, Y = (e) => {
46
+ let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map(), r = (n) => {
47
+ let r = t.get(n);
48
+ return r === void 0 ? !1 : r.expiresAt !== null && r.expiresAt <= e() ? (t.delete(n), !1) : !0;
49
+ }, i = (r, i, a, o) => {
50
+ if (t.set(r, {
51
+ value: i,
52
+ expiresAt: a === void 0 ? null : e() + a
53
+ }), o) for (let e of o) {
54
+ let t = n.get(e) ?? /* @__PURE__ */ new Set();
55
+ t.add(r), n.set(e, t);
56
+ }
57
+ };
58
+ return {
59
+ get: async (e) => r(e) ? t.get(e).value : void 0,
60
+ set: async (e, t, n) => {
61
+ i(e, t, n?.ttlMs);
62
+ },
63
+ setWithTags: async (e, t, n) => {
64
+ i(e, t, n.ttlMs, n.tags);
65
+ },
66
+ remove: async (e) => t.delete(e),
67
+ has: async (e) => r(e),
68
+ clear: async () => {
69
+ t.clear(), n.clear();
70
+ },
71
+ invalidateTag: async (e) => {
72
+ let r = n.get(e);
73
+ if (r === void 0) return 0;
74
+ let i = 0;
75
+ for (let e of r) t.delete(e) && i++;
76
+ return n.delete(e), i;
77
+ },
78
+ wrap: async (e, n, a) => {
79
+ if (r(e)) return t.get(e).value;
80
+ let o = await a();
81
+ return i(e, o, n.ttlMs, n.tags), o;
82
+ }
83
+ };
84
+ }, X = (e) => {
85
+ let t = /* @__PURE__ */ new Map(), n = (n) => {
86
+ let r = t.get(n);
87
+ return r === void 0 ? !1 : r.expiresAt !== null && r.expiresAt <= e() ? (t.delete(n), !1) : !0;
88
+ };
89
+ return {
90
+ get: async (e) => n(e) ? t.get(e).value : void 0,
91
+ getOrElse: async (e, r) => n(e) ? t.get(e).value : r(),
92
+ set: async (n, r, i) => {
93
+ t.set(n, {
94
+ value: r,
95
+ expiresAt: i?.ttlMs === void 0 ? null : e() + i.ttlMs
96
+ });
97
+ },
98
+ delete: async (e) => {
99
+ let r = n(e);
100
+ return t.delete(e), r;
101
+ },
102
+ has: async (e) => n(e),
103
+ list: async (e) => {
104
+ let r = [];
105
+ for (let i of t.keys()) n(i) && (e === "" || i.startsWith(e)) && r.push(i);
106
+ return r;
107
+ },
108
+ clear: async () => {
109
+ t.clear();
110
+ }
111
+ };
112
+ }, Z = async (e, t) => {
113
+ let n = _();
114
+ D(e);
115
+ try {
116
+ return await t();
117
+ } finally {
118
+ n === void 0 ? h() : D(n);
119
+ }
120
+ }, Q = /* @__PURE__ */ new Set(), ne = (e) => {
121
+ Q.has(e) || (Q.add(e), console.warn(e));
122
+ }, re = (e, t, n) => {
123
+ let r = ie, i;
124
+ return () => {
125
+ let a = t ?? g();
126
+ return t !== void 0 && g() !== void 0 && t !== g() && ne("[voltro:testing] this test passes `rowFilter:` while a DIFFERENT filter is registered via `setRowFilter`. The option wins, so this test does not exercise your app's registration. Drop `rowFilter:` to run the real process-global one."), (i === void 0 || a !== r) && (r = a, i = Z(n, () => E(w(a, e, (e) => {
127
+ console.error("[voltro:testing] row filter failed to load — reads refused for this subject", e);
128
+ })))), i;
129
+ };
130
+ }, ie = Symbol("unresolved"), $ = (e, t) => new Proxy(e, { get: (e, n) => {
131
+ if (n === "query") return async (n) => e.query(m(await t(), n));
132
+ let r = Reflect.get(e, n, e);
133
+ return typeof r == "function" ? r.bind(e) : r;
134
+ } }), ae = (s = {}) => {
135
+ if (k({
136
+ ...process.env,
137
+ ...s.env ?? {}
138
+ }), s.fieldEncryptionKey !== void 0 && te(x(s.fieldEncryptionKey)), s.relations !== void 0) {
139
+ a();
140
+ for (let e of s.relations) o(e);
141
+ }
142
+ let l = s.tables ?? i(), m = C(l), h = new d(s.store ?? {});
143
+ for (let e of s.plugins ?? []) e.bindDataStore?.(h);
144
+ let g = {
145
+ dataStore: h,
146
+ schemaRegistry: m
147
+ };
148
+ D(g);
149
+ let _ = new e(s.clockStart), w = new t(), E = n(), A = new r(s.llmResponses ?? []), P = Y(() => _.now()), R = X(() => _.now()), z = (e, t, n = h, r, i = !1) => {
150
+ let a = {
151
+ subject: e,
152
+ traceId: j,
153
+ ...t === void 0 ? {} : { connection: t }
154
+ }, o = r?.queued ?? [], c = r?.nudges ?? [], l = ee({
155
+ bus: E.bus,
156
+ tenantId: e.tenantId ?? null,
157
+ ...n === h ? {} : { deferToCommit: (e) => {
158
+ o.push(async () => {
159
+ e();
160
+ });
161
+ } }
162
+ }), d = b({ store: n }), x = i ? async () => f : re(e, s.rowFilter, g), C = {
163
+ apis: s.apis ?? Object.freeze({}),
164
+ transaction: async (r, a) => {
165
+ let o = T(e, a?.tenantId, n !== h);
166
+ if (o !== e) return z(o, t, h, void 0, !0).transaction(r);
167
+ if (n !== h) return r(C);
168
+ let s, l = await n.transactional(async (n) => {
169
+ let a = z(e, t, n, {
170
+ queued: [],
171
+ nudges: []
172
+ }, i), o = await r(a);
173
+ return s = a, o;
174
+ });
175
+ return s !== void 0 && (await K(s), c.push(...q(s))), l;
176
+ },
177
+ clock: _,
178
+ webhooks: w,
179
+ events: l,
180
+ eventBus: E,
181
+ llm: A,
182
+ ...s.ai === void 0 ? {} : { ai: s.ai },
183
+ request: a,
184
+ ...t === void 0 ? {} : { connectionState: y(t, n === h ? void 0 : (e) => {
185
+ o.push(e);
186
+ }) },
187
+ validation: p,
188
+ access: v(e),
189
+ cache: P,
190
+ kv: R,
191
+ store: O($(n, x), {
192
+ subject: e,
193
+ schemaRegistry: m,
194
+ rowFilter: f
195
+ }),
196
+ storeForTenant: (t) => O($(n, x), {
197
+ subject: u(e, t),
198
+ schemaRegistry: m,
199
+ rowFilter: f
200
+ }),
201
+ outbox: S({
202
+ store: n,
203
+ subject: e,
204
+ traceId: j,
205
+ nudge: () => {
206
+ c.push(j);
207
+ },
208
+ afterCommit: (e) => {
209
+ o.push(e);
210
+ }
211
+ }),
212
+ load: d.load,
213
+ loadMany: d.loadMany,
214
+ withSubject: (e, r) => Promise.resolve(r(z(e, t, n, {
215
+ queued: o,
216
+ nudges: c
217
+ }))),
218
+ withTenant: (r, i) => Promise.resolve(i(z({
219
+ ...e,
220
+ tenantId: r
221
+ }, t, n, {
222
+ queued: o,
223
+ nudges: c
224
+ }))),
225
+ asSubject: (e) => z(e, t, n, {
226
+ queued: o,
227
+ nudges: c
228
+ }),
229
+ asTenant: (r) => z({
230
+ ...e,
231
+ tenantId: r
232
+ }, t, n, {
233
+ queued: o,
234
+ nudges: c
235
+ })
236
+ };
237
+ return F.set(C, o), N.set(C, (t) => z(e, t, n, {
238
+ queued: o,
239
+ nudges: c
240
+ })), I.set(C, c), L.set(C, s.plugins ?? []), H.set(C, s.layers ?? []), M.set(C, (r) => n.transactional((n) => r(z(e, t, n, {
241
+ queued: o,
242
+ nudges: c
243
+ })))), C;
244
+ };
245
+ return z(s.subject ?? c(null), s.connection);
246
+ };
247
+ //#endregion
248
+ export { ae as makeTestContext, B as markRestInvocation, A as mockStore, q as outboxNudgesOf, R as pluginsOf, G as resetAfterCommit, V as restInvocationOf, U as rpcInterceptorFor, K as runAfterCommit, J as runInStoreTransaction, W as serviceLayerFor, P as withRpcConnection };