@voltro/testing 0.24.0 → 0.26.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/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { AppContext } from '@voltro/runtime';
2
2
  import { Effect } from 'effect';
3
+ import { EventBus } from '@voltro/runtime';
4
+ import { EventDescriptor } from '@voltro/protocol';
3
5
  import { InspectedStep } from '@voltro/workflow';
4
6
  import { InspectedWorkflow } from '@voltro/workflow';
5
7
  import { Layer } from 'effect';
@@ -283,7 +285,7 @@ export declare const mockStore: (seed: Record<string, ReadonlyArray<Row>>) => Re
283
285
  export declare class MockWebhooks {
284
286
  readonly emitted: EmittedWebhook[];
285
287
  /** Mirrors `WebhooksServiceShape.emit`. Accepts the event id OR a
286
- * `defineOutgoingEvent` descriptor, exactly as the real service does — the
288
+ * declared event, exactly as the real service does — the
287
289
  * handler under test should not have to be written differently to be
288
290
  * testable. */
289
291
  emit<P>(event: string | {
@@ -321,6 +323,13 @@ export declare interface ReachableTarget {
321
323
  readonly timeoutMs?: number;
322
324
  }
323
325
 
326
+ /** One delivery a test subscriber saw. */
327
+ export declare interface RecordedDelivery<P = unknown> {
328
+ readonly payload: P;
329
+ readonly origin: string;
330
+ readonly n: number;
331
+ }
332
+
324
333
  /**
325
334
  * Drop queued post-commit work WITHOUT running it.
326
335
  *
@@ -401,8 +410,79 @@ export declare interface TestContext extends AppContext {
401
410
  withTenant<T>(tenantId: string, fn: (ctx: TestContext) => T | Promise<T>): Promise<T>;
402
411
  }
403
412
 
413
+ export declare interface TestEventBus {
414
+ /**
415
+ * Publish exactly as a handler would — through the real validation, the real
416
+ * size gate and the real serial assignment. A payload that does not match the
417
+ * descriptor fails here, which is the point: a harness that skipped validation
418
+ * would let a test pass on a payload production rejects.
419
+ */
420
+ publish<Name extends string, Key extends Schema.Schema.Any, Payload extends Schema.Schema.Any>(descriptor: EventDescriptor<Name, Key, Payload>, key: Schema.Schema.Type<Key>, payload: Schema.Schema.Type<Payload>): Promise<{
421
+ readonly n: number;
422
+ }>;
423
+ /** Publish and ASSERT it succeeded — the common case, one line. */
424
+ subscribe<Name extends string, Key extends Schema.Schema.Any, Payload extends Schema.Schema.Any>(descriptor: EventDescriptor<Name, Key, Payload>, key: Schema.Schema.Type<Key>, options?: {
425
+ readonly tenantId?: string | null;
426
+ readonly resume?: ReadonlyArray<{
427
+ origin: string;
428
+ n: number;
429
+ }>;
430
+ }): TestSubscriber<Schema.Schema.Type<Payload>>;
431
+ /**
432
+ * Force a gap WITHOUT waiting for a buffer to overflow.
433
+ *
434
+ * A consumer's recovery path (`onMissed` → resync) is the hardest thing to
435
+ * test honestly, because provoking a real loss means racing a queue. This
436
+ * injects a delivery `count` serials ahead, so the next subscriber to resume
437
+ * is owed messages the ring never held — exactly the shape a slow consumer
438
+ * produces, deterministically.
439
+ */
440
+ skipSerials<Name extends string, Key extends Schema.Schema.Any, Payload extends Schema.Schema.Any>(descriptor: EventDescriptor<Name, Key, Payload>, key: Schema.Schema.Type<Key>, count: number, options?: {
441
+ readonly tenantId?: string | null;
442
+ }): void;
443
+ /** The underlying bus, for a test that needs something not exposed here. */
444
+ readonly bus: EventBus;
445
+ /** Drop every subscriber and buffer. */
446
+ readonly reset: () => void;
447
+ }
448
+
449
+ /**
450
+ * A running event bus with no transport.
451
+ *
452
+ * ```ts
453
+ * const events = testEventBus()
454
+ * const display = events.subscribe(gameStarted, { arenaId: 'a1' })
455
+ * await events.publish(gameStarted, { arenaId: 'a1' }, { gameId: 'g1' })
456
+ * expect(display.received).toEqual([{ gameId: 'g1' }])
457
+ * ```
458
+ */
459
+ export declare const testEventBus: (options?: TestEventBusOptions) => TestEventBus;
460
+
461
+ export declare interface TestEventBusOptions {
462
+ /** Default tenant for publishes and subscriptions. `null` = system. */
463
+ readonly tenantId?: string | null;
464
+ /** Ring depth — a small one makes an eviction test deterministic. */
465
+ readonly ringSize?: number;
466
+ readonly origin?: string;
467
+ }
468
+
404
469
  export declare const TESTING_PRESET_VERSION: 1;
405
470
 
471
+ export declare interface TestSubscriber<P = unknown> {
472
+ /** Payloads delivered so far, in order. */
473
+ readonly received: ReadonlyArray<P>;
474
+ /** Full envelopes, when a test cares about serials or origin. */
475
+ readonly deliveries: ReadonlyArray<RecordedDelivery<P>>;
476
+ /** Losses the server could PROVE, with their cause. */
477
+ readonly missed: ReadonlyArray<{
478
+ readonly count: number;
479
+ readonly reason: 'buffer' | 'resume';
480
+ }>;
481
+ /** Total proven losses. */
482
+ readonly missedCount: number;
483
+ readonly stop: () => void;
484
+ }
485
+
406
486
  /** A workflow + its execute function, as the framework pairs them via
407
487
  * `workflow.toLayer(execute)`. Structural (no `@effect/workflow` generic
408
488
  * signature) so callers can pass the value from `workflow({ name, ... })`
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import { AUTO_FILLED_COLUMNS as e, allRegisteredTables as t, clearRelationsRegistry as n, missingRequiredColumns as r, registerRelations as i } from "@voltro/database";
2
2
  import { Cause as a, Effect as o, Exit as s, Layer as c, Schema as l } from "effect";
3
- import { SubjectService as u, anonymousSubject as d, checkGuardsEffect as f, composeRpcInterceptors as p, tenantScopedSubject as m } from "@voltro/protocol";
4
- import { InMemoryDataStore as h, applyRowFilterToDescriptor as g, clearSystemStoreHandle as _, getRowFilter as v, getSystemStoreHandle as y, makeAppAccess as b, makeDataLoader as x, makeEffectStoreLayer as S, makeOutboxFacade as C, makeSchemaRegistry as ee, resolveRowFilterScopeFor as te, runProvidedEffect as w, runWithDeadlockRetry as T, setSystemStoreHandle as E, wrapStoreWithMixinBehaviour as D } from "@voltro/runtime";
5
- import { installEnvSnapshot as O } from "@voltro/env";
6
- import k from "node:net";
7
- import { describe as A } from "vitest";
8
- import { CurrentWorkflowRunId as j, inMemoryWorkflowEngineLayer as M, makeInMemoryRecorder as N } from "@voltro/workflow";
3
+ import { SubjectService as u, anonymousSubject as d, checkGuardsEffect as f, composeRpcInterceptors as p, eventRoute as m, tenantScopedSubject as h } from "@voltro/protocol";
4
+ import { EventBus as g, InMemoryDataStore as _, applyRowFilterToDescriptor as v, clearSystemStoreHandle as y, getRowFilter as b, getSystemStoreHandle as x, makeAppAccess as ee, makeDataLoader as S, makeEffectStoreLayer as C, makeOutboxFacade as w, makeSchemaRegistry as T, publishEvent as te, resolveRowFilterScopeFor as E, runProvidedEffect as D, runWithDeadlockRetry as O, setSystemStoreHandle as k, wrapStoreWithMixinBehaviour as A } from "@voltro/runtime";
5
+ import { installEnvSnapshot as j } from "@voltro/env";
6
+ import M from "node:net";
7
+ import { describe as N } from "vitest";
8
+ import { CurrentWorkflowRunId as ne, inMemoryWorkflowEngineLayer as re, makeInMemoryRecorder as ie } from "@voltro/workflow";
9
9
  //#region src/fixtureRow.ts
10
- var P = new Set(e), F = 0, ne = (e, t) => {
10
+ var ae = new Set(e), P = 0, oe = (e, t) => {
11
11
  if (t.oneOf && t.oneOf.length > 0) return t.oneOf[0];
12
12
  if (t.enumValues && t.enumValues.length > 0) return t.enumValues[0];
13
13
  let n = t.unique === !0;
@@ -15,24 +15,24 @@ var P = new Set(e), F = 0, ne = (e, t) => {
15
15
  case "text":
16
16
  case "reference":
17
17
  case "id":
18
- case "enum": return n ? `${e}-${++F}` : e;
18
+ case "enum": return n ? `${e}-${++P}` : e;
19
19
  case "integer":
20
- case "real": return n ? ++F : 0;
20
+ case "real": return n ? ++P : 0;
21
21
  case "decimal":
22
- case "bigint": return n ? String(++F) : "0";
22
+ case "bigint": return n ? String(++P) : "0";
23
23
  case "boolean": return !1;
24
24
  case "timestamp":
25
25
  case "date": return /* @__PURE__ */ new Date(0);
26
26
  default: throw Error(`fixtureRow: column '${e}' is a required '${t.type}' with no default, and fixtureRow can't synthesize a safe placeholder for that type. Pass it explicitly: fixtureRow(table, { ${e}: … }).`);
27
27
  }
28
- }, re = (e, t = {}) => {
28
+ }, se = (e, t = {}) => {
29
29
  let n = {};
30
- for (let i of r(e, t)) P.has(i) || (n[i] = ne(i, e.fields[i]));
30
+ for (let i of r(e, t)) ae.has(i) || (n[i] = oe(i, e.fields[i]));
31
31
  return {
32
32
  ...n,
33
33
  ...t
34
34
  };
35
- }, I = class {
35
+ }, F = class {
36
36
  currentMs;
37
37
  constructor(e = /* @__PURE__ */ new Date("2026-01-01T00:00:00Z")) {
38
38
  this.currentMs = typeof e == "number" ? e : e.getTime();
@@ -44,9 +44,9 @@ var P = new Set(e), F = 0, ne = (e, t) => {
44
44
  return new Date(this.currentMs);
45
45
  }
46
46
  advance(e) {
47
- this.currentMs += typeof e == "number" ? e : ie(e);
47
+ this.currentMs += typeof e == "number" ? e : I(e);
48
48
  }
49
- }, ie = (e) => {
49
+ }, I = (e) => {
50
50
  let t = /^\s*(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)\s*$/.exec(e);
51
51
  if (!t) throw Error(`mockClock: cannot parse duration '${e}'`);
52
52
  let n = Number(t[1]), r = t[2];
@@ -122,13 +122,13 @@ var P = new Set(e), F = 0, ne = (e, t) => {
122
122
  let n = [...t];
123
123
  t.length = 0;
124
124
  for (let e of n) await e();
125
- }, ae = (e) => W.get(e) ?? [], Y = async (e, t) => {
125
+ }, Y = (e) => W.get(e) ?? [], X = async (e, t) => {
126
126
  let n = H.get(e);
127
127
  return n === void 0 ? e.store.transactional(async (n) => t({
128
128
  ...e,
129
129
  store: n
130
130
  })) : n(t);
131
- }, oe = (e) => {
131
+ }, ce = (e) => {
132
132
  let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map(), r = (n) => {
133
133
  let r = t.get(n);
134
134
  return r === void 0 ? !1 : r.expiresAt !== null && r.expiresAt <= e() ? (t.delete(n), !1) : !0;
@@ -167,7 +167,7 @@ var P = new Set(e), F = 0, ne = (e, t) => {
167
167
  return i(e, o, n.ttlMs, n.tags), o;
168
168
  }
169
169
  };
170
- }, se = (e) => {
170
+ }, le = (e) => {
171
171
  let t = /* @__PURE__ */ new Map(), n = (n) => {
172
172
  let r = t.get(n);
173
173
  return r === void 0 ? !1 : r.expiresAt !== null && r.expiresAt <= e() ? (t.delete(n), !1) : !0;
@@ -195,62 +195,62 @@ var P = new Set(e), F = 0, ne = (e, t) => {
195
195
  t.clear();
196
196
  }
197
197
  };
198
- }, ce = async (e, t) => {
199
- let n = y();
200
- E(e);
198
+ }, ue = async (e, t) => {
199
+ let n = x();
200
+ k(e);
201
201
  try {
202
202
  return await t();
203
203
  } finally {
204
- n === void 0 ? _() : E(n);
204
+ n === void 0 ? y() : k(n);
205
205
  }
206
- }, le = (e, t, n) => {
207
- let r = X, i;
206
+ }, de = (e, t, n) => {
207
+ let r = fe, i;
208
208
  return () => {
209
- let a = t ?? v();
210
- return (i === void 0 || a !== r) && (r = a, i = ce(n, () => w(te(a, e, (e) => {
209
+ let a = t ?? b();
210
+ return (i === void 0 || a !== r) && (r = a, i = ue(n, () => D(E(a, e, (e) => {
211
211
  console.error("[voltro:testing] row filter failed to load — reads refused for this subject", e);
212
212
  })))), i;
213
213
  };
214
- }, X = Symbol("unresolved"), Z = (e, t) => new Proxy(e, { get: (e, n) => {
215
- if (n === "query") return async (n) => e.query(g(await t(), n));
214
+ }, fe = Symbol("unresolved"), Z = (e, t) => new Proxy(e, { get: (e, n) => {
215
+ if (n === "query") return async (n) => e.query(v(await t(), n));
216
216
  let r = Reflect.get(e, n, e);
217
217
  return typeof r == "function" ? r.bind(e) : r;
218
- } }), ue = (e = {}) => {
219
- if (O({
218
+ } }), pe = (e = {}) => {
219
+ if (j({
220
220
  ...process.env,
221
221
  ...e.env ?? {}
222
222
  }), e.relations !== void 0) {
223
223
  n();
224
224
  for (let t of e.relations) i(t);
225
225
  }
226
- let r = ee(e.tables ?? t()), a = new h(e.store ?? {}), o = {
226
+ let r = T(e.tables ?? t()), a = new _(e.store ?? {}), o = {
227
227
  dataStore: a,
228
228
  schemaRegistry: r
229
229
  };
230
- E(o);
231
- let s = new I(e.clockStart), c = new L(), l = new R(), u = new z(e.llmResponses ?? []), f = oe(() => s.now()), p = se(() => s.now()), g = (t, n = a, i) => {
230
+ k(o);
231
+ let s = new F(e.clockStart), c = new L(), l = new R(), u = new z(e.llmResponses ?? []), f = ce(() => s.now()), p = le(() => s.now()), m = (t, n = a, i) => {
232
232
  let d = {
233
233
  subject: t,
234
234
  traceId: V
235
- }, h = i?.queued ?? [], _ = i?.nudges ?? [], v = x({ store: n }), y = le(t, e.rowFilter, o), S = {
235
+ }, g = i?.queued ?? [], _ = i?.nudges ?? [], v = S({ store: n }), y = de(t, e.rowFilter, o), b = {
236
236
  clock: s,
237
237
  email: c,
238
238
  webhooks: l,
239
239
  llm: u,
240
240
  ...e.ai === void 0 ? {} : { ai: e.ai },
241
241
  request: d,
242
- access: b(t),
242
+ access: ee(t),
243
243
  cache: f,
244
244
  kv: p,
245
- store: D(Z(n, y), {
245
+ store: A(Z(n, y), {
246
246
  subject: t,
247
247
  schemaRegistry: r
248
248
  }),
249
- storeForTenant: (e) => D(Z(n, y), {
250
- subject: m(t, e),
249
+ storeForTenant: (e) => A(Z(n, y), {
250
+ subject: h(t, e),
251
251
  schemaRegistry: r
252
252
  }),
253
- outbox: C({
253
+ outbox: w({
254
254
  store: n,
255
255
  subject: t,
256
256
  traceId: V,
@@ -258,33 +258,33 @@ var P = new Set(e), F = 0, ne = (e, t) => {
258
258
  _.push(V);
259
259
  },
260
260
  afterCommit: (e) => {
261
- h.push(e);
261
+ g.push(e);
262
262
  }
263
263
  }),
264
264
  load: v.load,
265
265
  loadMany: v.loadMany,
266
- withSubject: (e, t) => Promise.resolve(t(g(e, n, {
267
- queued: h,
266
+ withSubject: (e, t) => Promise.resolve(t(m(e, n, {
267
+ queued: g,
268
268
  nudges: _
269
269
  }))),
270
- withTenant: (e, r) => Promise.resolve(r(g({
270
+ withTenant: (e, r) => Promise.resolve(r(m({
271
271
  ...t,
272
272
  tenantId: e
273
273
  }, n, {
274
- queued: h,
274
+ queued: g,
275
275
  nudges: _
276
276
  })))
277
277
  };
278
- return U.set(S, h), W.set(S, _), G.set(S, e.plugins ?? []), H.set(S, (e) => n.transactional((n) => e(g(t, n, {
279
- queued: h,
278
+ return U.set(b, g), W.set(b, _), G.set(b, e.plugins ?? []), H.set(b, (e) => n.transactional((n) => e(m(t, n, {
279
+ queued: g,
280
280
  nudges: _
281
- })))), S;
281
+ })))), b;
282
282
  };
283
- return g(e.subject ?? d(null));
284
- }, de = (e, t) => e.pipe(o.provide(S(t.store)), o.provideService(u, t.request.subject)), fe = async (e, t, n, r) => {
283
+ return m(e.subject ?? d(null));
284
+ }, me = (e, t) => e.pipe(o.provide(C(t.store)), o.provideService(u, t.request.subject)), he = async (e, t, n, r) => {
285
285
  let i = e.input, c = await l.decodeUnknownPromise(i)(n), u = e.kind, d = async (e) => {
286
286
  let n = t(c, e);
287
- return o.isEffect(n) ? w(de(n, e)) : n;
287
+ return o.isEffect(n) ? D(me(n, e)) : n;
288
288
  }, p = async () => {
289
289
  let t = e.guards;
290
290
  if (t !== void 0 && t.length > 0) {
@@ -292,7 +292,7 @@ var P = new Set(e), F = 0, ne = (e, t) => {
292
292
  if (e !== null) throw e;
293
293
  }
294
294
  if (u !== "mutation") return d(r);
295
- let n = await T(async () => (q(r), Y(r, async (e) => d(e))), { delay: () => Promise.resolve() });
295
+ let n = await O(async () => (q(r), X(r, async (e) => d(e))), { delay: () => Promise.resolve() });
296
296
  return await J(r), n;
297
297
  }, m = u === "mutation" || u === "query" || u === "action" ? u : void 0, h = m === void 0 ? void 0 : K(r, m);
298
298
  if (h === void 0 || m === void 0) return await p();
@@ -310,7 +310,7 @@ var P = new Set(e), F = 0, ne = (e, t) => {
310
310
  if (s.isSuccess(_)) return _.value;
311
311
  throw a.squash(_.cause);
312
312
  }, Q = (e) => new Promise((t) => {
313
- let n = new k.Socket(), r = (e) => {
313
+ let n = new M.Socket(), r = (e) => {
314
314
  clearTimeout(i), n.destroy(), t(e);
315
315
  }, i = setTimeout(() => r(!1), e.timeoutMs ?? 750);
316
316
  n.once("connect", () => r(!0)), n.once("error", () => r(!1)), n.connect(e.port, e.host);
@@ -321,10 +321,80 @@ var P = new Set(e), F = 0, ne = (e, t) => {
321
321
  } catch {
322
322
  i = !1;
323
323
  }
324
- A.skipIf(!i)(i ? e : `${e} [SKIPPED: needs ${t}]`, r);
325
- }, pe = async (e, t, n) => {
324
+ N.skipIf(!i)(i ? e : `${e} [SKIPPED: needs ${t}]`, r);
325
+ }, ge = async (e, t, n) => {
326
326
  await $(e, `${t.name} at ${t.host}:${t.port}`, () => Q(t), n);
327
- }, me = (e) => {
327
+ }, _e = (e = {}) => {
328
+ let t = e.tenantId ?? null, n = new g({
329
+ origin: e.origin ?? "test",
330
+ ...e.ringSize === void 0 ? {} : { ringSize: e.ringSize }
331
+ });
332
+ return {
333
+ publish: async (e, r, i) => {
334
+ let a = await o.runPromise(o.either(te({
335
+ bus: n,
336
+ tenantId: t
337
+ }, e, r, i)));
338
+ if (a._tag === "Left") {
339
+ let t = a.left;
340
+ throw Error(`testEventBus.publish(${e.name}) was rejected: ${t._tag}`, { cause: t });
341
+ }
342
+ return { n: a.right.n };
343
+ },
344
+ subscribe: (e, r, i) => {
345
+ let a = [], o = [];
346
+ return {
347
+ get received() {
348
+ return a.map((e) => e.payload);
349
+ },
350
+ get deliveries() {
351
+ return a;
352
+ },
353
+ get missed() {
354
+ return o;
355
+ },
356
+ get missedCount() {
357
+ return o.reduce((e, t) => e + t.count, 0);
358
+ },
359
+ stop: n.subscribe({
360
+ tenantId: i?.tenantId ?? t,
361
+ event: e.name,
362
+ key: r,
363
+ listener: (e) => {
364
+ if (e.kind === "gap") {
365
+ o.push({
366
+ count: e.missed,
367
+ reason: e.reason
368
+ });
369
+ return;
370
+ }
371
+ a.push({
372
+ payload: e.envelope.payload,
373
+ origin: e.envelope.origin,
374
+ n: e.envelope.n
375
+ });
376
+ },
377
+ ...i?.resume === void 0 ? {} : { options: { resume: i.resume } }
378
+ })
379
+ };
380
+ },
381
+ skipSerials: (e, r, i, a) => {
382
+ let o = {
383
+ route: m(a?.tenantId ?? t, e.name, r),
384
+ event: e.name,
385
+ origin: "test-gap",
386
+ n: i + 1,
387
+ emittedAt: Date.now(),
388
+ payload: void 0
389
+ };
390
+ n.injectRemote(o);
391
+ },
392
+ bus: n,
393
+ reset: () => {
394
+ n.clear();
395
+ }
396
+ };
397
+ }, ve = (e) => {
328
398
  let t = /* @__PURE__ */ new Map();
329
399
  for (let n of e) {
330
400
  let e = t.get(n.stepName);
@@ -350,13 +420,13 @@ var P = new Set(e), F = 0, ne = (e, t) => {
350
420
  });
351
421
  }
352
422
  return n.sort((e, t) => e._startedAt - t._startedAt), n.map(({ _startedAt: e, ...t }) => t);
353
- }, he = (e) => {
423
+ }, ye = (e) => {
354
424
  let t = e.workflows ?? [], n = /* @__PURE__ */ new Map(), r = 0;
355
425
  return {
356
426
  start: async (i, a) => {
357
427
  let s = t.find((e) => e.workflow.name === i);
358
428
  if (s === void 0) throw Error(`makeWorkflowRunner: no workflow named '${i}' — pass it in makeWorkflowRunner({ ctx, workflows: [{ workflow, execute }] }).`);
359
- let l = `wfrun_test_${++r}`, u = N(), d = s.workflow.toLayer(s.execute).pipe(c.provideMerge(M)), f = /* @__PURE__ */ new Date(), p = s.workflow.execute(a).pipe(o.locally(j, l), o.provide(u.layer), o.provide(d), o.either), m = await o.runPromise(p), h = me(u.readSteps()), g = /* @__PURE__ */ new Date(), _;
429
+ let l = `wfrun_test_${++r}`, u = ie(), d = s.workflow.toLayer(s.execute).pipe(c.provideMerge(re)), f = /* @__PURE__ */ new Date(), p = s.workflow.execute(a).pipe(o.locally(ne, l), o.provide(u.layer), o.provide(d), o.either), m = await o.runPromise(p), h = ve(u.readSteps()), g = /* @__PURE__ */ new Date(), _;
360
430
  if (m._tag === "Right") _ = {
361
431
  status: "succeeded",
362
432
  output: m.right,
@@ -396,6 +466,6 @@ var P = new Set(e), F = 0, ne = (e, t) => {
396
466
  },
397
467
  inspect: async (e) => n.get(e) ?? null
398
468
  };
399
- }, ge = 1;
469
+ }, be = 1;
400
470
  //#endregion
401
- export { I as MockClock, L as MockEmail, z as MockLLM, R as MockWebhooks, ge as TESTING_PRESET_VERSION, $ as describeIfAvailable, pe as describeIfReachable, re as fixtureRow, fe as invoke, Q as isTcpReachable, ue as makeTestContext, he as makeWorkflowRunner, B as mockStore, ae as outboxNudgesOf, q as resetAfterCommit, K as rpcInterceptorFor, J as runAfterCommit, Y as runInStoreTransaction };
471
+ export { F as MockClock, L as MockEmail, z as MockLLM, R as MockWebhooks, be as TESTING_PRESET_VERSION, $ as describeIfAvailable, ge as describeIfReachable, se as fixtureRow, he as invoke, Q as isTcpReachable, pe as makeTestContext, ye as makeWorkflowRunner, B as mockStore, Y as outboxNudgesOf, q as resetAfterCommit, K as rpcInterceptorFor, J as runAfterCommit, X as runInStoreTransaction, _e as testEventBus };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/testing",
3
- "version": "0.24.0",
3
+ "version": "0.26.0",
4
4
  "description": "Test utilities for Voltro apps — deterministic clock, captured emails, queued LLM responses, scoped subject/tenant runners, and a cross-dialect parity harness.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -42,14 +42,14 @@
42
42
  "node": ">=24.0.0"
43
43
  },
44
44
  "dependencies": {
45
- "@voltro/database": "0.24.0",
46
- "@voltro/env": "0.24.0",
47
- "@voltro/protocol": "0.24.0",
48
- "@voltro/runtime": "0.24.0",
49
- "@voltro/workflow": "0.24.0"
45
+ "@voltro/database": "0.26.0",
46
+ "@voltro/env": "0.26.0",
47
+ "@voltro/protocol": "0.26.0",
48
+ "@voltro/runtime": "0.26.0",
49
+ "@voltro/workflow": "0.26.0"
50
50
  },
51
51
  "peerDependencies": {
52
- "@voltro/client": "0.24.0",
52
+ "@voltro/client": "0.26.0",
53
53
  "effect": "^3.22.0",
54
54
  "react": "^19.0.0",
55
55
  "@effect/sql": "^0.52.0"