@rotorsoft/act-tck 1.13.2 → 1.14.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.js CHANGED
@@ -228,6 +228,208 @@ ${content}`;
228
228
  }).join("\n");
229
229
  }
230
230
 
231
+ // src/store-property-tck.ts
232
+ import { fc, test } from "@fast-check/vitest";
233
+ import { afterAll, beforeAll, describe as describe4, expect as expect4 } from "vitest";
234
+ var streamArb = fc.constantFrom("s1", "s2", "s3");
235
+ var commitArb = fc.record({
236
+ stream: streamArb,
237
+ count: fc.integer({ min: 1, max: 3 })
238
+ });
239
+ var claimStreamArb = fc.constantFrom("a", "b", "c");
240
+ var opArb = fc.oneof(
241
+ fc.record({ kind: fc.constant("commit"), stream: claimStreamArb }),
242
+ fc.record({ kind: fc.constant("claim") }),
243
+ fc.record({ kind: fc.constant("ack-all") }),
244
+ fc.record({ kind: fc.constant("block-all") })
245
+ );
246
+ var events = (count) => Array.from({ length: count }, () => inc(1));
247
+ var runStorePropertyTck = (options) => {
248
+ const numRuns = options.numRuns ?? 100;
249
+ describe4(`TCK / Store properties / ${options.name}`, () => {
250
+ let store;
251
+ beforeAll(async () => {
252
+ store = await options.factory();
253
+ await store.seed();
254
+ });
255
+ afterAll(async () => {
256
+ await store.dispose();
257
+ });
258
+ const reset2 = async () => {
259
+ await store.drop();
260
+ await store.seed();
261
+ };
262
+ describe4("commit version invariants", () => {
263
+ test.prop([fc.array(commitArb, { minLength: 0, maxLength: 30 })], {
264
+ numRuns
265
+ })(
266
+ "per-stream versions are 0..N-1 in commit order, regardless of interleaving",
267
+ async (commits) => {
268
+ await reset2();
269
+ const expected = /* @__PURE__ */ new Map();
270
+ for (const { stream, count } of commits) {
271
+ const before = expected.get(stream) ?? -1;
272
+ const committed = await store.commit(
273
+ stream,
274
+ events(count),
275
+ make_meta({ stream })
276
+ );
277
+ committed.forEach((e, i) => {
278
+ expect4(e.version).toBe(before + 1 + i);
279
+ });
280
+ expected.set(stream, before + count);
281
+ }
282
+ for (const stream of new Set(commits.map((c) => c.stream))) {
283
+ const seen = await collect(store, { stream, stream_exact: true });
284
+ seen.forEach((e, i) => {
285
+ expect4(e.version).toBe(i);
286
+ });
287
+ }
288
+ }
289
+ );
290
+ test.prop([fc.array(commitArb, { minLength: 1, maxLength: 20 })], {
291
+ numRuns
292
+ })(
293
+ "bad expectedVersion throws and commits no events",
294
+ async (commits) => {
295
+ await reset2();
296
+ for (const { stream: stream2, count } of commits) {
297
+ await store.commit(
298
+ stream2,
299
+ events(count),
300
+ make_meta({ stream: stream2 })
301
+ );
302
+ }
303
+ const stream = commits[0].stream;
304
+ const before = await collect(store, { stream, stream_exact: true });
305
+ await expect4(
306
+ store.commit(
307
+ stream,
308
+ [inc(1)],
309
+ make_meta({ stream }),
310
+ before.length
311
+ )
312
+ ).rejects.toThrow();
313
+ const after = await collect(store, { stream, stream_exact: true });
314
+ expect4(after.length).toBe(before.length);
315
+ }
316
+ );
317
+ });
318
+ describe4("claim/lease lifecycle invariants", () => {
319
+ test.prop([fc.array(opArb, { minLength: 1, maxLength: 30 })], {
320
+ numRuns
321
+ })(
322
+ "no leaks: claims are always acked or blocked, never lost",
323
+ async (ops) => {
324
+ await reset2();
325
+ await store.subscribe([
326
+ { stream: "a" },
327
+ { stream: "b" },
328
+ { stream: "c" }
329
+ ]);
330
+ let totalClaims = 0;
331
+ let totalResolved = 0;
332
+ let pending = [];
333
+ for (const op of ops) {
334
+ if (op.kind === "commit") {
335
+ await store.commit(
336
+ op.stream,
337
+ [inc(1)],
338
+ make_meta({ stream: op.stream })
339
+ );
340
+ } else if (op.kind === "claim") {
341
+ const claimed = await store.claim(5, 5, "worker", 6e4);
342
+ totalClaims += claimed.length;
343
+ pending = [...pending, ...claimed];
344
+ } else if (op.kind === "ack-all") {
345
+ const acked = await store.ack(pending);
346
+ totalResolved += acked.length;
347
+ pending = pending.filter(
348
+ (p) => !acked.find((a) => a.stream === p.stream)
349
+ );
350
+ } else {
351
+ const blocked = await store.block(
352
+ pending.map((p) => ({ ...p, error: "test" }))
353
+ );
354
+ totalResolved += blocked.length;
355
+ pending = pending.filter(
356
+ (p) => !blocked.find((b) => b.stream === p.stream)
357
+ );
358
+ }
359
+ }
360
+ expect4(totalResolved + pending.length).toBe(totalClaims);
361
+ }
362
+ );
363
+ test.prop(
364
+ [
365
+ fc.array(claimStreamArb, { minLength: 1, maxLength: 5 }),
366
+ fc.array(claimStreamArb, { minLength: 0, maxLength: 5 })
367
+ ],
368
+ { numRuns }
369
+ )(
370
+ "ack advances the watermark monotonically per stream",
371
+ async (commitsA, commitsB) => {
372
+ await reset2();
373
+ const all = [.../* @__PURE__ */ new Set([...commitsA, ...commitsB, "a", "b", "c"])];
374
+ await store.subscribe(all.map((stream) => ({ stream })));
375
+ for (const stream of commitsA) {
376
+ await store.commit(
377
+ stream,
378
+ [inc(1)],
379
+ make_meta({ stream })
380
+ );
381
+ }
382
+ const acked1 = await store.ack(
383
+ await store.claim(10, 10, "worker", 6e4)
384
+ );
385
+ const watermark1 = new Map(all.map((stream) => [stream, -1]));
386
+ for (const l of acked1) watermark1.set(l.stream, l.at);
387
+ for (const stream of commitsB) {
388
+ await store.commit(
389
+ stream,
390
+ [inc(1)],
391
+ make_meta({ stream })
392
+ );
393
+ }
394
+ const acked2 = await store.ack(
395
+ await store.claim(10, 10, "worker", 6e4)
396
+ );
397
+ for (const lease of acked2) {
398
+ expect4(lease.at).toBeGreaterThanOrEqual(
399
+ watermark1.get(lease.stream)
400
+ );
401
+ }
402
+ }
403
+ );
404
+ test.prop([fc.array(claimStreamArb, { minLength: 1, maxLength: 5 })], {
405
+ numRuns
406
+ })("blocked streams cannot be claimed again", async (commits) => {
407
+ await reset2();
408
+ const streams = [...new Set(commits)];
409
+ await store.subscribe(streams.map((stream) => ({ stream })));
410
+ for (const stream of commits) {
411
+ await store.commit(
412
+ stream,
413
+ [inc(1)],
414
+ make_meta({ stream })
415
+ );
416
+ }
417
+ const claimed = await store.claim(10, 10, "worker", 6e4);
418
+ await store.block(claimed.map((l) => ({ ...l, error: "test" })));
419
+ const blockedSet = new Set(claimed.map((l) => l.stream));
420
+ await store.subscribe([{ stream: "ctrl" }]);
421
+ await store.commit(
422
+ "ctrl",
423
+ [inc(1)],
424
+ make_meta({ stream: "ctrl" })
425
+ );
426
+ const reclaim = await store.claim(10, 10, "worker2", 6e4);
427
+ for (const l of reclaim) expect4(blockedSet.has(l.stream)).toBe(false);
428
+ });
429
+ });
430
+ });
431
+ };
432
+
231
433
  // src/store-tck.ts
232
434
  import {
233
435
  act,
@@ -236,20 +438,20 @@ import {
236
438
  SNAP_EVENT,
237
439
  TOMBSTONE_EVENT
238
440
  } from "@rotorsoft/act";
239
- import { afterAll, beforeAll, describe as describe4, expect as expect4, it as it4 } from "vitest";
441
+ import { afterAll as afterAll2, beforeAll as beforeAll2, describe as describe5, expect as expect5, it as it4 } from "vitest";
240
442
  var runStoreTck = (options) => {
241
- describe4(`TCK / Store / ${options.name}`, () => {
443
+ describe5(`TCK / Store / ${options.name}`, () => {
242
444
  let store;
243
445
  const caps = { ...options.capabilities };
244
- beforeAll(async () => {
446
+ beforeAll2(async () => {
245
447
  store = await options.factory();
246
448
  await store.drop();
247
449
  await store.seed();
248
450
  });
249
- afterAll(async () => {
451
+ afterAll2(async () => {
250
452
  await store.dispose();
251
453
  });
252
- describe4("commit", () => {
454
+ describe5("commit", () => {
253
455
  it4("returns committed events with sequenced ids and versions", async () => {
254
456
  const s = `commit-seq-${uid()}`;
255
457
  const committed = await store.commit(
@@ -257,14 +459,14 @@ var runStoreTck = (options) => {
257
459
  [inc(1), inc(2), dec(3)],
258
460
  make_meta({ stream: s })
259
461
  );
260
- expect4(committed).toHaveLength(3);
261
- expect4(committed[0].version).toBe(0);
262
- expect4(committed[1].version).toBe(1);
263
- expect4(committed[2].version).toBe(2);
264
- expect4(committed[0].name).toBe("Incremented");
265
- expect4(committed[2].data).toEqual({ amount: 3 });
462
+ expect5(committed).toHaveLength(3);
463
+ expect5(committed[0].version).toBe(0);
464
+ expect5(committed[1].version).toBe(1);
465
+ expect5(committed[2].version).toBe(2);
466
+ expect5(committed[0].name).toBe("Incremented");
467
+ expect5(committed[2].data).toEqual({ amount: 3 });
266
468
  for (let i = 1; i < committed.length; i++) {
267
- expect4(committed[i].id).toBeGreaterThan(committed[i - 1].id);
469
+ expect5(committed[i].id).toBeGreaterThan(committed[i - 1].id);
268
470
  }
269
471
  });
270
472
  it4("attaches correlation and stream metadata", async () => {
@@ -275,8 +477,8 @@ var runStoreTck = (options) => {
275
477
  [inc(1)],
276
478
  make_meta({ stream: s, correlation })
277
479
  );
278
- expect4(committed[0].stream).toBe(s);
279
- expect4(committed[0].meta.correlation).toBe(correlation);
480
+ expect5(committed[0].stream).toBe(s);
481
+ expect5(committed[0].meta.correlation).toBe(correlation);
280
482
  });
281
483
  it4("throws ConcurrencyError when expectedVersion is wrong", async () => {
282
484
  const s = `commit-cc-${uid()}`;
@@ -291,7 +493,7 @@ var runStoreTck = (options) => {
291
493
  make_meta({ stream: s }),
292
494
  0
293
495
  );
294
- await expect4(
496
+ await expect5(
295
497
  store.commit(s, [inc(1)], make_meta({ stream: s }), 0)
296
498
  ).rejects.toBeInstanceOf(ConcurrencyError);
297
499
  });
@@ -302,14 +504,14 @@ var runStoreTck = (options) => {
302
504
  [inc(1), inc(2)],
303
505
  make_meta({ stream: s })
304
506
  );
305
- await expect4(
507
+ await expect5(
306
508
  store.commit(s, [inc(3)], make_meta({ stream: s }), 0)
307
509
  ).rejects.toBeInstanceOf(ConcurrencyError);
308
510
  const found = await collect(store, { stream: s, stream_exact: true });
309
- expect4(found).toHaveLength(2);
511
+ expect5(found).toHaveLength(2);
310
512
  });
311
513
  });
312
- describe4("query", () => {
514
+ describe5("query", () => {
313
515
  it4("filters by stream, names, correlation, limit, with_snaps", async () => {
314
516
  const s1 = `q-s1-${uid()}`;
315
517
  const s2 = `q-s2-${uid()}`;
@@ -328,21 +530,21 @@ var runStoreTck = (options) => {
328
530
  stream: s1,
329
531
  stream_exact: true
330
532
  });
331
- expect4(by_stream).toHaveLength(2);
533
+ expect5(by_stream).toHaveLength(2);
332
534
  const by_name = await collect(store, {
333
535
  stream: s2,
334
536
  stream_exact: true,
335
537
  names: ["Reset"]
336
538
  });
337
- expect4(by_name).toHaveLength(1);
338
- expect4(by_name[0].name).toBe("Reset");
539
+ expect5(by_name).toHaveLength(1);
540
+ expect5(by_name[0].name).toBe("Reset");
339
541
  const by_correlation = await collect(store, { correlation: cor });
340
- expect4(by_correlation).toHaveLength(5);
542
+ expect5(by_correlation).toHaveLength(5);
341
543
  const limited = await collect(store, {
342
544
  correlation: cor,
343
545
  limit: 2
344
546
  });
345
- expect4(limited).toHaveLength(2);
547
+ expect5(limited).toHaveLength(2);
346
548
  });
347
549
  it4("supports backward traversal", async () => {
348
550
  const s = `q-back-${uid()}`;
@@ -357,8 +559,8 @@ var runStoreTck = (options) => {
357
559
  stream_exact: true,
358
560
  backward: true
359
561
  });
360
- expect4(forward.map((e) => e.id)).toEqual(committed.map((c) => c.id));
361
- expect4(backward.map((e) => e.id)).toEqual(
562
+ expect5(forward.map((e) => e.id)).toEqual(committed.map((c) => c.id));
563
+ expect5(backward.map((e) => e.id)).toEqual(
362
564
  [...committed].reverse().map((c) => c.id)
363
565
  );
364
566
  const latest = await collect(store, {
@@ -367,8 +569,8 @@ var runStoreTck = (options) => {
367
569
  backward: true,
368
570
  limit: 1
369
571
  });
370
- expect4(latest).toHaveLength(1);
371
- expect4(latest[0].id).toBe(committed.at(-1).id);
572
+ expect5(latest).toHaveLength(1);
573
+ expect5(latest[0].id).toBe(committed.at(-1).id);
372
574
  });
373
575
  it4("after/before bound the id range", async () => {
374
576
  const s = `q-bounds-${uid()}`;
@@ -382,7 +584,7 @@ var runStoreTck = (options) => {
382
584
  stream_exact: true,
383
585
  after: committed[0].id
384
586
  });
385
- expect4(after_first.map((e) => e.id)).toEqual(
587
+ expect5(after_first.map((e) => e.id)).toEqual(
386
588
  committed.slice(1).map((c) => c.id)
387
589
  );
388
590
  const before_last = await collect(store, {
@@ -390,7 +592,7 @@ var runStoreTck = (options) => {
390
592
  stream_exact: true,
391
593
  before: committed[committed.length - 1].id
392
594
  });
393
- expect4(before_last.map((e) => e.id)).toEqual(
595
+ expect5(before_last.map((e) => e.id)).toEqual(
394
596
  committed.slice(0, -1).map((c) => c.id)
395
597
  );
396
598
  });
@@ -409,13 +611,13 @@ var runStoreTck = (options) => {
409
611
  created_after: before,
410
612
  created_before: future
411
613
  });
412
- expect4(in_window.length).toBe(1);
614
+ expect5(in_window.length).toBe(1);
413
615
  const out_of_window = await collect(store, {
414
616
  stream: s,
415
617
  stream_exact: true,
416
618
  created_after: future
417
619
  });
418
- expect4(out_of_window.length).toBe(0);
620
+ expect5(out_of_window.length).toBe(0);
419
621
  });
420
622
  it4("backward traversal short-circuits at `after` id boundary", async () => {
421
623
  const s = `q-back-after-${uid()}`;
@@ -430,7 +632,7 @@ var runStoreTck = (options) => {
430
632
  backward: true,
431
633
  after: committed[0].id
432
634
  });
433
- expect4(got.map((e) => e.id)).toEqual([
635
+ expect5(got.map((e) => e.id)).toEqual([
434
636
  committed[2].id,
435
637
  committed[1].id
436
638
  ]);
@@ -449,7 +651,7 @@ var runStoreTck = (options) => {
449
651
  backward: true,
450
652
  created_after: future
451
653
  });
452
- expect4(got).toHaveLength(0);
654
+ expect5(got).toHaveLength(0);
453
655
  });
454
656
  it4("backward traversal honors created_before by skipping newer events", async () => {
455
657
  const s = `q-back-ts-${uid()}`;
@@ -461,7 +663,7 @@ var runStoreTck = (options) => {
461
663
  backward: true,
462
664
  created_before: past
463
665
  });
464
- expect4(got).toHaveLength(0);
666
+ expect5(got).toHaveLength(0);
465
667
  });
466
668
  it4("stream_exact disables regex matching", async () => {
467
669
  const tag = uid();
@@ -478,8 +680,8 @@ var runStoreTck = (options) => {
478
680
  make_meta({ stream: b })
479
681
  );
480
682
  const exact = await collect(store, { stream: a, stream_exact: true });
481
- expect4(exact).toHaveLength(1);
482
- expect4(exact[0].data).toEqual({ amount: 1 });
683
+ expect5(exact).toHaveLength(1);
684
+ expect5(exact[0].data).toEqual({ amount: 1 });
483
685
  });
484
686
  it4("plain regex without anchors is a substring match", async () => {
485
687
  const tag = uid();
@@ -496,7 +698,7 @@ var runStoreTck = (options) => {
496
698
  make_meta({ stream: longer })
497
699
  );
498
700
  const got = await collect(store, { stream: `qr-${tag}-inner` });
499
- expect4(got.map((e) => e.stream).sort()).toEqual([inner, longer].sort());
701
+ expect5(got.map((e) => e.stream).sort()).toEqual([inner, longer].sort());
500
702
  });
501
703
  it4("caller-anchored `^name$` matches only the whole string", async () => {
502
704
  const tag = uid();
@@ -513,8 +715,8 @@ var runStoreTck = (options) => {
513
715
  make_meta({ stream: longer })
514
716
  );
515
717
  const got = await collect(store, { stream: `^qr-${tag}-anchor$` });
516
- expect4(got).toHaveLength(1);
517
- expect4(got[0].stream).toBe(inner);
718
+ expect5(got).toHaveLength(1);
719
+ expect5(got[0].stream).toBe(inner);
518
720
  });
519
721
  it4("caller-anchored `^prefix` matches by prefix", async () => {
520
722
  const tag = uid();
@@ -537,16 +739,16 @@ var runStoreTck = (options) => {
537
739
  make_meta({ stream: other })
538
740
  );
539
741
  const got = await collect(store, { stream: `^qr-${tag}-pfx-` });
540
- expect4(got.map((e) => e.stream).sort()).toEqual([a, b].sort());
742
+ expect5(got.map((e) => e.stream).sort()).toEqual([a, b].sort());
541
743
  });
542
744
  });
543
- describe4("subscribe + claim + ack", () => {
745
+ describe5("subscribe + claim + ack", () => {
544
746
  it4("subscribes new streams and is idempotent on repeat", async () => {
545
747
  const s = `sub-${uid()}`;
546
748
  const first = await store.subscribe([{ stream: s }]);
547
- expect4(first.subscribed).toBe(1);
749
+ expect5(first.subscribed).toBe(1);
548
750
  const second = await store.subscribe([{ stream: s }]);
549
- expect4(second.subscribed).toBe(0);
751
+ expect5(second.subscribed).toBe(0);
550
752
  });
551
753
  it4("claims a subscribed stream and ack releases the lease", async () => {
552
754
  const s = `claim-${uid()}`;
@@ -559,8 +761,8 @@ var runStoreTck = (options) => {
559
761
  const by = `worker-${uid()}`;
560
762
  const leased = await store.claim(100, 0, by, 1e4);
561
763
  const mine = leased.find((l) => l.stream === s);
562
- expect4(mine).toBeDefined();
563
- expect4(mine.by).toBe(by);
764
+ expect5(mine).toBeDefined();
765
+ expect5(mine.by).toBe(by);
564
766
  await store.ack([{ ...mine, at: mine.at + 1 }]);
565
767
  });
566
768
  it4("does not double-claim a held lease", async () => {
@@ -574,7 +776,7 @@ var runStoreTck = (options) => {
574
776
  );
575
777
  const leasedA = await store.claim(100, 0, `wA-${uid()}`, 1e5);
576
778
  const targetA = leasedA.find((l) => l.stream === s);
577
- expect4(targetA).toBeDefined();
779
+ expect5(targetA).toBeDefined();
578
780
  await store.subscribe([{ stream: other }]);
579
781
  await store.commit(
580
782
  other,
@@ -582,9 +784,9 @@ var runStoreTck = (options) => {
582
784
  make_meta({ stream: other })
583
785
  );
584
786
  const leasedB = await store.claim(100, 0, `wB-${uid()}`, 1e5);
585
- expect4(leasedB.length).toBeGreaterThan(0);
586
- expect4(leasedB.find((l) => l.stream === s)).toBeUndefined();
587
- expect4(leasedB.find((l) => l.stream === other)).toBeDefined();
787
+ expect5(leasedB.length).toBeGreaterThan(0);
788
+ expect5(leasedB.find((l) => l.stream === s)).toBeUndefined();
789
+ expect5(leasedB.find((l) => l.stream === other)).toBeDefined();
588
790
  });
589
791
  it4("supports dual frontiers (lagging + leading)", async () => {
590
792
  const s = `claim-dual-${uid()}`;
@@ -596,10 +798,10 @@ var runStoreTck = (options) => {
596
798
  );
597
799
  const first = await store.claim(100, 0, `w-${uid()}`, 1);
598
800
  const mine = first.find((l) => l.stream === s);
599
- expect4(mine).toBeDefined();
801
+ expect5(mine).toBeDefined();
600
802
  await store.ack([{ ...mine, at: mine.at + 1 }]);
601
803
  const second = await store.claim(0, 100, `w-${uid()}`, 1);
602
- expect4(second.find((l) => l.stream === s)).toBeDefined();
804
+ expect5(second.find((l) => l.stream === s)).toBeDefined();
603
805
  });
604
806
  it4("dedupes when both frontiers would return the same stream", async () => {
605
807
  const s = `claim-dedup-${uid()}`;
@@ -611,7 +813,7 @@ var runStoreTck = (options) => {
611
813
  );
612
814
  const claimed = await store.claim(100, 100, `w-${uid()}`, 1e5);
613
815
  const matches = claimed.filter((l) => l.stream === s);
614
- expect4(matches).toHaveLength(1);
816
+ expect5(matches).toHaveLength(1);
615
817
  });
616
818
  it4("silently ignores ack from the wrong holder", async () => {
617
819
  const s = `ack-wrong-${uid()}`;
@@ -630,14 +832,14 @@ var runStoreTck = (options) => {
630
832
  const leased = await store.claim(100, 0, `right-${uid()}`, 1e5);
631
833
  const mine = leased.find((l) => l.stream === s);
632
834
  const sibling_lease = leased.find((l) => l.stream === sibling);
633
- expect4(mine).toBeDefined();
634
- expect4(sibling_lease).toBeDefined();
835
+ expect5(mine).toBeDefined();
836
+ expect5(sibling_lease).toBeDefined();
635
837
  const acked = await store.ack([
636
838
  { ...mine, by: "imposter" },
637
839
  sibling_lease
638
840
  ]);
639
- expect4(acked.length).toBeGreaterThan(0);
640
- expect4(acked.find((l) => l.stream === s)).toBeUndefined();
841
+ expect5(acked.length).toBeGreaterThan(0);
842
+ expect5(acked.find((l) => l.stream === s)).toBeUndefined();
641
843
  });
642
844
  it4("ack with a stale (lower) watermark does not throw", async () => {
643
845
  const s = `ack-stale-${uid()}`;
@@ -645,8 +847,8 @@ var runStoreTck = (options) => {
645
847
  const by = `w-${uid()}`;
646
848
  const leased = await store.claim(100, 0, by, 1e5);
647
849
  const mine = leased.find((l) => l.stream === s);
648
- expect4(mine).toBeDefined();
649
- await expect4(
850
+ expect5(mine).toBeDefined();
851
+ await expect5(
650
852
  store.ack([{ ...mine, at: -5 }])
651
853
  ).resolves.toBeDefined();
652
854
  });
@@ -656,13 +858,13 @@ var runStoreTck = (options) => {
656
858
  await fresh.drop();
657
859
  await fresh.seed();
658
860
  const claimed = await fresh.claim(1, 1, `w-${uid()}`, 1e3);
659
- expect4(claimed).toEqual([]);
861
+ expect5(claimed).toEqual([]);
660
862
  } finally {
661
863
  await fresh.dispose();
662
864
  }
663
865
  });
664
866
  });
665
- describe4("lease semantics", () => {
867
+ describe5("lease semantics", () => {
666
868
  it4("returns retry=0 on first claim and increments on re-claim without ack", async () => {
667
869
  const fresh = await options.factory();
668
870
  try {
@@ -677,12 +879,12 @@ var runStoreTck = (options) => {
677
879
  );
678
880
  const first = await fresh.claim(1, 0, `w-${uid()}`, 0);
679
881
  const f = first.find((l) => l.stream === s);
680
- expect4(f).toBeDefined();
681
- expect4(f.retry).toBe(0);
882
+ expect5(f).toBeDefined();
883
+ expect5(f.retry).toBe(0);
682
884
  const second = await fresh.claim(1, 0, `w-${uid()}`, 1e5);
683
885
  const sec = second.find((l) => l.stream === s);
684
- expect4(sec).toBeDefined();
685
- expect4(sec.retry).toBe(1);
886
+ expect5(sec).toBeDefined();
887
+ expect5(sec.retry).toBe(1);
686
888
  } finally {
687
889
  await fresh.dispose();
688
890
  }
@@ -700,15 +902,46 @@ var runStoreTck = (options) => {
700
902
  make_meta({ stream: s })
701
903
  );
702
904
  const lag = await fresh.claim(1, 0, `w-${uid()}`, 0);
703
- expect4(lag.find((l) => l.stream === s)?.lagging).toBe(true);
905
+ expect5(lag.find((l) => l.stream === s)?.lagging).toBe(true);
704
906
  const lead = await fresh.claim(0, 1, `w-${uid()}`, 1e5);
705
- expect4(lead.find((l) => l.stream === s)?.lagging).toBe(false);
907
+ expect5(lead.find((l) => l.stream === s)?.lagging).toBe(false);
908
+ } finally {
909
+ await fresh.dispose();
910
+ }
911
+ });
912
+ });
913
+ describe5.skipIf(!caps.concurrent_claim)("concurrency (capability)", () => {
914
+ it4("never double-leases a stream across concurrent claimers", async () => {
915
+ const fresh = await options.factory();
916
+ try {
917
+ await fresh.drop();
918
+ await fresh.seed();
919
+ const streams = Array.from(
920
+ { length: 8 },
921
+ () => `concurrent-${uid()}`
922
+ );
923
+ await fresh.subscribe(streams.map((stream) => ({ stream })));
924
+ for (const stream of streams) {
925
+ await fresh.commit(
926
+ stream,
927
+ [inc(1)],
928
+ make_meta({ stream })
929
+ );
930
+ }
931
+ const owned = new Set(streams);
932
+ const [a, b] = await Promise.all([
933
+ fresh.claim(100, 100, `wA-${uid()}`, 6e4),
934
+ fresh.claim(100, 100, `wB-${uid()}`, 6e4)
935
+ ]);
936
+ const claimed = [...a, ...b].map((l) => l.stream).filter((stream) => owned.has(stream));
937
+ expect5(new Set(claimed).size).toBe(claimed.length);
938
+ expect5(claimed.length).toBe(owned.size);
706
939
  } finally {
707
940
  await fresh.dispose();
708
941
  }
709
942
  });
710
943
  });
711
- describe4("block", () => {
944
+ describe5("block", () => {
712
945
  it4("hides blocked streams from claim", async () => {
713
946
  const s = `block-${uid()}`;
714
947
  await store.subscribe([{ stream: s }]);
@@ -719,16 +952,16 @@ var runStoreTck = (options) => {
719
952
  );
720
953
  const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
721
954
  const mine = leased.find((l) => l.stream === s);
722
- expect4(mine).toBeDefined();
955
+ expect5(mine).toBeDefined();
723
956
  const others = leased.filter((l) => l.stream !== s);
724
957
  await store.ack(others);
725
958
  const blocked = await store.block([
726
959
  { ...mine, error: "boom" }
727
960
  ]);
728
- expect4(blocked).toHaveLength(1);
729
- expect4(blocked[0].error).toBe("boom");
961
+ expect5(blocked).toHaveLength(1);
962
+ expect5(blocked[0].error).toBe("boom");
730
963
  const again = await store.claim(100, 100, `w2-${uid()}`, 1e5);
731
- expect4(again.find((l) => l.stream === s)).toBeUndefined();
964
+ expect5(again.find((l) => l.stream === s)).toBeUndefined();
732
965
  });
733
966
  it4("rejects block calls from a different holder", async () => {
734
967
  const s = `block-wrong-${uid()}`;
@@ -740,16 +973,16 @@ var runStoreTck = (options) => {
740
973
  );
741
974
  const leased = await store.claim(100, 0, `right-${uid()}`, 1e5);
742
975
  const mine = leased.find((l) => l.stream === s);
743
- expect4(mine).toBeDefined();
976
+ expect5(mine).toBeDefined();
744
977
  const others = leased.filter((l) => l.stream !== s);
745
978
  await store.ack(others);
746
979
  const blocked = await store.block([
747
980
  { ...mine, by: "imposter", error: "no" }
748
981
  ]);
749
- expect4(blocked).toHaveLength(0);
982
+ expect5(blocked).toHaveLength(0);
750
983
  });
751
984
  });
752
- describe4("reset", () => {
985
+ describe5("reset", () => {
753
986
  it4("rewinds a stream watermark to -1", async () => {
754
987
  const s = `reset-${uid()}`;
755
988
  await store.subscribe([{ stream: s }]);
@@ -760,13 +993,13 @@ var runStoreTck = (options) => {
760
993
  );
761
994
  const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
762
995
  const mine = leased.find((l) => l.stream === s);
763
- expect4(mine).toBeDefined();
996
+ expect5(mine).toBeDefined();
764
997
  await store.ack([{ ...mine, at: 99 }]);
765
- expect4(await store.reset([s])).toBe(1);
998
+ expect5(await store.reset([s])).toBe(1);
766
999
  const after = await store.claim(100, 0, `w2-${uid()}`, 1e5);
767
1000
  const back = after.find((l) => l.stream === s);
768
- expect4(back).toBeDefined();
769
- expect4(back.at).toBe(-1);
1001
+ expect5(back).toBeDefined();
1002
+ expect5(back.at).toBe(-1);
770
1003
  });
771
1004
  it4("clears blocked status when resetting", async () => {
772
1005
  const s = `reset-blk-${uid()}`;
@@ -781,16 +1014,16 @@ var runStoreTck = (options) => {
781
1014
  const others = leased.filter((l) => l.stream !== s);
782
1015
  await store.ack(others);
783
1016
  await store.block([{ ...mine, error: "boom" }]);
784
- expect4(await store.reset([s])).toBe(1);
1017
+ expect5(await store.reset([s])).toBe(1);
785
1018
  const after = await store.claim(100, 0, `w2-${uid()}`, 1e5);
786
- expect4(after.find((l) => l.stream === s)).toBeDefined();
1019
+ expect5(after.find((l) => l.stream === s)).toBeDefined();
787
1020
  });
788
1021
  it4("returns 0 for unknown streams and empty input", async () => {
789
- expect4(await store.reset([`missing-${uid()}`])).toBe(0);
790
- expect4(await store.reset([])).toBe(0);
1022
+ expect5(await store.reset([`missing-${uid()}`])).toBe(0);
1023
+ expect5(await store.reset([])).toBe(0);
791
1024
  });
792
1025
  });
793
- describe4("unblock", () => {
1026
+ describe5("unblock", () => {
794
1027
  it4("clears blocked flag and preserves the watermark", async () => {
795
1028
  const s = `unblock-${uid()}`;
796
1029
  await store.subscribe([{ stream: s }]);
@@ -809,7 +1042,7 @@ var runStoreTck = (options) => {
809
1042
  await store.ack([{ ...m1, at: m1.at }]);
810
1043
  const before_block = await store.claim(100, 0, `w-${uid()}`, 1e5);
811
1044
  const m2 = before_block.find((l) => l.stream === s);
812
- expect4(m2).toBeDefined();
1045
+ expect5(m2).toBeDefined();
813
1046
  const watermark_before = m2.at;
814
1047
  await store.block([{ ...m2, error: "permanent" }]);
815
1048
  let blocked_flag;
@@ -819,13 +1052,13 @@ var runStoreTck = (options) => {
819
1052
  },
820
1053
  { stream: s, stream_exact: true, limit: 1 }
821
1054
  );
822
- expect4(blocked_flag).toBe(true);
823
- expect4(await store.unblock([s])).toBe(1);
1055
+ expect5(blocked_flag).toBe(true);
1056
+ expect5(await store.unblock([s])).toBe(1);
824
1057
  const after = await store.claim(100, 0, `w-${uid()}`, 1e5);
825
1058
  const back = after.find((l) => l.stream === s);
826
- expect4(back).toBeDefined();
827
- expect4(back.at).toBe(watermark_before);
828
- expect4(back.retry).toBe(0);
1059
+ expect5(back).toBeDefined();
1060
+ expect5(back.at).toBe(watermark_before);
1061
+ expect5(back.retry).toBe(0);
829
1062
  });
830
1063
  it4("returns 0 when the stream is not blocked", async () => {
831
1064
  const s = `unblock-noop-${uid()}`;
@@ -835,11 +1068,11 @@ var runStoreTck = (options) => {
835
1068
  [inc(1)],
836
1069
  make_meta({ stream: s })
837
1070
  );
838
- expect4(await store.unblock([s])).toBe(0);
1071
+ expect5(await store.unblock([s])).toBe(0);
839
1072
  });
840
1073
  it4("returns 0 for unknown streams and empty input", async () => {
841
- expect4(await store.unblock([`missing-${uid()}`])).toBe(0);
842
- expect4(await store.unblock([])).toBe(0);
1074
+ expect5(await store.unblock([`missing-${uid()}`])).toBe(0);
1075
+ expect5(await store.unblock([])).toBe(0);
843
1076
  });
844
1077
  it4("only counts streams that were actually blocked", async () => {
845
1078
  const s1 = `unblock-mix-a-${uid()}`;
@@ -860,7 +1093,7 @@ var runStoreTck = (options) => {
860
1093
  const others = leased.filter((l) => l.stream !== s1);
861
1094
  await store.ack(others);
862
1095
  await store.block([{ ...m1, error: "boom" }]);
863
- expect4(await store.unblock([s1, s2])).toBe(1);
1096
+ expect5(await store.unblock([s1, s2])).toBe(1);
864
1097
  });
865
1098
  it4("filter form: unblocks by stream pattern", async () => {
866
1099
  const tag = uid();
@@ -894,11 +1127,11 @@ var runStoreTck = (options) => {
894
1127
  const count = await store.unblock({
895
1128
  stream: `^unblock-filter-${tag}-`
896
1129
  });
897
- expect4(count).toBe(2);
1130
+ expect5(count).toBe(2);
898
1131
  const after = await store.claim(100, 0, `w-${uid()}`, 1e5);
899
- expect4(after.find((l) => l.stream === s3)).toBeUndefined();
900
- expect4(after.find((l) => l.stream === s1)).toBeDefined();
901
- expect4(after.find((l) => l.stream === s2)).toBeDefined();
1132
+ expect5(after.find((l) => l.stream === s3)).toBeUndefined();
1133
+ expect5(after.find((l) => l.stream === s1)).toBeDefined();
1134
+ expect5(after.find((l) => l.stream === s2)).toBeDefined();
902
1135
  });
903
1136
  it4("filter form: empty filter unblocks every blocked stream", async () => {
904
1137
  const tag = uid();
@@ -924,7 +1157,7 @@ var runStoreTck = (options) => {
924
1157
  const count = await store.unblock({
925
1158
  stream: `^unblock-empty-${tag}-`
926
1159
  });
927
- expect4(count).toBe(2);
1160
+ expect5(count).toBe(2);
928
1161
  });
929
1162
  it4("filter form: explicit blocked:false matches nothing", async () => {
930
1163
  const tag = uid();
@@ -935,7 +1168,7 @@ var runStoreTck = (options) => {
935
1168
  [inc(1)],
936
1169
  make_meta({ stream: s })
937
1170
  );
938
- expect4(
1171
+ expect5(
939
1172
  await store.unblock({
940
1173
  stream: `^unblock-blocked-false-${tag}`,
941
1174
  blocked: false
@@ -943,7 +1176,7 @@ var runStoreTck = (options) => {
943
1176
  ).toBe(0);
944
1177
  });
945
1178
  });
946
- describe4("reset filter form", () => {
1179
+ describe5("reset filter form", () => {
947
1180
  it4("resets streams matching a stream pattern", async () => {
948
1181
  const tag = uid();
949
1182
  const s1 = `reset-filter-${tag}-a`;
@@ -975,7 +1208,7 @@ var runStoreTck = (options) => {
975
1208
  );
976
1209
  await store.ack(mine.map((l) => ({ ...l, at: l.at + 100 })));
977
1210
  const count = await store.reset({ stream: `^reset-filter-${tag}-` });
978
- expect4(count).toBe(2);
1211
+ expect5(count).toBe(2);
979
1212
  const position_for = async (name) => {
980
1213
  let at = null;
981
1214
  await store.query_streams(
@@ -986,9 +1219,9 @@ var runStoreTck = (options) => {
986
1219
  );
987
1220
  return at;
988
1221
  };
989
- expect4(await position_for(s1)).toBe(-1);
990
- expect4(await position_for(s2)).toBe(-1);
991
- expect4(await position_for(other)).toBeGreaterThan(-1);
1222
+ expect5(await position_for(s1)).toBe(-1);
1223
+ expect5(await position_for(s2)).toBe(-1);
1224
+ expect5(await position_for(other)).toBeGreaterThan(-1);
992
1225
  });
993
1226
  it4("filter form: resets only blocked streams when blocked:true", async () => {
994
1227
  const tag = uid();
@@ -1013,10 +1246,10 @@ var runStoreTck = (options) => {
1013
1246
  stream: `^reset-blocked-${tag}-`,
1014
1247
  blocked: true
1015
1248
  });
1016
- expect4(count).toBe(1);
1249
+ expect5(count).toBe(1);
1017
1250
  });
1018
1251
  });
1019
- describe4("prioritize", () => {
1252
+ describe5("prioritize", () => {
1020
1253
  it4("sets priority directly, overriding subscribe's max() rule", async () => {
1021
1254
  const tag = uid();
1022
1255
  const s1 = `pri-${tag}-a`;
@@ -1029,7 +1262,7 @@ var runStoreTck = (options) => {
1029
1262
  { stream: s1, stream_exact: true },
1030
1263
  3
1031
1264
  );
1032
- expect4(updated).toBe(1);
1265
+ expect5(updated).toBe(1);
1033
1266
  const got1 = {};
1034
1267
  const got2 = {};
1035
1268
  await store.query_streams(
@@ -1039,11 +1272,11 @@ var runStoreTck = (options) => {
1039
1272
  },
1040
1273
  { stream: `pri-${tag}-.*`, limit: 100 }
1041
1274
  );
1042
- expect4(got1.priority).toBe(3);
1043
- expect4(got2.priority).toBe(5);
1275
+ expect5(got1.priority).toBe(3);
1276
+ expect5(got2.priority).toBe(5);
1044
1277
  });
1045
1278
  });
1046
- describe4("lanes", () => {
1279
+ describe5("lanes", () => {
1047
1280
  it4("subscribe defaults lane to 'default' when omitted", async () => {
1048
1281
  const s = `lane-default-${uid()}`;
1049
1282
  await store.subscribe([{ stream: s }]);
@@ -1052,7 +1285,7 @@ var runStoreTck = (options) => {
1052
1285
  stream: s,
1053
1286
  stream_exact: true
1054
1287
  });
1055
- expect4(seen).toEqual(["default"]);
1288
+ expect5(seen).toEqual(["default"]);
1056
1289
  });
1057
1290
  it4("subscribe records the lane passed in", async () => {
1058
1291
  const s = `lane-set-${uid()}`;
@@ -1062,7 +1295,7 @@ var runStoreTck = (options) => {
1062
1295
  stream: s,
1063
1296
  stream_exact: true
1064
1297
  });
1065
- expect4(seen).toEqual(["slow"]);
1298
+ expect5(seen).toEqual(["slow"]);
1066
1299
  });
1067
1300
  it4("subscribe re-lanes existing streams on subsequent calls", async () => {
1068
1301
  const s = `lane-upsert-${uid()}`;
@@ -1073,7 +1306,7 @@ var runStoreTck = (options) => {
1073
1306
  stream: s,
1074
1307
  stream_exact: true
1075
1308
  });
1076
- expect4(seen).toEqual(["fast"]);
1309
+ expect5(seen).toEqual(["fast"]);
1077
1310
  });
1078
1311
  it4("claim() filters by lane when supplied and returns lane on the Lease", async () => {
1079
1312
  const tag = uid();
@@ -1099,13 +1332,13 @@ var runStoreTck = (options) => {
1099
1332
  const slow_mine = slow.filter(
1100
1333
  (l) => l.stream === sub_default || l.stream === sub_slow
1101
1334
  );
1102
- expect4(slow_mine.map((l) => l.stream)).toEqual([sub_slow]);
1103
- expect4(slow_mine[0]?.lane).toBe("slow");
1335
+ expect5(slow_mine.map((l) => l.stream)).toEqual([sub_slow]);
1336
+ expect5(slow_mine[0]?.lane).toBe("slow");
1104
1337
  await store.ack(slow_mine.map((l) => ({ ...l, at: l.at + 1 })));
1105
1338
  const all = await store.claim(50, 0, `w-all-${tag}`, 1e3);
1106
1339
  const all_mine = all.filter((l) => l.stream === sub_default || l.stream === sub_slow).map((l) => ({ stream: l.stream, lane: l.lane }));
1107
- expect4(all_mine).toEqual(
1108
- expect4.arrayContaining([
1340
+ expect5(all_mine).toEqual(
1341
+ expect5.arrayContaining([
1109
1342
  { stream: sub_default, lane: "default" },
1110
1343
  { stream: sub_slow, lane: "slow" }
1111
1344
  ])
@@ -1127,7 +1360,7 @@ var runStoreTck = (options) => {
1127
1360
  stream: `lane-q-.*-${tag}`,
1128
1361
  limit: 100
1129
1362
  });
1130
- expect4(seen.sort()).toEqual([a, c]);
1363
+ expect5(seen.sort()).toEqual([a, c]);
1131
1364
  });
1132
1365
  it4("prioritize filters by lane", async () => {
1133
1366
  const tag = uid();
@@ -1138,14 +1371,14 @@ var runStoreTck = (options) => {
1138
1371
  { stream: b, lane: `pfast-${tag}` }
1139
1372
  ]);
1140
1373
  const updated = await store.prioritize({ lane: `pslow-${tag}` }, 7);
1141
- expect4(updated).toBe(1);
1374
+ expect5(updated).toBe(1);
1142
1375
  const seen = /* @__PURE__ */ new Map();
1143
1376
  await store.query_streams((p) => seen.set(p.stream, p.priority), {
1144
1377
  stream: `lane-pri-.*-${tag}`,
1145
1378
  limit: 100
1146
1379
  });
1147
- expect4(seen.get(a)).toBe(7);
1148
- expect4(seen.get(b)).toBe(0);
1380
+ expect5(seen.get(a)).toBe(7);
1381
+ expect5(seen.get(b)).toBe(0);
1149
1382
  });
1150
1383
  it4("reset filters by lane", async () => {
1151
1384
  const tag = uid();
@@ -1165,7 +1398,7 @@ var runStoreTck = (options) => {
1165
1398
  const mine = leases.filter((l) => l.stream === a || l.stream === b);
1166
1399
  await store.ack(mine.map((l) => ({ ...l, at: l.at + 1 })));
1167
1400
  const count = await store.reset({ lane: `rslow-${tag}` });
1168
- expect4(count).toBe(1);
1401
+ expect5(count).toBe(1);
1169
1402
  const ats = /* @__PURE__ */ new Map();
1170
1403
  for (const name of [a, b]) {
1171
1404
  await store.query_streams((p) => ats.set(p.stream, p.at), {
@@ -1173,8 +1406,8 @@ var runStoreTck = (options) => {
1173
1406
  stream_exact: true
1174
1407
  });
1175
1408
  }
1176
- expect4(ats.get(a)).toBe(-1);
1177
- expect4(ats.get(b)).toBeGreaterThanOrEqual(0);
1409
+ expect5(ats.get(a)).toBe(-1);
1410
+ expect5(ats.get(b)).toBeGreaterThanOrEqual(0);
1178
1411
  });
1179
1412
  it4("unblock filters by lane", async () => {
1180
1413
  const tag = uid();
@@ -1194,7 +1427,7 @@ var runStoreTck = (options) => {
1194
1427
  const mine = leases.filter((l) => l.stream === a || l.stream === b);
1195
1428
  await store.block(mine.map((l) => ({ ...l, error: "boom" })));
1196
1429
  const count = await store.unblock({ lane: `uslow-${tag}` });
1197
- expect4(count).toBe(1);
1430
+ expect5(count).toBe(1);
1198
1431
  const blocked = /* @__PURE__ */ new Map();
1199
1432
  for (const name of [a, b]) {
1200
1433
  await store.query_streams((p) => blocked.set(p.stream, p.blocked), {
@@ -1202,11 +1435,11 @@ var runStoreTck = (options) => {
1202
1435
  stream_exact: true
1203
1436
  });
1204
1437
  }
1205
- expect4(blocked.get(a)).toBe(false);
1206
- expect4(blocked.get(b)).toBe(true);
1438
+ expect5(blocked.get(a)).toBe(false);
1439
+ expect5(blocked.get(b)).toBe(true);
1207
1440
  });
1208
1441
  });
1209
- describe4("truncate", () => {
1442
+ describe5("truncate", () => {
1210
1443
  it4("seeds a tombstone when no snapshot is provided", async () => {
1211
1444
  const s = `trunc-tomb-${uid()}`;
1212
1445
  await store.commit(
@@ -1215,7 +1448,7 @@ var runStoreTck = (options) => {
1215
1448
  make_meta({ stream: s })
1216
1449
  );
1217
1450
  const result = await store.truncate([{ stream: s }]);
1218
- expect4(result.get(s)?.deleted).toBe(2);
1451
+ expect5(result.get(s)?.deleted).toBe(2);
1219
1452
  const remaining = [];
1220
1453
  await store.query(
1221
1454
  (e) => {
@@ -1223,8 +1456,8 @@ var runStoreTck = (options) => {
1223
1456
  },
1224
1457
  { stream: s, stream_exact: true }
1225
1458
  );
1226
- expect4(remaining).toHaveLength(1);
1227
- expect4(remaining[0].name).toBe(
1459
+ expect5(remaining).toHaveLength(1);
1460
+ expect5(remaining[0].name).toBe(
1228
1461
  "__tombstone__"
1229
1462
  );
1230
1463
  });
@@ -1238,7 +1471,7 @@ var runStoreTck = (options) => {
1238
1471
  const result = await store.truncate([
1239
1472
  { stream: s, snapshot: { count: 7 } }
1240
1473
  ]);
1241
- expect4(result.get(s)?.deleted).toBe(1);
1474
+ expect5(result.get(s)?.deleted).toBe(1);
1242
1475
  const remaining = [];
1243
1476
  await store.query(
1244
1477
  (e) => {
@@ -1246,23 +1479,23 @@ var runStoreTck = (options) => {
1246
1479
  },
1247
1480
  { stream: s, stream_exact: true, with_snaps: true }
1248
1481
  );
1249
- expect4(remaining).toHaveLength(1);
1250
- expect4(remaining[0].name).toBe(
1482
+ expect5(remaining).toHaveLength(1);
1483
+ expect5(remaining[0].name).toBe(
1251
1484
  "__snapshot__"
1252
1485
  );
1253
- expect4(remaining[0].data).toEqual({ count: 7 });
1486
+ expect5(remaining[0].data).toEqual({ count: 7 });
1254
1487
  });
1255
1488
  it4("returns an empty map for empty input", async () => {
1256
1489
  const result = await store.truncate([]);
1257
- expect4(result.size).toBe(0);
1490
+ expect5(result.size).toBe(0);
1258
1491
  });
1259
1492
  it4("returns 0 deleted for streams that don't exist", async () => {
1260
1493
  const s = `trunc-missing-${uid()}`;
1261
1494
  const result = await store.truncate([{ stream: s }]);
1262
- expect4(result.get(s)?.deleted).toBe(0);
1495
+ expect5(result.get(s)?.deleted).toBe(0);
1263
1496
  });
1264
1497
  });
1265
- describe4("query_streams", () => {
1498
+ describe5("query_streams", () => {
1266
1499
  it4("returns positions filtered by stream regex, exact, source, and source_exact", async () => {
1267
1500
  const tag = uid();
1268
1501
  const proj1 = `qs-${tag}-projection-tickets`;
@@ -1282,35 +1515,35 @@ var runStoreTck = (options) => {
1282
1515
  (p) => all.push({ stream: p.stream, source: p.source }),
1283
1516
  { stream: `qs-${tag}-.*` }
1284
1517
  );
1285
- expect4(all_result.count).toBe(4);
1286
- expect4(all_result.maxEventId).toBeGreaterThanOrEqual(-1);
1287
- expect4(all.map((p) => p.stream).sort()).toEqual(
1518
+ expect5(all_result.count).toBe(4);
1519
+ expect5(all_result.maxEventId).toBeGreaterThanOrEqual(-1);
1520
+ expect5(all.map((p) => p.stream).sort()).toEqual(
1288
1521
  [proj1, proj2, dyn1, dyn2].sort()
1289
1522
  );
1290
1523
  const projections = [];
1291
1524
  await store.query_streams((p) => projections.push(p.stream), {
1292
1525
  stream: `qs-${tag}-projection-.*`
1293
1526
  });
1294
- expect4(projections.sort()).toEqual([proj1, proj2].sort());
1527
+ expect5(projections.sort()).toEqual([proj1, proj2].sort());
1295
1528
  const exact = [];
1296
1529
  await store.query_streams((p) => exact.push(p.stream), {
1297
1530
  stream: dyn1,
1298
1531
  stream_exact: true
1299
1532
  });
1300
- expect4(exact).toEqual([dyn1]);
1533
+ expect5(exact).toEqual([dyn1]);
1301
1534
  const by_source = [];
1302
1535
  await store.query_streams((p) => by_source.push(p.stream), {
1303
1536
  stream: `qs-${tag}-.*`,
1304
1537
  source: `qs-${tag}-src-.*`
1305
1538
  });
1306
- expect4(by_source.sort()).toEqual([dyn1, dyn2].sort());
1539
+ expect5(by_source.sort()).toEqual([dyn1, dyn2].sort());
1307
1540
  const exact_source = [];
1308
1541
  await store.query_streams((p) => exact_source.push(p.stream), {
1309
1542
  stream: `qs-${tag}-.*`,
1310
1543
  source: src2,
1311
1544
  source_exact: true
1312
1545
  });
1313
- expect4(exact_source).toEqual([dyn2]);
1546
+ expect5(exact_source).toEqual([dyn2]);
1314
1547
  });
1315
1548
  it4("paginates with limit + after (keyset)", async () => {
1316
1549
  const tag = uid();
@@ -1326,15 +1559,15 @@ var runStoreTck = (options) => {
1326
1559
  stream: `qp-${tag}-.*`,
1327
1560
  limit: 2
1328
1561
  });
1329
- expect4(page1).toHaveLength(2);
1562
+ expect5(page1).toHaveLength(2);
1330
1563
  const page2 = [];
1331
1564
  await store.query_streams((p) => page2.push(p.stream), {
1332
1565
  stream: `qp-${tag}-.*`,
1333
1566
  limit: 2,
1334
1567
  after: page1.at(-1)
1335
1568
  });
1336
- expect4(page2).toHaveLength(2);
1337
- expect4([...page1, ...page2].sort()).toEqual([...streams].sort());
1569
+ expect5(page2).toHaveLength(2);
1570
+ expect5([...page1, ...page2].sort()).toEqual([...streams].sort());
1338
1571
  });
1339
1572
  it4("filters by blocked status", async () => {
1340
1573
  const tag = uid();
@@ -1356,17 +1589,17 @@ var runStoreTck = (options) => {
1356
1589
  (p) => blocked.push({ stream: p.stream, error: p.error }),
1357
1590
  { stream: `qb-${tag}.*`, blocked: true }
1358
1591
  );
1359
- expect4(blocked).toHaveLength(1);
1360
- expect4(blocked[0].error).toBe("boom");
1592
+ expect5(blocked).toHaveLength(1);
1593
+ expect5(blocked[0].error).toBe("boom");
1361
1594
  const unblocked = [];
1362
1595
  await store.query_streams((p) => unblocked.push(p.stream), {
1363
1596
  stream: `qb-${tag}.*`,
1364
1597
  blocked: false
1365
1598
  });
1366
- expect4(unblocked).toEqual([sibling]);
1599
+ expect5(unblocked).toEqual([sibling]);
1367
1600
  });
1368
1601
  });
1369
- describe4("query_stats", () => {
1602
+ describe5("query_stats", () => {
1370
1603
  it4("array input \u2014 returns head per stream, absent when not in input", async () => {
1371
1604
  const tag = uid();
1372
1605
  const sA = `qst-${tag}-a`;
@@ -1388,16 +1621,16 @@ var runStoreTck = (options) => {
1388
1621
  make_meta({ stream: sUnasked })
1389
1622
  );
1390
1623
  const stats = await store.query_stats([sA, sB]);
1391
- expect4(stats.size).toBe(2);
1392
- expect4(stats.get(sA)?.head.name).toBe("Incremented");
1393
- expect4((stats.get(sA)?.head.data).amount).toBe(2);
1394
- expect4(stats.get(sB)?.head.name).toBe("Decremented");
1395
- expect4((stats.get(sB)?.head.data).amount).toBe(5);
1396
- expect4(stats.has(sUnasked)).toBe(false);
1624
+ expect5(stats.size).toBe(2);
1625
+ expect5(stats.get(sA)?.head.name).toBe("Incremented");
1626
+ expect5((stats.get(sA)?.head.data).amount).toBe(2);
1627
+ expect5(stats.get(sB)?.head.name).toBe("Decremented");
1628
+ expect5((stats.get(sB)?.head.data).amount).toBe(5);
1629
+ expect5(stats.has(sUnasked)).toBe(false);
1397
1630
  const empty = await store.query_stats([]);
1398
- expect4(empty.size).toBe(0);
1631
+ expect5(empty.size).toBe(0);
1399
1632
  const unknown = await store.query_stats([`qst-${tag}-missing`]);
1400
- expect4(unknown.size).toBe(0);
1633
+ expect5(unknown.size).toBe(0);
1401
1634
  });
1402
1635
  it4("tail returns the earliest event per stream", async () => {
1403
1636
  const tag = uid();
@@ -1421,10 +1654,10 @@ var runStoreTck = (options) => {
1421
1654
  tail: true
1422
1655
  });
1423
1656
  const r = stats.get(s);
1424
- expect4(r?.head.name).toBe("Incremented");
1425
- expect4((r?.head.data).amount).toBe(3);
1426
- expect4(r?.tail?.name).toBe("Incremented");
1427
- expect4((r?.tail?.data).amount).toBe(1);
1657
+ expect5(r?.head.name).toBe("Incremented");
1658
+ expect5((r?.head.data).amount).toBe(3);
1659
+ expect5(r?.tail?.name).toBe("Incremented");
1660
+ expect5((r?.tail?.data).amount).toBe(1);
1428
1661
  });
1429
1662
  it4("count + names \u2014 full aggregates including framework markers", async () => {
1430
1663
  const tag = uid();
@@ -1445,11 +1678,11 @@ var runStoreTck = (options) => {
1445
1678
  names: true
1446
1679
  });
1447
1680
  const r = stats.get(s);
1448
- expect4(r?.count).toBe(4);
1449
- expect4(r?.names?.[SNAP_EVENT]).toBe(1);
1450
- expect4(r?.names?.Incremented).toBe(2);
1451
- expect4(r?.names?.Decremented).toBe(1);
1452
- expect4(r?.names?.[SNAP_EVENT]).toBe(1);
1681
+ expect5(r?.count).toBe(4);
1682
+ expect5(r?.names?.[SNAP_EVENT]).toBe(1);
1683
+ expect5(r?.names?.Incremented).toBe(2);
1684
+ expect5(r?.names?.Decremented).toBe(1);
1685
+ expect5(r?.names?.[SNAP_EVENT]).toBe(1);
1453
1686
  });
1454
1687
  it4("exclude shifts head past filtered events; stream absent when all filtered", async () => {
1455
1688
  const tag = uid();
@@ -1466,21 +1699,21 @@ var runStoreTck = (options) => {
1466
1699
  make_meta({ stream: sAllOut })
1467
1700
  );
1468
1701
  const all = await store.query_stats([s]);
1469
- expect4(all.get(s)?.head.name).toBe("Incremented");
1470
- expect4((all.get(s)?.head.data).amount).toBe(3);
1702
+ expect5(all.get(s)?.head.name).toBe("Incremented");
1703
+ expect5((all.get(s)?.head.data).amount).toBe(3);
1471
1704
  const excl = await store.query_stats([s], {
1472
1705
  exclude: ["Incremented"]
1473
1706
  });
1474
- expect4(excl.get(s)?.head.name).toBe("Decremented");
1475
- expect4((excl.get(s)?.head.data).amount).toBe(2);
1707
+ expect5(excl.get(s)?.head.name).toBe("Decremented");
1708
+ expect5((excl.get(s)?.head.data).amount).toBe(2);
1476
1709
  const wipe = await store.query_stats([sAllOut], {
1477
1710
  exclude: ["Incremented", "Decremented", "Reset"]
1478
1711
  });
1479
- expect4(wipe.has(sAllOut)).toBe(false);
1712
+ expect5(wipe.has(sAllOut)).toBe(false);
1480
1713
  const no_tomb = await store.query_stats([s], {
1481
1714
  exclude: [TOMBSTONE_EVENT]
1482
1715
  });
1483
- expect4(no_tomb.get(s)?.head.name).toBe("Incremented");
1716
+ expect5(no_tomb.get(s)?.head.name).toBe("Incremented");
1484
1717
  });
1485
1718
  it4("before \u2014 time travel narrows head/tail/count", async () => {
1486
1719
  const tag = uid();
@@ -1507,13 +1740,13 @@ var runStoreTck = (options) => {
1507
1740
  before
1508
1741
  });
1509
1742
  const r = stats.get(s);
1510
- expect4(r?.count).toBe(1);
1511
- expect4(r?.head.id).toBe(c1[0].id);
1512
- expect4(r?.tail?.id).toBe(c1[0].id);
1743
+ expect5(r?.count).toBe(1);
1744
+ expect5(r?.head.id).toBe(c1[0].id);
1745
+ expect5(r?.tail?.id).toBe(c1[0].id);
1513
1746
  const empty = await store.query_stats([s], {
1514
1747
  before: 0
1515
1748
  });
1516
- expect4(empty.has(s)).toBe(false);
1749
+ expect5(empty.has(s)).toBe(false);
1517
1750
  });
1518
1751
  it4("filter form \u2014 stream regex, stream_exact, empty {} match", async () => {
1519
1752
  const tag = uid();
@@ -1538,16 +1771,16 @@ var runStoreTck = (options) => {
1538
1771
  const orders = await store.query_stats({
1539
1772
  stream: `^qsf-${tag}-orders-`
1540
1773
  });
1541
- expect4([...orders.keys()].sort()).toEqual([sA, sB].sort());
1774
+ expect5([...orders.keys()].sort()).toEqual([sA, sB].sort());
1542
1775
  const exact = await store.query_stats({
1543
1776
  stream: sA,
1544
1777
  stream_exact: true
1545
1778
  });
1546
- expect4([...exact.keys()]).toEqual([sA]);
1779
+ expect5([...exact.keys()]).toEqual([sA]);
1547
1780
  const all = await store.query_stats({
1548
1781
  stream: `^qsf-${tag}-`
1549
1782
  });
1550
- expect4([...all.keys()].sort()).toEqual([sA, sB, sOther].sort());
1783
+ expect5([...all.keys()].sort()).toEqual([sA, sB, sOther].sort());
1551
1784
  });
1552
1785
  it4("compose with query_streams for subscription-level filters", async () => {
1553
1786
  const tag = uid();
@@ -1566,7 +1799,7 @@ var runStoreTck = (options) => {
1566
1799
  );
1567
1800
  const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
1568
1801
  const mine = leased.find((l) => l.stream === a);
1569
- expect4(mine).toBeDefined();
1802
+ expect5(mine).toBeDefined();
1570
1803
  const others = leased.filter((l) => l.stream !== a);
1571
1804
  await store.ack(others);
1572
1805
  await store.block([{ ...mine, error: "boom" }]);
@@ -1575,10 +1808,10 @@ var runStoreTck = (options) => {
1575
1808
  stream: `^qsc-${tag}-`,
1576
1809
  blocked: true
1577
1810
  });
1578
- expect4(blocked_names).toEqual([a]);
1811
+ expect5(blocked_names).toEqual([a]);
1579
1812
  const stats = await store.query_stats(blocked_names);
1580
- expect4(stats.get(a)?.head.name).toBe("Incremented");
1581
- expect4(stats.has(b)).toBe(false);
1813
+ expect5(stats.get(a)?.head.name).toBe("Incremented");
1814
+ expect5(stats.has(b)).toBe(false);
1582
1815
  });
1583
1816
  it4("empty filter {} \u2014 matches every event-bearing stream", async () => {
1584
1817
  const tag = uid();
@@ -1595,8 +1828,8 @@ var runStoreTck = (options) => {
1595
1828
  make_meta({ stream: b })
1596
1829
  );
1597
1830
  const all = await store.query_stats({});
1598
- expect4(all.has(a)).toBe(true);
1599
- expect4(all.has(b)).toBe(true);
1831
+ expect5(all.has(a)).toBe(true);
1832
+ expect5(all.has(b)).toBe(true);
1600
1833
  });
1601
1834
  it4("stat-flag combinations \u2014 count-only, names-only, tail-only", async () => {
1602
1835
  const tag = uid();
@@ -1609,23 +1842,23 @@ var runStoreTck = (options) => {
1609
1842
  const c = await store.query_stats([s], {
1610
1843
  count: true
1611
1844
  });
1612
- expect4(c.get(s)?.count).toBe(3);
1613
- expect4(c.get(s)?.names).toBeUndefined();
1614
- expect4(c.get(s)?.tail).toBeUndefined();
1845
+ expect5(c.get(s)?.count).toBe(3);
1846
+ expect5(c.get(s)?.names).toBeUndefined();
1847
+ expect5(c.get(s)?.tail).toBeUndefined();
1615
1848
  const n = await store.query_stats([s], {
1616
1849
  names: true
1617
1850
  });
1618
- expect4(n.get(s)?.names).toEqual({ Incremented: 2, Decremented: 1 });
1619
- expect4(n.get(s)?.count).toBeUndefined();
1620
- expect4(n.get(s)?.tail).toBeUndefined();
1851
+ expect5(n.get(s)?.names).toEqual({ Incremented: 2, Decremented: 1 });
1852
+ expect5(n.get(s)?.count).toBeUndefined();
1853
+ expect5(n.get(s)?.tail).toBeUndefined();
1621
1854
  const t = await store.query_stats([s], { tail: true });
1622
- expect4(t.get(s)?.tail?.name).toBe("Incremented");
1623
- expect4((t.get(s)?.tail?.data).amount).toBe(1);
1624
- expect4(t.get(s)?.count).toBeUndefined();
1625
- expect4(t.get(s)?.names).toBeUndefined();
1855
+ expect5(t.get(s)?.tail?.name).toBe("Incremented");
1856
+ expect5((t.get(s)?.tail?.data).amount).toBe(1);
1857
+ expect5(t.get(s)?.count).toBeUndefined();
1858
+ expect5(t.get(s)?.names).toBeUndefined();
1626
1859
  });
1627
1860
  });
1628
- describe4("query_streams anchor contract", () => {
1861
+ describe5("query_streams anchor contract", () => {
1629
1862
  it4("plain regex without anchors is a substring match", async () => {
1630
1863
  const tag = uid();
1631
1864
  const inner = `qsr-${tag}-inner`;
@@ -1640,7 +1873,7 @@ var runStoreTck = (options) => {
1640
1873
  await store.query_streams((p) => seen.push(p.stream), {
1641
1874
  stream: `qsr-${tag}-inner`
1642
1875
  });
1643
- expect4(seen.sort()).toEqual([inner, longer].sort());
1876
+ expect5(seen.sort()).toEqual([inner, longer].sort());
1644
1877
  });
1645
1878
  it4("caller-anchored `^name$` matches only the whole string", async () => {
1646
1879
  const tag = uid();
@@ -1651,7 +1884,7 @@ var runStoreTck = (options) => {
1651
1884
  await store.query_streams((p) => seen.push(p.stream), {
1652
1885
  stream: `^qsr-${tag}-anchor$`
1653
1886
  });
1654
- expect4(seen).toEqual([inner]);
1887
+ expect5(seen).toEqual([inner]);
1655
1888
  });
1656
1889
  it4("caller-anchored `^prefix` matches by prefix", async () => {
1657
1890
  const tag = uid();
@@ -1667,10 +1900,10 @@ var runStoreTck = (options) => {
1667
1900
  await store.query_streams((p) => seen.push(p.stream), {
1668
1901
  stream: `^qsr-${tag}-pfx-`
1669
1902
  });
1670
- expect4(seen.sort()).toEqual([a, b].sort());
1903
+ expect5(seen.sort()).toEqual([a, b].sort());
1671
1904
  });
1672
1905
  });
1673
- describe4("prioritize anchor contract", () => {
1906
+ describe5("prioritize anchor contract", () => {
1674
1907
  it4("caller-anchored `^name$` filter matches only the whole string", async () => {
1675
1908
  const tag = uid();
1676
1909
  const inner = `pr-${tag}-anchor`;
@@ -1683,16 +1916,16 @@ var runStoreTck = (options) => {
1683
1916
  { stream: `^pr-${tag}-anchor$` },
1684
1917
  7
1685
1918
  );
1686
- expect4(updated).toBe(1);
1919
+ expect5(updated).toBe(1);
1687
1920
  const seen = /* @__PURE__ */ new Map();
1688
1921
  await store.query_streams((p) => seen.set(p.stream, p.priority), {
1689
1922
  stream: `pr-${tag}-anchor`
1690
1923
  });
1691
- expect4(seen.get(inner)).toBe(7);
1692
- expect4(seen.get(longer)).toBe(0);
1924
+ expect5(seen.get(inner)).toBe(7);
1925
+ expect5(seen.get(longer)).toBe(0);
1693
1926
  });
1694
1927
  });
1695
- describe4("query_streams head", () => {
1928
+ describe5("query_streams head", () => {
1696
1929
  it4("maxEventId tracks the highest committed id", async () => {
1697
1930
  const s = `head-${uid()}`;
1698
1931
  await store.subscribe([{ stream: s }]);
@@ -1706,34 +1939,34 @@ var runStoreTck = (options) => {
1706
1939
  (p) => positions.push(p.stream),
1707
1940
  { stream: s, stream_exact: true, limit: 1 }
1708
1941
  );
1709
- expect4(maxEventId).toBeGreaterThanOrEqual(0);
1710
- expect4(positions).toEqual([s]);
1942
+ expect5(maxEventId).toBeGreaterThanOrEqual(0);
1943
+ expect5(positions).toEqual([s]);
1711
1944
  });
1712
1945
  });
1713
- describe4("seed_stream helper coverage", () => {
1946
+ describe5("seed_stream helper coverage", () => {
1714
1947
  it4("commits N events with monotonically increasing ids", async () => {
1715
1948
  const s = `seed-${uid()}`;
1716
1949
  const committed = await seed_stream(store, s, 3);
1717
- expect4(committed).toHaveLength(3);
1950
+ expect5(committed).toHaveLength(3);
1718
1951
  for (let i = 1; i < committed.length; i++) {
1719
- expect4(committed[i].id).toBeGreaterThan(committed[i - 1].id);
1952
+ expect5(committed[i].id).toBeGreaterThan(committed[i - 1].id);
1720
1953
  }
1721
1954
  });
1722
1955
  });
1723
- describe4.skipIf(!caps.restore)("restore (capability)", () => {
1956
+ describe5.skipIf(!caps.restore)("restore (capability)", () => {
1724
1957
  beforeEach(async () => {
1725
1958
  await store.drop();
1726
1959
  await store.seed();
1727
1960
  });
1728
- const as_source = (events) => ({
1961
+ const as_source = (events2) => ({
1729
1962
  async query(callback) {
1730
- for (const e of events)
1963
+ for (const e of events2)
1731
1964
  await Promise.resolve(
1732
1965
  callback(
1733
1966
  e
1734
1967
  )
1735
1968
  );
1736
- return events.length;
1969
+ return events2.length;
1737
1970
  },
1738
1971
  async dispose() {
1739
1972
  }
@@ -1759,27 +1992,27 @@ var runStoreTck = (options) => {
1759
1992
  };
1760
1993
  it4("returns kept=0 on an empty source", async () => {
1761
1994
  const result = await restore(as_source([]));
1762
- expect4(result.kept).toBe(0);
1763
- expect4(result.duration_ms).toBeGreaterThanOrEqual(0);
1764
- expect4(result.dropped).toEqual({
1995
+ expect5(result.kept).toBe(0);
1996
+ expect5(result.duration_ms).toBeGreaterThanOrEqual(0);
1997
+ expect5(result.dropped).toEqual({
1765
1998
  closed_streams: 0,
1766
1999
  snapshots: 0
1767
2000
  });
1768
- const events = await collect(store, { limit: 10 });
1769
- expect4(events).toHaveLength(0);
2001
+ const events2 = await collect(store, { limit: 10 });
2002
+ expect5(events2).toHaveLength(0);
1770
2003
  });
1771
2004
  it4("rebuilds a single stream and preserves `created` verbatim", async () => {
1772
2005
  const s = `restore-single-${uid()}`;
1773
2006
  const t0 = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
1774
2007
  const t1 = /* @__PURE__ */ new Date("2020-01-02T00:00:00.000Z");
1775
2008
  const t2 = /* @__PURE__ */ new Date("2020-01-03T00:00:00.000Z");
1776
- const events = [
2009
+ const events2 = [
1777
2010
  event(1, s, 0, "Incremented", t0, { amount: 1 }),
1778
2011
  event(2, s, 1, "Incremented", t1, { amount: 2 }),
1779
2012
  event(3, s, 2, "Decremented", t2, { amount: 1 })
1780
2013
  ];
1781
- const result = await restore(as_source(events));
1782
- expect4(result.kept).toBe(3);
2014
+ const result = await restore(as_source(events2));
2015
+ expect5(result.kept).toBe(3);
1783
2016
  const back = [];
1784
2017
  await store.query(
1785
2018
  (e) => {
@@ -1787,8 +2020,8 @@ var runStoreTck = (options) => {
1787
2020
  },
1788
2021
  { stream: s, stream_exact: true }
1789
2022
  );
1790
- expect4(back).toHaveLength(3);
1791
- expect4(
2023
+ expect5(back).toHaveLength(3);
2024
+ expect5(
1792
2025
  back.map((e) => ({
1793
2026
  stream: e.stream,
1794
2027
  version: e.version,
@@ -1824,14 +2057,14 @@ var runStoreTck = (options) => {
1824
2057
  const a = `restore-multi-a-${uid()}`;
1825
2058
  const b = `restore-multi-b-${uid()}`;
1826
2059
  const t = /* @__PURE__ */ new Date("2020-06-01T00:00:00.000Z");
1827
- const events = [
2060
+ const events2 = [
1828
2061
  event(1, a, 0, "Incremented", t, { amount: 10 }),
1829
2062
  event(2, b, 0, "Incremented", t, { amount: 20 }),
1830
2063
  event(3, a, 1, "Decremented", t, { amount: 5 }),
1831
2064
  event(4, b, 1, "Incremented", t, { amount: 30 })
1832
2065
  ];
1833
- const result = await restore(as_source(events));
1834
- expect4(result.kept).toBe(4);
2066
+ const result = await restore(as_source(events2));
2067
+ expect5(result.kept).toBe(4);
1835
2068
  const aBack = [];
1836
2069
  const bBack = [];
1837
2070
  await store.query(
@@ -1846,8 +2079,8 @@ var runStoreTck = (options) => {
1846
2079
  },
1847
2080
  { stream: b, stream_exact: true }
1848
2081
  );
1849
- expect4(aBack.map((e) => e.version)).toEqual([0, 1]);
1850
- expect4(bBack.map((e) => e.version)).toEqual([0, 1]);
2082
+ expect5(aBack.map((e) => e.version)).toEqual([0, 1]);
2083
+ expect5(bBack.map((e) => e.version)).toEqual([0, 1]);
1851
2084
  });
1852
2085
  it4("preserves Date `created` verbatim", async () => {
1853
2086
  const s = `restore-isoc-${uid()}`;
@@ -1872,8 +2105,8 @@ var runStoreTck = (options) => {
1872
2105
  },
1873
2106
  { stream: s, stream_exact: true }
1874
2107
  );
1875
- expect4(back).toHaveLength(1);
1876
- expect4(back[0].created.toISOString()).toBe(iso);
2108
+ expect5(back).toHaveLength(1);
2109
+ expect5(back[0].created.toISOString()).toBe(iso);
1877
2110
  });
1878
2111
  it4("wipes pre-existing events before inserting", async () => {
1879
2112
  const old = `restore-old-${uid()}`;
@@ -1891,12 +2124,12 @@ var runStoreTck = (options) => {
1891
2124
  stream: old,
1892
2125
  stream_exact: true
1893
2126
  });
1894
- expect4(old_back).toHaveLength(0);
2127
+ expect5(old_back).toHaveLength(0);
1895
2128
  const fresh_back = await collect(store, {
1896
2129
  stream: fresh,
1897
2130
  stream_exact: true
1898
2131
  });
1899
- expect4(fresh_back).toHaveLength(1);
2132
+ expect5(fresh_back).toHaveLength(1);
1900
2133
  });
1901
2134
  it4("clears subscription/stream-position metadata", async () => {
1902
2135
  const sub = `restore-sub-${uid()}`;
@@ -1909,10 +2142,10 @@ var runStoreTck = (options) => {
1909
2142
  return out;
1910
2143
  };
1911
2144
  const before = await collect_streams();
1912
- expect4(before.includes(sub)).toBe(true);
2145
+ expect5(before.includes(sub)).toBe(true);
1913
2146
  await restore(as_source([]));
1914
2147
  const after = await collect_streams();
1915
- expect4(after.includes(sub)).toBe(false);
2148
+ expect5(after.includes(sub)).toBe(false);
1916
2149
  });
1917
2150
  it4("preserves snapshot events through restore", async () => {
1918
2151
  const s = `restore-snap-${uid()}`;
@@ -1935,13 +2168,13 @@ var runStoreTck = (options) => {
1935
2168
  stream_exact: true,
1936
2169
  with_snaps: true
1937
2170
  });
1938
- expect4(back).toHaveLength(1);
1939
- expect4(back[0].name).toBe(SNAP_EVENT);
2171
+ expect5(back).toHaveLength(1);
2172
+ expect5(back[0].name).toBe(SNAP_EVENT);
1940
2173
  });
1941
2174
  it4("rewrites causation refs through the old\u2192new id map", async () => {
1942
2175
  const s = `restore-caus-${uid()}`;
1943
2176
  const t = /* @__PURE__ */ new Date("2020-08-01T00:00:00.000Z");
1944
- const events = [
2177
+ const events2 = [
1945
2178
  {
1946
2179
  id: 5,
1947
2180
  stream: s,
@@ -1980,7 +2213,7 @@ var runStoreTck = (options) => {
1980
2213
  }
1981
2214
  }
1982
2215
  ];
1983
- await restore(as_source(events));
2216
+ await restore(as_source(events2));
1984
2217
  const back = [];
1985
2218
  await store.query(
1986
2219
  (e) => {
@@ -1988,10 +2221,10 @@ var runStoreTck = (options) => {
1988
2221
  },
1989
2222
  { stream: s, stream_exact: true }
1990
2223
  );
1991
- expect4(back).toHaveLength(3);
1992
- expect4(back[0].meta.causation.event).toBeUndefined();
1993
- expect4(back[1].meta.causation.event?.id).toBe(back[0].id);
1994
- expect4(back[2].meta.causation.event?.id).toBe(back[1].id);
2224
+ expect5(back).toHaveLength(3);
2225
+ expect5(back[0].meta.causation.event).toBeUndefined();
2226
+ expect5(back[1].meta.causation.event?.id).toBe(back[0].id);
2227
+ expect5(back[2].meta.causation.event?.id).toBe(back[1].id);
1995
2228
  });
1996
2229
  it4("leaves causation refs unmapped when the target isn't in the source", async () => {
1997
2230
  const s = `restore-orphan-${uid()}`;
@@ -2021,7 +2254,7 @@ var runStoreTck = (options) => {
2021
2254
  },
2022
2255
  { stream: s, stream_exact: true }
2023
2256
  );
2024
- expect4(back[0].meta.causation.event?.id).toBe(999);
2257
+ expect5(back[0].meta.causation.event?.id).toBe(999);
2025
2258
  });
2026
2259
  it4("rolls back atomically when the source throws mid-iteration", async () => {
2027
2260
  const original = `restore-pre-${uid()}`;
@@ -2049,7 +2282,7 @@ var runStoreTck = (options) => {
2049
2282
  async dispose() {
2050
2283
  }
2051
2284
  };
2052
- await expect4(restore(explosive)).rejects.toThrow("boom");
2285
+ await expect5(restore(explosive)).rejects.toThrow("boom");
2053
2286
  const back = [];
2054
2287
  await store.query(
2055
2288
  (e) => {
@@ -2057,8 +2290,8 @@ var runStoreTck = (options) => {
2057
2290
  },
2058
2291
  { stream: original, stream_exact: true }
2059
2292
  );
2060
- expect4(back).toHaveLength(3);
2061
- expect4(back.map((e) => e.id)).toEqual(committed.map((c) => c.id));
2293
+ expect5(back).toHaveLength(3);
2294
+ expect5(back.map((e) => e.id)).toEqual(committed.map((c) => c.id));
2062
2295
  });
2063
2296
  it4("drop_snapshots: skips SNAP_EVENT rows and counts them", async () => {
2064
2297
  const s = `restore-drop-snap-${uid()}`;
@@ -2079,15 +2312,15 @@ var runStoreTck = (options) => {
2079
2312
  ]),
2080
2313
  { drop_snapshots: true }
2081
2314
  );
2082
- expect4(result.kept).toBe(2);
2083
- expect4(result.dropped.snapshots).toBe(1);
2315
+ expect5(result.kept).toBe(2);
2316
+ expect5(result.dropped.snapshots).toBe(1);
2084
2317
  const back = await collect(store, {
2085
2318
  stream: s,
2086
2319
  stream_exact: true,
2087
2320
  with_snaps: true
2088
2321
  });
2089
- expect4(back).toHaveLength(2);
2090
- expect4(
2322
+ expect5(back).toHaveLength(2);
2323
+ expect5(
2091
2324
  back.every((e) => e.name !== SNAP_EVENT)
2092
2325
  ).toBe(true);
2093
2326
  });
@@ -2102,10 +2335,10 @@ var runStoreTck = (options) => {
2102
2335
  ]),
2103
2336
  { on_progress: (p) => calls.push(p.processed) }
2104
2337
  );
2105
- expect4(calls).toEqual([1, 2]);
2338
+ expect5(calls).toEqual([1, 2]);
2106
2339
  });
2107
2340
  });
2108
- describe4.skipIf(!caps.pii_isolation)("pii_isolation (capability)", () => {
2341
+ describe5.skipIf(!caps.pii_isolation)("pii_isolation (capability)", () => {
2109
2342
  it4("commits and loads pii alongside data", async () => {
2110
2343
  const s = `pii-roundtrip-${uid()}`;
2111
2344
  const committed = await store.commit(
@@ -2119,8 +2352,8 @@ var runStoreTck = (options) => {
2119
2352
  ],
2120
2353
  make_meta({ stream: s })
2121
2354
  );
2122
- expect4(committed).toHaveLength(1);
2123
- expect4(committed[0].pii).toEqual({
2355
+ expect5(committed).toHaveLength(1);
2356
+ expect5(committed[0].pii).toEqual({
2124
2357
  email: "u@example.com",
2125
2358
  name: "Ursula"
2126
2359
  });
@@ -2131,9 +2364,9 @@ var runStoreTck = (options) => {
2131
2364
  },
2132
2365
  { stream: s, stream_exact: true }
2133
2366
  );
2134
- expect4(seen).toHaveLength(1);
2135
- expect4(seen[0].pii).toEqual({ email: "u@example.com", name: "Ursula" });
2136
- expect4(seen[0].data).toEqual({ amount: 1 });
2367
+ expect5(seen).toHaveLength(1);
2368
+ expect5(seen[0].pii).toEqual({ email: "u@example.com", name: "Ursula" });
2369
+ expect5(seen[0].data).toEqual({ amount: 1 });
2137
2370
  });
2138
2371
  it4("passes through events without pii (pii is null or undefined on load)", async () => {
2139
2372
  const s = `pii-none-${uid()}`;
@@ -2149,8 +2382,8 @@ var runStoreTck = (options) => {
2149
2382
  },
2150
2383
  { stream: s, stream_exact: true }
2151
2384
  );
2152
- expect4(seen).toHaveLength(1);
2153
- expect4(seen[0].pii == null).toBe(true);
2385
+ expect5(seen).toHaveLength(1);
2386
+ expect5(seen[0].pii == null).toBe(true);
2154
2387
  });
2155
2388
  it4("wipes pii for every event on the stream via forget_pii", async () => {
2156
2389
  const s = `pii-forget-${uid()}`;
@@ -2171,9 +2404,9 @@ var runStoreTck = (options) => {
2171
2404
  make_meta({ stream: s })
2172
2405
  );
2173
2406
  const forget = store.forget_pii;
2174
- expect4(forget).toBeDefined();
2407
+ expect5(forget).toBeDefined();
2175
2408
  const wiped = await forget.call(store, s);
2176
- expect4(wiped).toBe(2);
2409
+ expect5(wiped).toBe(2);
2177
2410
  const seen = [];
2178
2411
  await store.query(
2179
2412
  (e) => {
@@ -2181,10 +2414,10 @@ var runStoreTck = (options) => {
2181
2414
  },
2182
2415
  { stream: s, stream_exact: true }
2183
2416
  );
2184
- expect4(seen).toHaveLength(2);
2417
+ expect5(seen).toHaveLength(2);
2185
2418
  for (const e of seen) {
2186
- expect4(e.pii == null).toBe(true);
2187
- expect4(e.data).toBeDefined();
2419
+ expect5(e.pii == null).toBe(true);
2420
+ expect5(e.data).toBeDefined();
2188
2421
  }
2189
2422
  });
2190
2423
  it4("is idempotent \u2014 second forget_pii returns 0, no error", async () => {
@@ -2202,9 +2435,9 @@ var runStoreTck = (options) => {
2202
2435
  );
2203
2436
  const forget = store.forget_pii;
2204
2437
  const first = await forget.call(store, s);
2205
- expect4(first).toBe(1);
2438
+ expect5(first).toBe(1);
2206
2439
  const second = await forget.call(store, s);
2207
- expect4(second).toBe(0);
2440
+ expect5(second).toBe(0);
2208
2441
  });
2209
2442
  it4("only wipes the targeted stream \u2014 siblings untouched", async () => {
2210
2443
  const sA = `pii-iso-a-${uid()}`;
@@ -2239,7 +2472,7 @@ var runStoreTck = (options) => {
2239
2472
  },
2240
2473
  { stream: sA, stream_exact: true }
2241
2474
  );
2242
- expect4(a[0].pii == null).toBe(true);
2475
+ expect5(a[0].pii == null).toBe(true);
2243
2476
  const b = [];
2244
2477
  await store.query(
2245
2478
  (e) => {
@@ -2247,7 +2480,7 @@ var runStoreTck = (options) => {
2247
2480
  },
2248
2481
  { stream: sB, stream_exact: true }
2249
2482
  );
2250
- expect4(b[0].pii).toEqual({ email: "bob@example.com" });
2483
+ expect5(b[0].pii).toEqual({ email: "bob@example.com" });
2251
2484
  });
2252
2485
  it4("forget_pii on a stream with no pii events returns 0", async () => {
2253
2486
  const s = `pii-forget-empty-${uid()}`;
@@ -2257,14 +2490,14 @@ var runStoreTck = (options) => {
2257
2490
  make_meta({ stream: s })
2258
2491
  );
2259
2492
  const wiped = await store.forget_pii.call(store, s);
2260
- expect4(wiped).toBe(0);
2493
+ expect5(wiped).toBe(0);
2261
2494
  });
2262
2495
  });
2263
2496
  if (caps.notify) {
2264
- describe4("notify (capability)", () => {
2497
+ describe5("notify (capability)", () => {
2265
2498
  it4("delivers a notification when a different instance commits", async () => {
2266
2499
  const notify = store.notify;
2267
- expect4(notify).toBeDefined();
2500
+ expect5(notify).toBeDefined();
2268
2501
  const received = [];
2269
2502
  let resolve_arrived;
2270
2503
  const arrived = new Promise((res) => {
@@ -2283,9 +2516,9 @@ var runStoreTck = (options) => {
2283
2516
  make_meta({ stream })
2284
2517
  );
2285
2518
  await arrived;
2286
- expect4(received.length).toBeGreaterThanOrEqual(1);
2287
- expect4(received[0].stream).toBe(stream);
2288
- expect4(received[0].events.length).toBeGreaterThanOrEqual(1);
2519
+ expect5(received.length).toBeGreaterThanOrEqual(1);
2520
+ expect5(received[0].stream).toBe(stream);
2521
+ expect5(received[0].events.length).toBeGreaterThanOrEqual(1);
2289
2522
  } finally {
2290
2523
  await writer.dispose();
2291
2524
  await Promise.resolve(disposer());
@@ -2309,6 +2542,7 @@ export {
2309
2542
  runCacheTck,
2310
2543
  runLoggerTck,
2311
2544
  runStabilityTck,
2545
+ runStorePropertyTck,
2312
2546
  runStoreTck,
2313
2547
  uid
2314
2548
  };