@rotorsoft/act-tck 1.41.3 → 2.0.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.cjs CHANGED
@@ -82,8 +82,7 @@ var seed_stream = async (store, stream, count, correlation) => {
82
82
  const out = [];
83
83
  for (let i = 0; i < count; i++) {
84
84
  const committed = await store.commit(
85
- stream,
86
- [inc(1)],
85
+ [{ stream, msgs: [inc(1)] }],
87
86
  make_meta({ correlation, stream })
88
87
  );
89
88
  out.push(...committed);
@@ -646,14 +645,17 @@ var apply_plan = async (store, plan, pii_isolation) => {
646
645
  if (op.t === "commit") {
647
646
  const msgs = op.pii && pii_isolation ? op.msgs.map((m, i) => i === 0 ? { ...m, pii: op.pii } : m) : op.msgs;
648
647
  await store.commit(
649
- op.stream,
650
- msgs,
648
+ [{ stream: op.stream, msgs }],
651
649
  make_meta({ stream: op.stream, correlation: plan.correlation })
652
650
  );
653
651
  } else if (op.t === "snapshot") {
654
652
  await store.commit(
655
- op.stream,
656
- [{ name: import_act.SNAP_EVENT, data: { count: op.count } }],
653
+ [
654
+ {
655
+ stream: op.stream,
656
+ msgs: [{ name: import_act.SNAP_EVENT, data: { count: op.count } }]
657
+ }
658
+ ],
657
659
  make_meta({ stream: op.stream, correlation: plan.correlation })
658
660
  );
659
661
  } else if (op.t === "truncate") {
@@ -662,8 +664,12 @@ var apply_plan = async (store, plan, pii_isolation) => {
662
664
  ]);
663
665
  } else if (op.t === "truncate_windowed") {
664
666
  await store.commit(
665
- op.stream,
666
- [{ name: import_act.SNAP_EVENT, data: { count: 1 } }],
667
+ [
668
+ {
669
+ stream: op.stream,
670
+ msgs: [{ name: import_act.SNAP_EVENT, data: { count: 1 } }]
671
+ }
672
+ ],
667
673
  make_meta({ stream: op.stream, correlation: plan.correlation })
668
674
  );
669
675
  await store.truncate([
@@ -974,8 +980,7 @@ var runStorePropertyTck = (options) => {
974
980
  for (const { stream, count } of commits) {
975
981
  const before = expected.get(stream) ?? -1;
976
982
  const committed = await store.commit(
977
- stream,
978
- events(count),
983
+ [{ stream, msgs: events(count) }],
979
984
  make_meta({ stream })
980
985
  );
981
986
  committed.forEach((e, i) => {
@@ -1000,8 +1005,7 @@ var runStorePropertyTck = (options) => {
1000
1005
  await reset2();
1001
1006
  for (const { stream: stream2, count } of commits) {
1002
1007
  await store.commit(
1003
- stream2,
1004
- events(count),
1008
+ [{ stream: stream2, msgs: events(count) }],
1005
1009
  make_meta({ stream: stream2 })
1006
1010
  );
1007
1011
  }
@@ -1009,10 +1013,8 @@ var runStorePropertyTck = (options) => {
1009
1013
  const before = await collect(store, { stream, stream_exact: true });
1010
1014
  await (0, import_vitest8.expect)(
1011
1015
  store.commit(
1012
- stream,
1013
- [inc(1)],
1014
- make_meta({ stream }),
1015
- before.length
1016
+ [{ stream, msgs: [inc(1)], expectedVersion: before.length }],
1017
+ make_meta({ stream })
1016
1018
  )
1017
1019
  ).rejects.toThrow();
1018
1020
  const after = await collect(store, { stream, stream_exact: true });
@@ -1039,8 +1041,7 @@ var runStorePropertyTck = (options) => {
1039
1041
  for (const op of ops) {
1040
1042
  if (op.kind === "commit") {
1041
1043
  await store.commit(
1042
- op.stream,
1043
- [inc(1)],
1044
+ [{ stream: op.stream, msgs: [inc(1)] }],
1044
1045
  make_meta({ stream: op.stream })
1045
1046
  );
1046
1047
  } else if (op.kind === "claim") {
@@ -1082,8 +1083,7 @@ var runStorePropertyTck = (options) => {
1082
1083
  await store.subscribe(all.map((stream) => ({ stream })));
1083
1084
  for (const stream of commitsA) {
1084
1085
  await store.commit(
1085
- stream,
1086
- [inc(1)],
1086
+ [{ stream, msgs: [inc(1)] }],
1087
1087
  make_meta({ stream })
1088
1088
  );
1089
1089
  }
@@ -1095,8 +1095,7 @@ var runStorePropertyTck = (options) => {
1095
1095
  for (const l of acked1) watermark1.set(l.stream, l.at);
1096
1096
  for (const stream of commitsB) {
1097
1097
  await store.commit(
1098
- stream,
1099
- [inc(1)],
1098
+ [{ stream, msgs: [inc(1)] }],
1100
1099
  make_meta({ stream })
1101
1100
  );
1102
1101
  }
@@ -1122,8 +1121,7 @@ var runStorePropertyTck = (options) => {
1122
1121
  await store.subscribe(streams.map((stream) => ({ stream })));
1123
1122
  for (const stream of commits) {
1124
1123
  await store.commit(
1125
- stream,
1126
- [inc(1)],
1124
+ [{ stream, msgs: [inc(1)] }],
1127
1125
  make_meta({ stream })
1128
1126
  );
1129
1127
  }
@@ -1133,8 +1131,7 @@ var runStorePropertyTck = (options) => {
1133
1131
  const blockedSet = new Set(claimed.map((l) => l.stream));
1134
1132
  await store.subscribe([{ stream: "ctrl" }]);
1135
1133
  await store.commit(
1136
- "ctrl",
1137
- [inc(1)],
1134
+ [{ stream: "ctrl", msgs: [inc(1)] }],
1138
1135
  make_meta({ stream: "ctrl" })
1139
1136
  );
1140
1137
  await mark_all(store);
@@ -1155,7 +1152,6 @@ var runStoreTck = (options) => {
1155
1152
  (0, import_vitest9.describe)(`TCK / Store / ${options.name}`, () => {
1156
1153
  let store;
1157
1154
  const caps = { ...options.capabilities };
1158
- const is_literal = (source) => !/[\]^$.*+?()[{}|\\]/.test(source);
1159
1155
  const correlate = async (target = store) => {
1160
1156
  const rows = [];
1161
1157
  await target.query_streams(
@@ -1175,7 +1171,10 @@ var runStoreTck = (options) => {
1175
1171
  (e) => {
1176
1172
  mark = Math.max(mark, e.id);
1177
1173
  },
1178
- row.source !== void 0 ? { stream: row.source, stream_exact: is_literal(row.source) } : {}
1174
+ row.source !== void 0 ? {
1175
+ stream: row.source,
1176
+ stream_exact: (0, import_act2.is_literal_source)(row.source, row.stream)
1177
+ } : {}
1179
1178
  );
1180
1179
  if (mark > row.at)
1181
1180
  marks.push({
@@ -1211,14 +1210,12 @@ var runStoreTck = (options) => {
1211
1210
  await zero_config.seed();
1212
1211
  const s = `default-config-${uid()}`;
1213
1212
  const first = await zero_config.commit(
1214
- s,
1215
- [inc(1)],
1213
+ [{ stream: s, msgs: [inc(1)] }],
1216
1214
  make_meta({ stream: s })
1217
1215
  );
1218
1216
  (0, import_vitest9.expect)(first[0].version).toBe(0);
1219
1217
  const second = await zero_config.commit(
1220
- s,
1221
- [inc(2)],
1218
+ [{ stream: s, msgs: [inc(2)] }],
1222
1219
  make_meta({ stream: s })
1223
1220
  );
1224
1221
  (0, import_vitest9.expect)(second[0].version).toBe(1);
@@ -1234,12 +1231,371 @@ var runStoreTck = (options) => {
1234
1231
  });
1235
1232
  });
1236
1233
  }
1234
+ (0, import_vitest9.describe)("commit across streams", () => {
1235
+ const meta = make_meta({ stream: "across" });
1236
+ const count = (stream) => store.query(() => {
1237
+ }, { stream, stream_exact: true });
1238
+ (0, import_vitest9.it)("lands every append, each from its own head, with ids in append order", async () => {
1239
+ const a = `across-a-${uid()}`;
1240
+ const b = `across-b-${uid()}`;
1241
+ await store.commit(
1242
+ [{ stream: a, msgs: [inc(1)] }],
1243
+ meta
1244
+ );
1245
+ const committed = await store.commit(
1246
+ [
1247
+ { stream: a, msgs: [inc(2), inc(3)], expectedVersion: 0 },
1248
+ { stream: b, msgs: [dec(4)], expectedVersion: -1 }
1249
+ ],
1250
+ meta
1251
+ );
1252
+ (0, import_vitest9.expect)(committed.map((e) => [e.stream, e.version])).toEqual([
1253
+ [a, 1],
1254
+ [a, 2],
1255
+ [b, 0]
1256
+ ]);
1257
+ for (let i = 1; i < committed.length; i++)
1258
+ (0, import_vitest9.expect)(committed[i].id).toBeGreaterThan(committed[i - 1].id);
1259
+ const read = [];
1260
+ await store.query((e) => read.push(e.id), {
1261
+ after: committed[0].id - 1
1262
+ });
1263
+ (0, import_vitest9.expect)(read.filter((id) => committed.some((e) => e.id === id))).toEqual(
1264
+ committed.map((e) => e.id)
1265
+ );
1266
+ });
1267
+ (0, import_vitest9.it)("stores nothing when the first, a middle or the last append is stale, and names its stream", async () => {
1268
+ for (const at of [0, 1, 2]) {
1269
+ const streams = [0, 1, 2].map((i) => `across-stale-${i}-${uid()}`);
1270
+ for (const stream of streams)
1271
+ await store.commit(
1272
+ [{ stream, msgs: [inc(1)] }],
1273
+ meta
1274
+ );
1275
+ const refused = store.commit(
1276
+ streams.map((stream, i) => ({
1277
+ stream,
1278
+ msgs: [inc(2)],
1279
+ expectedVersion: i === at ? 5 : 0
1280
+ })),
1281
+ meta
1282
+ );
1283
+ await (0, import_vitest9.expect)(refused).rejects.toBeInstanceOf(import_act2.ConcurrencyError);
1284
+ await (0, import_vitest9.expect)(refused).rejects.toMatchObject({ stream: streams[at] });
1285
+ for (const stream of streams) (0, import_vitest9.expect)(await count(stream)).toBe(1);
1286
+ }
1287
+ });
1288
+ (0, import_vitest9.it)("refuses a stream named twice and an append with no events, storing nothing", async () => {
1289
+ const a = `across-twice-${uid()}`;
1290
+ const b = `across-empty-${uid()}`;
1291
+ await (0, import_vitest9.expect)(
1292
+ store.commit(
1293
+ [
1294
+ { stream: a, msgs: [inc(1)] },
1295
+ { stream: a, msgs: [inc(2)] }
1296
+ ],
1297
+ meta
1298
+ )
1299
+ ).rejects.toThrow(`stream "${a}" is named twice`);
1300
+ await (0, import_vitest9.expect)(
1301
+ store.commit(
1302
+ [
1303
+ { stream: a, msgs: [inc(1)] },
1304
+ { stream: b, msgs: [] }
1305
+ ],
1306
+ meta
1307
+ )
1308
+ ).rejects.toThrow(`the append to "${b}" has no events`);
1309
+ (0, import_vitest9.expect)(await count(a)).toBe(0);
1310
+ (0, import_vitest9.expect)(await count(b)).toBe(0);
1311
+ });
1312
+ (0, import_vitest9.it)("commits nothing for no appends", async () => {
1313
+ (0, import_vitest9.expect)(await store.commit([], meta)).toEqual([]);
1314
+ });
1315
+ (0, import_vitest9.it)("lands an unguarded append beside guarded ones", async () => {
1316
+ const a = `across-guarded-${uid()}`;
1317
+ const b = `across-free-${uid()}`;
1318
+ await store.commit(
1319
+ [{ stream: b, msgs: [inc(1)] }],
1320
+ meta
1321
+ );
1322
+ const committed = await store.commit(
1323
+ [
1324
+ { stream: a, msgs: [inc(1)], expectedVersion: -1 },
1325
+ { stream: b, msgs: [inc(2)] }
1326
+ ],
1327
+ meta
1328
+ );
1329
+ (0, import_vitest9.expect)(committed.map((e) => [e.stream, e.version])).toEqual([
1330
+ [a, 0],
1331
+ [b, 1]
1332
+ ]);
1333
+ });
1334
+ (0, import_vitest9.it)("stores every event of every append, whole", async () => {
1335
+ const a = `across-whole-a-${uid()}`;
1336
+ const b = `across-whole-b-${uid()}`;
1337
+ const across = make_meta({ stream: a });
1338
+ const committed = await store.commit(
1339
+ [
1340
+ { stream: a, msgs: [inc(1)] },
1341
+ { stream: b, msgs: [inc(2), dec(3)] }
1342
+ ],
1343
+ across
1344
+ );
1345
+ const shape = (e) => [
1346
+ e.name,
1347
+ e.data,
1348
+ e.meta
1349
+ ];
1350
+ const expected = [
1351
+ ["Incremented", { amount: 2 }, across],
1352
+ ["Decremented", { amount: 3 }, across]
1353
+ ];
1354
+ (0, import_vitest9.expect)(committed.filter((e) => e.stream === b).map(shape)).toEqual(
1355
+ expected
1356
+ );
1357
+ const stored = [];
1358
+ await store.query((e) => stored.push(e), {
1359
+ stream: b,
1360
+ stream_exact: true
1361
+ });
1362
+ (0, import_vitest9.expect)(stored.map(shape)).toEqual(expected);
1363
+ });
1364
+ import_vitest9.it.skipIf(!caps.pii_isolation)(
1365
+ "keeps the sensitive fields of a later append",
1366
+ async () => {
1367
+ const a = `across-pii-a-${uid()}`;
1368
+ const b = `across-pii-b-${uid()}`;
1369
+ await store.commit(
1370
+ [
1371
+ { stream: a, msgs: [inc(1)] },
1372
+ { stream: b, msgs: [{ ...inc(2), pii: { email: "e" } }] }
1373
+ ],
1374
+ make_meta({ stream: a })
1375
+ );
1376
+ const stored = [];
1377
+ await store.query((e) => stored.push(e.pii), {
1378
+ stream: b,
1379
+ stream_exact: true
1380
+ });
1381
+ (0, import_vitest9.expect)(stored).toEqual([{ email: "e" }]);
1382
+ }
1383
+ );
1384
+ (0, import_vitest9.it)("leaves nothing of the commit that lost a race, on any of its streams", async () => {
1385
+ const shared = `across-all-shared-${uid()}`;
1386
+ const [x, y] = [`across-all-x-${uid()}`, `across-all-y-${uid()}`];
1387
+ const race = (own) => store.commit(
1388
+ [
1389
+ { stream: own, msgs: [inc(1)], expectedVersion: -1 },
1390
+ { stream: shared, msgs: [inc(1)], expectedVersion: -1 }
1391
+ ],
1392
+ meta
1393
+ );
1394
+ const rs = await Promise.allSettled([race(x), race(y)]);
1395
+ (0, import_vitest9.expect)(rs.filter((r) => r.status === "fulfilled")).toHaveLength(1);
1396
+ (0, import_vitest9.expect)(await count(x) + await count(y)).toBe(1);
1397
+ });
1398
+ (0, import_vitest9.it)("lets one of two racing commits on a shared stream win, and both on disjoint streams", async () => {
1399
+ const shared = `across-shared-${uid()}`;
1400
+ const [x, y] = [`across-x-${uid()}`, `across-y-${uid()}`];
1401
+ const race = (own, other) => store.commit(
1402
+ [
1403
+ { stream: own, msgs: [inc(1)], expectedVersion: -1 },
1404
+ { stream: other, msgs: [inc(1)], expectedVersion: -1 }
1405
+ ],
1406
+ meta
1407
+ );
1408
+ const shared_race = await Promise.allSettled([
1409
+ race(x, shared),
1410
+ race(y, shared)
1411
+ ]);
1412
+ (0, import_vitest9.expect)(
1413
+ shared_race.filter((r2) => r2.status === "fulfilled")
1414
+ ).toHaveLength(1);
1415
+ const lost = shared_race.find((r2) => r2.status === "rejected");
1416
+ (0, import_vitest9.expect)(lost.reason).toBeInstanceOf(
1417
+ import_act2.ConcurrencyError
1418
+ );
1419
+ (0, import_vitest9.expect)(await count(shared)).toBe(1);
1420
+ const [p, q, r, t] = [0, 1, 2, 3].map((i) => `across-d${i}-${uid()}`);
1421
+ const disjoint = await Promise.allSettled([race(p, q), race(r, t)]);
1422
+ (0, import_vitest9.expect)(disjoint.every((d) => d.status === "fulfilled")).toBe(true);
1423
+ });
1424
+ });
1425
+ (0, import_vitest9.describe)("what a commit, guard or truncate must not get wrong", () => {
1426
+ const edge_uid = () => uid();
1427
+ const E = (data = { a: 1 }, pii) => ({
1428
+ name: "E",
1429
+ data,
1430
+ ...pii ? { pii } : {}
1431
+ });
1432
+ const edge_meta = (correlation = "m") => ({ correlation, causation: {} });
1433
+ const rows = async (query) => {
1434
+ const found = [];
1435
+ await store.query((e) => found.push(e), query);
1436
+ return found;
1437
+ };
1438
+ const one = (stream) => ({ stream, stream_exact: true });
1439
+ (0, import_vitest9.it)("names the FIRST stale stream when two are stale", async () => {
1440
+ const [a, b] = [`a-${edge_uid()}`, `b-${edge_uid()}`];
1441
+ const e = await store.commit(
1442
+ [
1443
+ { stream: a, msgs: [E()], expectedVersion: 3 },
1444
+ { stream: b, msgs: [E()], expectedVersion: 4 }
1445
+ ],
1446
+ edge_meta()
1447
+ ).catch((x) => x);
1448
+ (0, import_vitest9.expect)(e).toBeInstanceOf(import_act2.ConcurrencyError);
1449
+ (0, import_vitest9.expect)(e.stream).toBe(a);
1450
+ });
1451
+ (0, import_vitest9.it)("checks a stale expectedVersion of 0 inside a multi-stream commit", async () => {
1452
+ const [a, b] = [`a-${edge_uid()}`, `b-${edge_uid()}`];
1453
+ await store.commit(
1454
+ [{ stream: b, msgs: [E(), E()] }],
1455
+ edge_meta()
1456
+ );
1457
+ await (0, import_vitest9.expect)(
1458
+ store.commit(
1459
+ [
1460
+ { stream: a, msgs: [E()], expectedVersion: -1 },
1461
+ { stream: b, msgs: [E()], expectedVersion: 0 }
1462
+ ],
1463
+ edge_meta()
1464
+ )
1465
+ ).rejects.toBeInstanceOf(import_act2.ConcurrencyError);
1466
+ (0, import_vitest9.expect)(await rows(one(a))).toHaveLength(0);
1467
+ });
1468
+ (0, import_vitest9.it)("attaches meta to every append, not just the last", async () => {
1469
+ const ss = [0, 1, 2].map((i) => `m${i}-${edge_uid()}`);
1470
+ const c = `corr-${edge_uid()}`;
1471
+ await store.commit(
1472
+ ss.map((stream) => ({ stream, msgs: [E()] })),
1473
+ edge_meta(c)
1474
+ );
1475
+ for (const stream of ss)
1476
+ (0, import_vitest9.expect)((await rows(one(stream)))[0].meta.correlation).toBe(c);
1477
+ });
1478
+ (0, import_vitest9.it)("refuses a stream named twice when not adjacent", async () => {
1479
+ const [a, b] = [`a-${edge_uid()}`, `b-${edge_uid()}`];
1480
+ await (0, import_vitest9.expect)(
1481
+ store.commit(
1482
+ [
1483
+ { stream: a, msgs: [E()] },
1484
+ { stream: b, msgs: [E()] },
1485
+ { stream: a, msgs: [E()] }
1486
+ ],
1487
+ edge_meta()
1488
+ )
1489
+ ).rejects.toThrow("named twice");
1490
+ (0, import_vitest9.expect)(await rows(one(a))).toHaveLength(0);
1491
+ });
1492
+ (0, import_vitest9.it)("refuses a lone empty append (a version check alone)", async () => {
1493
+ const a = `a-${edge_uid()}`;
1494
+ await (0, import_vitest9.expect)(
1495
+ store.commit(
1496
+ [{ stream: a, msgs: [], expectedVersion: -1 }],
1497
+ edge_meta()
1498
+ )
1499
+ ).rejects.toThrow("no events");
1500
+ });
1501
+ (0, import_vitest9.it)("refuses an empty FIRST append", async () => {
1502
+ const [a, b] = [`a-${edge_uid()}`, `b-${edge_uid()}`];
1503
+ await (0, import_vitest9.expect)(
1504
+ store.commit(
1505
+ [
1506
+ { stream: a, msgs: [] },
1507
+ { stream: b, msgs: [E()] }
1508
+ ],
1509
+ edge_meta()
1510
+ )
1511
+ ).rejects.toThrow("no events");
1512
+ (0, import_vitest9.expect)(await rows(one(b))).toHaveLength(0);
1513
+ });
1514
+ (0, import_vitest9.it)("keeps the sensitive fields of the FIRST append", async () => {
1515
+ const [a, b] = [`a-${edge_uid()}`, `b-${edge_uid()}`];
1516
+ await store.commit(
1517
+ [
1518
+ { stream: a, msgs: [E({}, { email: "x" })] },
1519
+ { stream: b, msgs: [E()] }
1520
+ ],
1521
+ edge_meta()
1522
+ );
1523
+ (0, import_vitest9.expect)((await rows(one(a)))[0].pii).toEqual({ email: "x" });
1524
+ });
1525
+ (0, import_vitest9.it)("a guard below a trailing snapshot is stale", async () => {
1526
+ const a = `a-${edge_uid()}`;
1527
+ await store.commit([{ stream: a, msgs: [E()] }], edge_meta());
1528
+ await store.commit(
1529
+ [
1530
+ {
1531
+ stream: a,
1532
+ msgs: [{ name: import_act2.SNAP_EVENT, data: {} }],
1533
+ expectedVersion: 0
1534
+ }
1535
+ ],
1536
+ edge_meta()
1537
+ );
1538
+ await (0, import_vitest9.expect)(
1539
+ store.commit(
1540
+ [{ stream: a, msgs: [E()], expectedVersion: 0 }],
1541
+ edge_meta()
1542
+ )
1543
+ ).rejects.toBeInstanceOf(import_act2.ConcurrencyError);
1544
+ });
1545
+ (0, import_vitest9.it)("a guard below a trailing tombstone is stale (what stops a writer during a close)", async () => {
1546
+ const a = `a-${edge_uid()}`;
1547
+ await store.commit([{ stream: a, msgs: [E()] }], edge_meta());
1548
+ await store.commit(
1549
+ [
1550
+ {
1551
+ stream: a,
1552
+ msgs: [{ name: "__tombstone__", data: {} }],
1553
+ expectedVersion: 0
1554
+ }
1555
+ ],
1556
+ edge_meta()
1557
+ );
1558
+ await (0, import_vitest9.expect)(
1559
+ store.commit(
1560
+ [{ stream: a, msgs: [E()], expectedVersion: 0 }],
1561
+ edge_meta()
1562
+ )
1563
+ ).rejects.toBeInstanceOf(import_act2.ConcurrencyError);
1564
+ });
1565
+ (0, import_vitest9.it)("a commit after a full truncate continues from the seed at version 0", async () => {
1566
+ const a = `a-${edge_uid()}`;
1567
+ await store.commit(
1568
+ [{ stream: a, msgs: [E(), E(), E()] }],
1569
+ edge_meta()
1570
+ );
1571
+ await store.truncate([{ stream: a, snapshot: { n: 3 } }]);
1572
+ const [c] = await store.commit(
1573
+ [{ stream: a, msgs: [E()], expectedVersion: 0 }],
1574
+ edge_meta()
1575
+ );
1576
+ (0, import_vitest9.expect)(c.version).toBe(1);
1577
+ });
1578
+ (0, import_vitest9.it)("seeds a full truncate at version 0, carrying the meta it was given", async () => {
1579
+ const a = `a-${edge_uid()}`;
1580
+ await store.commit(
1581
+ [{ stream: a, msgs: [E(), E(), E()] }],
1582
+ edge_meta()
1583
+ );
1584
+ const c = `close-${edge_uid()}`;
1585
+ const r = await store.truncate([
1586
+ { stream: a, snapshot: { n: 3 }, meta: edge_meta(c) }
1587
+ ]);
1588
+ (0, import_vitest9.expect)(r.get(a).committed.version).toBe(0);
1589
+ const [seed] = await rows({ ...one(a), with_snaps: true });
1590
+ (0, import_vitest9.expect)(seed.version).toBe(0);
1591
+ (0, import_vitest9.expect)(seed.meta.correlation).toBe(c);
1592
+ });
1593
+ });
1237
1594
  (0, import_vitest9.describe)("commit", () => {
1238
1595
  (0, import_vitest9.it)("returns committed events with sequenced ids and versions", async () => {
1239
1596
  const s = `commit-seq-${uid()}`;
1240
1597
  const committed = await store.commit(
1241
- s,
1242
- [inc(1), inc(2), dec(3)],
1598
+ [{ stream: s, msgs: [inc(1), inc(2), dec(3)] }],
1243
1599
  make_meta({ stream: s })
1244
1600
  );
1245
1601
  (0, import_vitest9.expect)(committed).toHaveLength(3);
@@ -1256,8 +1612,7 @@ var runStoreTck = (options) => {
1256
1612
  const s = `commit-meta-${uid()}`;
1257
1613
  const correlation = `cor-${uid()}`;
1258
1614
  const committed = await store.commit(
1259
- s,
1260
- [inc(1)],
1615
+ [{ stream: s, msgs: [inc(1)] }],
1261
1616
  make_meta({ stream: s, correlation })
1262
1617
  );
1263
1618
  (0, import_vitest9.expect)(committed[0].stream).toBe(s);
@@ -1266,64 +1621,50 @@ var runStoreTck = (options) => {
1266
1621
  (0, import_vitest9.it)("throws ConcurrencyError when expectedVersion is wrong", async () => {
1267
1622
  const s = `commit-cc-${uid()}`;
1268
1623
  await store.commit(
1269
- s,
1270
- [inc(1)],
1624
+ [{ stream: s, msgs: [inc(1)] }],
1271
1625
  make_meta({ stream: s })
1272
1626
  );
1273
1627
  await store.commit(
1274
- s,
1275
- [inc(1)],
1276
- make_meta({ stream: s }),
1277
- 0
1628
+ [{ stream: s, msgs: [inc(1)], expectedVersion: 0 }],
1629
+ make_meta({ stream: s })
1278
1630
  );
1279
1631
  await (0, import_vitest9.expect)(
1280
- store.commit(s, [inc(1)], make_meta({ stream: s }), 0)
1632
+ store.commit(
1633
+ [{ stream: s, msgs: [inc(1)], expectedVersion: 0 }],
1634
+ make_meta({ stream: s })
1635
+ )
1281
1636
  ).rejects.toBeInstanceOf(import_act2.ConcurrencyError);
1282
1637
  });
1283
1638
  (0, import_vitest9.it)("treats expectedVersion -1 as an empty stream", async () => {
1284
1639
  const s = `commit-new-cc-${uid()}`;
1285
1640
  const [first] = await store.commit(
1286
- s,
1287
- [inc(1)],
1288
- make_meta({ stream: s }),
1289
- -1
1290
- );
1291
- (0, import_vitest9.expect)(first.version).toBe(0);
1292
- await (0, import_vitest9.expect)(
1293
- store.commit(s, [inc(1)], make_meta({ stream: s }), -1)
1294
- ).rejects.toBeInstanceOf(import_act2.ConcurrencyError);
1295
- });
1296
- (0, import_vitest9.it)("checks expectedVersion even when committing no events", async () => {
1297
- const s = `commit-empty-cc-${uid()}`;
1298
- await store.commit(
1299
- s,
1300
- [inc(1)],
1641
+ [{ stream: s, msgs: [inc(1)], expectedVersion: -1 }],
1301
1642
  make_meta({ stream: s })
1302
1643
  );
1644
+ (0, import_vitest9.expect)(first.version).toBe(0);
1303
1645
  await (0, import_vitest9.expect)(
1304
- store.commit(s, [], make_meta({ stream: s }), 5)
1646
+ store.commit(
1647
+ [{ stream: s, msgs: [inc(1)], expectedVersion: -1 }],
1648
+ make_meta({ stream: s })
1649
+ )
1305
1650
  ).rejects.toBeInstanceOf(import_act2.ConcurrencyError);
1306
- (0, import_vitest9.expect)(
1307
- await store.commit(s, [], make_meta({ stream: s }), 0)
1308
- ).toEqual([]);
1309
1651
  });
1310
1652
  (0, import_vitest9.it)("takes concurrent commits, reads and writes on one instance", async () => {
1311
1653
  const x = `commit-concurrent-x-${uid()}`;
1312
1654
  const y = `commit-concurrent-y-${uid()}`;
1313
1655
  const [first, second, other, read, subscribed] = await Promise.allSettled([
1314
1656
  store.commit(
1315
- x,
1316
- [inc(1)],
1317
- make_meta({ stream: x }),
1318
- -1
1657
+ [{ stream: x, msgs: [inc(1)], expectedVersion: -1 }],
1658
+ make_meta({ stream: x })
1319
1659
  ),
1320
1660
  store.commit(
1321
- x,
1322
- [inc(2)],
1323
- make_meta({ stream: x }),
1324
- -1
1661
+ [{ stream: x, msgs: [inc(2)], expectedVersion: -1 }],
1662
+ make_meta({ stream: x })
1663
+ ),
1664
+ store.commit(
1665
+ [{ stream: y, msgs: [inc(1)] }],
1666
+ make_meta({ stream: y })
1325
1667
  ),
1326
- store.commit(y, [inc(1)], make_meta({ stream: y })),
1327
1668
  store.query(() => {
1328
1669
  }, { stream: x, stream_exact: true }),
1329
1670
  // A source nothing writes, so later claims never see it.
@@ -1345,12 +1686,14 @@ var runStoreTck = (options) => {
1345
1686
  (0, import_vitest9.it)("preserves prior events when a concurrent commit is rejected", async () => {
1346
1687
  const s = `commit-cc-preserve-${uid()}`;
1347
1688
  await store.commit(
1348
- s,
1349
- [inc(1), inc(2)],
1689
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
1350
1690
  make_meta({ stream: s })
1351
1691
  );
1352
1692
  await (0, import_vitest9.expect)(
1353
- store.commit(s, [inc(3)], make_meta({ stream: s }), 0)
1693
+ store.commit(
1694
+ [{ stream: s, msgs: [inc(3)], expectedVersion: 0 }],
1695
+ make_meta({ stream: s })
1696
+ )
1354
1697
  ).rejects.toBeInstanceOf(import_act2.ConcurrencyError);
1355
1698
  const found = await collect(store, { stream: s, stream_exact: true });
1356
1699
  (0, import_vitest9.expect)(found).toHaveLength(2);
@@ -1362,13 +1705,11 @@ var runStoreTck = (options) => {
1362
1705
  const s2 = `q-s2-${uid()}`;
1363
1706
  const cor = `q-cor-${uid()}`;
1364
1707
  await store.commit(
1365
- s1,
1366
- [inc(1), dec(1)],
1708
+ [{ stream: s1, msgs: [inc(1), dec(1)] }],
1367
1709
  make_meta({ stream: s1, correlation: cor })
1368
1710
  );
1369
1711
  await store.commit(
1370
- s2,
1371
- [inc(2), dec(2), reset()],
1712
+ [{ stream: s2, msgs: [inc(2), dec(2), reset()] }],
1372
1713
  make_meta({ stream: s2, correlation: cor })
1373
1714
  );
1374
1715
  const by_stream = await collect(store, {
@@ -1394,8 +1735,7 @@ var runStoreTck = (options) => {
1394
1735
  (0, import_vitest9.it)("excludes snapshots when called with no query object", async () => {
1395
1736
  const s = `q-nofilter-${uid()}`;
1396
1737
  await store.commit(
1397
- s,
1398
- [{ name: import_act2.SNAP_EVENT, data: { count: 0 } }],
1738
+ [{ stream: s, msgs: [{ name: import_act2.SNAP_EVENT, data: { count: 0 } }] }],
1399
1739
  make_meta({ stream: s })
1400
1740
  );
1401
1741
  const names = [];
@@ -1407,18 +1747,15 @@ var runStoreTck = (options) => {
1407
1747
  (0, import_vitest9.it)("with_snaps resumes from the latest snapshot per stream", async () => {
1408
1748
  const s = `q-snap-${uid()}`;
1409
1749
  await store.commit(
1410
- s,
1411
- [inc(1), inc(1)],
1750
+ [{ stream: s, msgs: [inc(1), inc(1)] }],
1412
1751
  make_meta({ stream: s })
1413
1752
  );
1414
1753
  const [snap] = await store.commit(
1415
- s,
1416
- [{ name: import_act2.SNAP_EVENT, data: { count: 2 } }],
1754
+ [{ stream: s, msgs: [{ name: import_act2.SNAP_EVENT, data: { count: 2 } }] }],
1417
1755
  make_meta({ stream: s })
1418
1756
  );
1419
1757
  await store.commit(
1420
- s,
1421
- [inc(1), inc(1), inc(1)],
1758
+ [{ stream: s, msgs: [inc(1), inc(1), inc(1)] }],
1422
1759
  make_meta({ stream: s })
1423
1760
  );
1424
1761
  const from_snap = await collect(store, {
@@ -1439,8 +1776,7 @@ var runStoreTck = (options) => {
1439
1776
  (0, import_vitest9.expect)(after_snap).toHaveLength(3);
1440
1777
  const s2 = `q-nosnap-${uid()}`;
1441
1778
  await store.commit(
1442
- s2,
1443
- [inc(1), inc(1)],
1779
+ [{ stream: s2, msgs: [inc(1), inc(1)] }],
1444
1780
  make_meta({ stream: s2 })
1445
1781
  );
1446
1782
  const full = await collect(store, {
@@ -1453,18 +1789,15 @@ var runStoreTck = (options) => {
1453
1789
  (0, import_vitest9.it)("with_snaps applies the resume floor on a backward scan too", async () => {
1454
1790
  const s = `q-snap-back-${uid()}`;
1455
1791
  await store.commit(
1456
- s,
1457
- [inc(1), inc(1)],
1792
+ [{ stream: s, msgs: [inc(1), inc(1)] }],
1458
1793
  make_meta({ stream: s })
1459
1794
  );
1460
1795
  const [snap] = await store.commit(
1461
- s,
1462
- [{ name: import_act2.SNAP_EVENT, data: { count: 2 } }],
1796
+ [{ stream: s, msgs: [{ name: import_act2.SNAP_EVENT, data: { count: 2 } }] }],
1463
1797
  make_meta({ stream: s })
1464
1798
  );
1465
1799
  await store.commit(
1466
- s,
1467
- [inc(1), inc(1), inc(1)],
1800
+ [{ stream: s, msgs: [inc(1), inc(1), inc(1)] }],
1468
1801
  make_meta({ stream: s })
1469
1802
  );
1470
1803
  const backward = await collect(store, {
@@ -1478,8 +1811,7 @@ var runStoreTck = (options) => {
1478
1811
  (0, import_vitest9.expect)(backward.every((e) => e.id >= snap.id)).toBe(true);
1479
1812
  const s2 = `q-nosnap-back-${uid()}`;
1480
1813
  await store.commit(
1481
- s2,
1482
- [inc(1), inc(1)],
1814
+ [{ stream: s2, msgs: [inc(1), inc(1)] }],
1483
1815
  make_meta({ stream: s2 })
1484
1816
  );
1485
1817
  const full = await collect(store, {
@@ -1493,8 +1825,7 @@ var runStoreTck = (options) => {
1493
1825
  (0, import_vitest9.it)("supports backward traversal", async () => {
1494
1826
  const s = `q-back-${uid()}`;
1495
1827
  const committed = await store.commit(
1496
- s,
1497
- [inc(1), inc(2), inc(3)],
1828
+ [{ stream: s, msgs: [inc(1), inc(2), inc(3)] }],
1498
1829
  make_meta({ stream: s })
1499
1830
  );
1500
1831
  const forward = await collect(store, { stream: s, stream_exact: true });
@@ -1519,8 +1850,7 @@ var runStoreTck = (options) => {
1519
1850
  (0, import_vitest9.it)("after/before bound the id range", async () => {
1520
1851
  const s = `q-bounds-${uid()}`;
1521
1852
  const committed = await store.commit(
1522
- s,
1523
- [inc(1), inc(2), inc(3), inc(4)],
1853
+ [{ stream: s, msgs: [inc(1), inc(2), inc(3), inc(4)] }],
1524
1854
  make_meta({ stream: s })
1525
1855
  );
1526
1856
  const after_first = await collect(store, {
@@ -1543,8 +1873,7 @@ var runStoreTck = (options) => {
1543
1873
  (0, import_vitest9.it)("names:[] matches no events", async () => {
1544
1874
  const s = `q-names-empty-${uid()}`;
1545
1875
  await store.commit(
1546
- s,
1547
- [inc(1), dec(1)],
1876
+ [{ stream: s, msgs: [inc(1), dec(1)] }],
1548
1877
  make_meta({ stream: s })
1549
1878
  );
1550
1879
  const none = await collect(store, {
@@ -1559,8 +1888,7 @@ var runStoreTck = (options) => {
1559
1888
  (0, import_vitest9.it)("after:0 and before:0 are honored as id bounds", async () => {
1560
1889
  const s = `q-zero-bound-${uid()}`;
1561
1890
  await store.commit(
1562
- s,
1563
- [inc(1), inc(2), inc(3)],
1891
+ [{ stream: s, msgs: [inc(1), inc(2), inc(3)] }],
1564
1892
  make_meta({ stream: s })
1565
1893
  );
1566
1894
  (0, import_vitest9.expect)(
@@ -1591,8 +1919,7 @@ var runStoreTck = (options) => {
1591
1919
  (0, import_vitest9.it)("created_after/created_before filter by timestamp", async () => {
1592
1920
  const s = `q-ts-${uid()}`;
1593
1921
  const committed = await store.commit(
1594
- s,
1595
- [inc(1)],
1922
+ [{ stream: s, msgs: [inc(1)] }],
1596
1923
  make_meta({ stream: s })
1597
1924
  );
1598
1925
  const ts = committed[0].created.getTime();
@@ -1615,8 +1942,7 @@ var runStoreTck = (options) => {
1615
1942
  (0, import_vitest9.it)("created bounds exclude the event they are pinned to", async () => {
1616
1943
  const s = `q-ts-self-${uid()}`;
1617
1944
  const committed = await store.commit(
1618
- s,
1619
- [inc(1)],
1945
+ [{ stream: s, msgs: [inc(1)] }],
1620
1946
  make_meta({ stream: s })
1621
1947
  );
1622
1948
  const own = committed[0].created;
@@ -1636,8 +1962,7 @@ var runStoreTck = (options) => {
1636
1962
  (0, import_vitest9.it)("backward traversal short-circuits at `after` id boundary", async () => {
1637
1963
  const s = `q-back-after-${uid()}`;
1638
1964
  const committed = await store.commit(
1639
- s,
1640
- [inc(1), inc(2), inc(3)],
1965
+ [{ stream: s, msgs: [inc(1), inc(2), inc(3)] }],
1641
1966
  make_meta({ stream: s })
1642
1967
  );
1643
1968
  const got = await collect(store, {
@@ -1654,8 +1979,7 @@ var runStoreTck = (options) => {
1654
1979
  (0, import_vitest9.it)("backward traversal short-circuits at `created_after` boundary", async () => {
1655
1980
  const s = `q-back-cafter-${uid()}`;
1656
1981
  await store.commit(
1657
- s,
1658
- [inc(1)],
1982
+ [{ stream: s, msgs: [inc(1)] }],
1659
1983
  make_meta({ stream: s })
1660
1984
  );
1661
1985
  const future = new Date(Date.now() + 6e4);
@@ -1670,8 +1994,7 @@ var runStoreTck = (options) => {
1670
1994
  (0, import_vitest9.it)("backward traversal honors created_before by skipping newer events", async () => {
1671
1995
  const s = `q-back-ts-${uid()}`;
1672
1996
  const committed = await store.commit(
1673
- s,
1674
- [inc(1)],
1997
+ [{ stream: s, msgs: [inc(1)] }],
1675
1998
  make_meta()
1676
1999
  );
1677
2000
  const past = new Date(committed[0].created.getTime() - 6e4);
@@ -1688,13 +2011,11 @@ var runStoreTck = (options) => {
1688
2011
  const a = `q-exact-${tag}`;
1689
2012
  const b = `q-exact-${tag}-extra`;
1690
2013
  await store.commit(
1691
- a,
1692
- [inc(1)],
2014
+ [{ stream: a, msgs: [inc(1)] }],
1693
2015
  make_meta({ stream: a })
1694
2016
  );
1695
2017
  await store.commit(
1696
- b,
1697
- [inc(2)],
2018
+ [{ stream: b, msgs: [inc(2)] }],
1698
2019
  make_meta({ stream: b })
1699
2020
  );
1700
2021
  const exact = await collect(store, { stream: a, stream_exact: true });
@@ -1706,13 +2027,11 @@ var runStoreTck = (options) => {
1706
2027
  const inner = `qr-${tag}-inner`;
1707
2028
  const longer = `qr-${tag}-inner-extra`;
1708
2029
  await store.commit(
1709
- inner,
1710
- [inc(1)],
2030
+ [{ stream: inner, msgs: [inc(1)] }],
1711
2031
  make_meta({ stream: inner })
1712
2032
  );
1713
2033
  await store.commit(
1714
- longer,
1715
- [inc(2)],
2034
+ [{ stream: longer, msgs: [inc(2)] }],
1716
2035
  make_meta({ stream: longer })
1717
2036
  );
1718
2037
  const got = await collect(store, { stream: `qr-${tag}-inner` });
@@ -1723,13 +2042,11 @@ var runStoreTck = (options) => {
1723
2042
  const inner = `qr-${tag}-anchor`;
1724
2043
  const longer = `qr-${tag}-anchor-extra`;
1725
2044
  await store.commit(
1726
- inner,
1727
- [inc(1)],
2045
+ [{ stream: inner, msgs: [inc(1)] }],
1728
2046
  make_meta({ stream: inner })
1729
2047
  );
1730
2048
  await store.commit(
1731
- longer,
1732
- [inc(2)],
2049
+ [{ stream: longer, msgs: [inc(2)] }],
1733
2050
  make_meta({ stream: longer })
1734
2051
  );
1735
2052
  const got = await collect(store, { stream: `^qr-${tag}-anchor$` });
@@ -1742,18 +2059,15 @@ var runStoreTck = (options) => {
1742
2059
  const b = `qr-${tag}-pfx-b`;
1743
2060
  const other = `zz-${tag}-other`;
1744
2061
  await store.commit(
1745
- a,
1746
- [inc(1)],
2062
+ [{ stream: a, msgs: [inc(1)] }],
1747
2063
  make_meta({ stream: a })
1748
2064
  );
1749
2065
  await store.commit(
1750
- b,
1751
- [inc(2)],
2066
+ [{ stream: b, msgs: [inc(2)] }],
1752
2067
  make_meta({ stream: b })
1753
2068
  );
1754
2069
  await store.commit(
1755
- other,
1756
- [inc(3)],
2070
+ [{ stream: other, msgs: [inc(3)] }],
1757
2071
  make_meta({ stream: other })
1758
2072
  );
1759
2073
  const got = await collect(store, { stream: `^qr-${tag}-pfx-` });
@@ -1764,8 +2078,7 @@ var runStoreTck = (options) => {
1764
2078
  const seed_streams = async (streams) => {
1765
2079
  for (const s of streams)
1766
2080
  await store.commit(
1767
- s,
1768
- [inc(1)],
2081
+ [{ stream: s, msgs: [inc(1)] }],
1769
2082
  make_meta({ stream: s })
1770
2083
  );
1771
2084
  };
@@ -1929,13 +2242,22 @@ var runStoreTck = (options) => {
1929
2242
  const leases = await store.claim(5, 5, `w-${uid()}`, 5e3);
1930
2243
  (0, import_vitest9.expect)(leases.filter((l) => l.stream === s)).toHaveLength(0);
1931
2244
  });
2245
+ (0, import_vitest9.it)("takes a target reading itself, whatever its name holds", async () => {
2246
+ const name = `self-a+b(${uid()}`;
2247
+ await store.subscribe([{ stream: name, source: name }]);
2248
+ const found = [];
2249
+ await store.query_streams((p) => found.push(p.stream), {
2250
+ stream: name,
2251
+ stream_exact: true
2252
+ });
2253
+ (0, import_vitest9.expect)(found).toEqual([name]);
2254
+ });
1932
2255
  (0, import_vitest9.it)("claims a fresh subscription as soon as its first event is marked", async () => {
1933
2256
  const s = `fresh-first-${uid()}`;
1934
2257
  const src = `fresh-first-src-${uid()}`;
1935
2258
  await store.subscribe([{ stream: s, source: src }]);
1936
2259
  const [first] = await store.commit(
1937
- src,
1938
- [inc(1)],
2260
+ [{ stream: src, msgs: [inc(1)] }],
1939
2261
  make_meta({ stream: src })
1940
2262
  );
1941
2263
  await store.subscribe([{ stream: s, correlated_at: first.id }]);
@@ -2124,8 +2446,7 @@ var runStoreTck = (options) => {
2124
2446
  const src = `ws-unmarked-src-${uid()}`;
2125
2447
  await store.subscribe([{ stream: s, source: src }]);
2126
2448
  const [event] = await store.commit(
2127
- src,
2128
- [inc(1)],
2449
+ [{ stream: src, msgs: [inc(1)] }],
2129
2450
  make_meta({ stream: src })
2130
2451
  );
2131
2452
  (0, import_vitest9.expect)(await claimable(s)).toBe(false);
@@ -2157,8 +2478,7 @@ var runStoreTck = (options) => {
2157
2478
  const s = `claim-${uid()}`;
2158
2479
  await store.subscribe([{ stream: s }]);
2159
2480
  await store.commit(
2160
- s,
2161
- [inc(1)],
2481
+ [{ stream: s, msgs: [inc(1)] }],
2162
2482
  make_meta({ stream: s })
2163
2483
  );
2164
2484
  const by = `worker-${uid()}`;
@@ -2181,8 +2501,7 @@ var runStoreTck = (options) => {
2181
2501
  const other = `claim-other-${uid()}`;
2182
2502
  await store.subscribe([{ stream: s }]);
2183
2503
  await store.commit(
2184
- s,
2185
- [inc(1)],
2504
+ [{ stream: s, msgs: [inc(1)] }],
2186
2505
  make_meta({ stream: s })
2187
2506
  );
2188
2507
  await correlate();
@@ -2191,8 +2510,7 @@ var runStoreTck = (options) => {
2191
2510
  (0, import_vitest9.expect)(targetA).toBeDefined();
2192
2511
  await store.subscribe([{ stream: other }]);
2193
2512
  await store.commit(
2194
- other,
2195
- [inc(2)],
2513
+ [{ stream: other, msgs: [inc(2)] }],
2196
2514
  make_meta({ stream: other })
2197
2515
  );
2198
2516
  await correlate();
@@ -2205,8 +2523,7 @@ var runStoreTck = (options) => {
2205
2523
  const s = `claim-stolen-${uid()}`;
2206
2524
  await store.subscribe([{ stream: s }]);
2207
2525
  await store.commit(
2208
- s,
2209
- [inc(1), inc(2)],
2526
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
2210
2527
  make_meta({ stream: s })
2211
2528
  );
2212
2529
  await correlate();
@@ -2232,8 +2549,7 @@ var runStoreTck = (options) => {
2232
2549
  const s = `claim-dual-${uid()}`;
2233
2550
  await store.subscribe([{ stream: s }]);
2234
2551
  await store.commit(
2235
- s,
2236
- [inc(1), inc(2)],
2552
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
2237
2553
  make_meta({ stream: s })
2238
2554
  );
2239
2555
  await correlate();
@@ -2262,13 +2578,11 @@ var runStoreTck = (options) => {
2262
2578
  ]);
2263
2579
  for (const stream of highs)
2264
2580
  await fresh.commit(
2265
- stream,
2266
- [inc(1)],
2581
+ [{ stream, msgs: [inc(1)] }],
2267
2582
  make_meta({ stream })
2268
2583
  );
2269
2584
  await fresh.commit(
2270
- low,
2271
- [inc(1)],
2585
+ [{ stream: low, msgs: [inc(1)] }],
2272
2586
  make_meta({ stream: low })
2273
2587
  );
2274
2588
  const by = `w-${uid()}`;
@@ -2284,8 +2598,7 @@ var runStoreTck = (options) => {
2284
2598
  for (const l of leases) {
2285
2599
  await fresh.ack([{ ...l, at: l.at + 1 }]);
2286
2600
  await fresh.commit(
2287
- l.stream,
2288
- [inc(1)],
2601
+ [{ stream: l.stream, msgs: [inc(1)] }],
2289
2602
  make_meta({ stream: l.stream })
2290
2603
  );
2291
2604
  }
@@ -2299,8 +2612,7 @@ var runStoreTck = (options) => {
2299
2612
  const s = `claim-dedup-${uid()}`;
2300
2613
  await store.subscribe([{ stream: s }]);
2301
2614
  await store.commit(
2302
- s,
2303
- [inc(1)],
2615
+ [{ stream: s, msgs: [inc(1)] }],
2304
2616
  make_meta({ stream: s })
2305
2617
  );
2306
2618
  await correlate();
@@ -2313,13 +2625,11 @@ var runStoreTck = (options) => {
2313
2625
  const sibling = `ack-sibling-${uid()}`;
2314
2626
  await store.subscribe([{ stream: s }, { stream: sibling }]);
2315
2627
  await store.commit(
2316
- s,
2317
- [inc(1)],
2628
+ [{ stream: s, msgs: [inc(1)] }],
2318
2629
  make_meta({ stream: s })
2319
2630
  );
2320
2631
  await store.commit(
2321
- sibling,
2322
- [inc(2)],
2632
+ [{ stream: sibling, msgs: [inc(2)] }],
2323
2633
  make_meta({ stream: sibling })
2324
2634
  );
2325
2635
  await correlate();
@@ -2369,8 +2679,7 @@ var runStoreTck = (options) => {
2369
2679
  const s = `lease-retry-${uid()}`;
2370
2680
  await fresh.subscribe([{ stream: s }]);
2371
2681
  await fresh.commit(
2372
- s,
2373
- [inc(1)],
2682
+ [{ stream: s, msgs: [inc(1)] }],
2374
2683
  make_meta({ stream: s })
2375
2684
  );
2376
2685
  await correlate(fresh);
@@ -2395,8 +2704,7 @@ var runStoreTck = (options) => {
2395
2704
  const s = `lease-zero-${uid()}`;
2396
2705
  await fresh.subscribe([{ stream: s }]);
2397
2706
  const [committed] = await fresh.commit(
2398
- s,
2399
- [inc(1)],
2707
+ [{ stream: s, msgs: [inc(1)] }],
2400
2708
  make_meta({ stream: s })
2401
2709
  );
2402
2710
  await correlate(fresh);
@@ -2425,8 +2733,7 @@ var runStoreTck = (options) => {
2425
2733
  const s = `lease-lag-${uid()}`;
2426
2734
  await fresh.subscribe([{ stream: s }]);
2427
2735
  await fresh.commit(
2428
- s,
2429
- [inc(1)],
2736
+ [{ stream: s, msgs: [inc(1)] }],
2430
2737
  make_meta({ stream: s })
2431
2738
  );
2432
2739
  await correlate(fresh);
@@ -2555,8 +2862,7 @@ var runStoreTck = (options) => {
2555
2862
  await fresh.subscribe(streams.map((stream) => ({ stream })));
2556
2863
  for (const stream of streams) {
2557
2864
  await fresh.commit(
2558
- stream,
2559
- [inc(1)],
2865
+ [{ stream, msgs: [inc(1)] }],
2560
2866
  make_meta({ stream })
2561
2867
  );
2562
2868
  }
@@ -2628,8 +2934,7 @@ var runStoreTck = (options) => {
2628
2934
  const s = `block-${uid()}`;
2629
2935
  await store.subscribe([{ stream: s }]);
2630
2936
  await store.commit(
2631
- s,
2632
- [inc(1)],
2937
+ [{ stream: s, msgs: [inc(1)] }],
2633
2938
  make_meta({ stream: s })
2634
2939
  );
2635
2940
  await correlate();
@@ -2651,8 +2956,7 @@ var runStoreTck = (options) => {
2651
2956
  const s = `block-returns-${uid()}`;
2652
2957
  await store.subscribe([{ stream: s }]);
2653
2958
  await store.commit(
2654
- s,
2655
- [inc(1)],
2959
+ [{ stream: s, msgs: [inc(1)] }],
2656
2960
  make_meta({ stream: s })
2657
2961
  );
2658
2962
  await correlate();
@@ -2677,8 +2981,7 @@ var runStoreTck = (options) => {
2677
2981
  const s = `block-wrong-${uid()}`;
2678
2982
  await store.subscribe([{ stream: s }]);
2679
2983
  await store.commit(
2680
- s,
2681
- [inc(1)],
2984
+ [{ stream: s, msgs: [inc(1)] }],
2682
2985
  make_meta({ stream: s })
2683
2986
  );
2684
2987
  await correlate();
@@ -2696,8 +2999,7 @@ var runStoreTck = (options) => {
2696
2999
  const s = `block-twice-${uid()}`;
2697
3000
  await store.subscribe([{ stream: s }]);
2698
3001
  await store.commit(
2699
- s,
2700
- [inc(1)],
3002
+ [{ stream: s, msgs: [inc(1)] }],
2701
3003
  make_meta({ stream: s })
2702
3004
  );
2703
3005
  await correlate();
@@ -2722,8 +3024,7 @@ var runStoreTck = (options) => {
2722
3024
  await store.subscribe([{ stream: s }, { stream: ctl }]);
2723
3025
  for (const st of [s, ctl])
2724
3026
  await store.commit(
2725
- st,
2726
- [inc(1)],
3027
+ [{ stream: st, msgs: [inc(1)] }],
2727
3028
  make_meta({ stream: st })
2728
3029
  );
2729
3030
  (0, import_vitest9.expect)(await store.defer([s], Date.now() + 36e5)).toBe(1);
@@ -2736,8 +3037,7 @@ var runStoreTck = (options) => {
2736
3037
  const s = `defer-clock-${uid()}`;
2737
3038
  await store.subscribe([{ stream: s }]);
2738
3039
  await store.commit(
2739
- s,
2740
- [inc(1)],
3040
+ [{ stream: s, msgs: [inc(1)] }],
2741
3041
  make_meta({ stream: s })
2742
3042
  );
2743
3043
  (0, import_vitest9.expect)(await store.defer([s], Date.now() + 6e4)).toBe(1);
@@ -2760,8 +3060,7 @@ var runStoreTck = (options) => {
2760
3060
  const s = `defer-past-${uid()}`;
2761
3061
  await store.subscribe([{ stream: s }]);
2762
3062
  await store.commit(
2763
- s,
2764
- [inc(1)],
3063
+ [{ stream: s, msgs: [inc(1)] }],
2765
3064
  make_meta({ stream: s })
2766
3065
  );
2767
3066
  (0, import_vitest9.expect)(await store.defer([s], Date.now() - 1e3)).toBe(1);
@@ -2777,8 +3076,7 @@ var runStoreTck = (options) => {
2777
3076
  const s = `defer-retry-${uid()}`;
2778
3077
  await store.subscribe([{ stream: s }]);
2779
3078
  await store.commit(
2780
- s,
2781
- [inc(1)],
3079
+ [{ stream: s, msgs: [inc(1)] }],
2782
3080
  make_meta({ stream: s })
2783
3081
  );
2784
3082
  await store.defer([s], Date.now() + 36e5);
@@ -2800,8 +3098,7 @@ var runStoreTck = (options) => {
2800
3098
  const s = `defer-reset-${uid()}`;
2801
3099
  await store.subscribe([{ stream: s }]);
2802
3100
  await store.commit(
2803
- s,
2804
- [inc(1)],
3101
+ [{ stream: s, msgs: [inc(1)] }],
2805
3102
  make_meta({ stream: s })
2806
3103
  );
2807
3104
  await store.defer([s], Date.now() + 36e5);
@@ -2816,8 +3113,7 @@ var runStoreTck = (options) => {
2816
3113
  await store.subscribe([{ stream: deferred }, { stream: plain }]);
2817
3114
  for (const s of [deferred, plain])
2818
3115
  await store.commit(
2819
- s,
2820
- [inc(1)],
3116
+ [{ stream: s, msgs: [inc(1)] }],
2821
3117
  make_meta({ stream: s })
2822
3118
  );
2823
3119
  const due = Date.now() + 36e5;
@@ -2839,15 +3135,13 @@ var runStoreTck = (options) => {
2839
3135
  await store.subscribe([{ stream: a }, { stream: b }]);
2840
3136
  for (const s of [a, b])
2841
3137
  await store.commit(
2842
- s,
2843
- [inc(1)],
3138
+ [{ stream: s, msgs: [inc(1)] }],
2844
3139
  make_meta({ stream: s })
2845
3140
  );
2846
3141
  const ctl = `defer-filterctl-${tag}`;
2847
3142
  await store.subscribe([{ stream: ctl }]);
2848
3143
  await store.commit(
2849
- ctl,
2850
- [inc(1)],
3144
+ [{ stream: ctl, msgs: [inc(1)] }],
2851
3145
  make_meta({ stream: ctl })
2852
3146
  );
2853
3147
  const n = await store.defer(
@@ -2869,8 +3163,7 @@ var runStoreTck = (options) => {
2869
3163
  const s = `defer-dup-${uid()}`;
2870
3164
  await store.subscribe([{ stream: s }]);
2871
3165
  await store.commit(
2872
- s,
2873
- [inc(1)],
3166
+ [{ stream: s, msgs: [inc(1)] }],
2874
3167
  make_meta({ stream: s })
2875
3168
  );
2876
3169
  (0, import_vitest9.expect)(await store.defer([s, s], Date.now() + 36e5)).toBe(1);
@@ -2882,13 +3175,11 @@ var runStoreTck = (options) => {
2882
3175
  const ctl = `ackdefer-ctl-${uid()}`;
2883
3176
  await store.subscribe([{ stream: s }, { stream: ctl }]);
2884
3177
  await store.commit(
2885
- s,
2886
- [inc(1)],
3178
+ [{ stream: s, msgs: [inc(1)] }],
2887
3179
  make_meta({ stream: s })
2888
3180
  );
2889
3181
  const [ctl_head] = await store.commit(
2890
- ctl,
2891
- [inc(1)],
3182
+ [{ stream: ctl, msgs: [inc(1)] }],
2892
3183
  make_meta({ stream: ctl })
2893
3184
  );
2894
3185
  await correlate();
@@ -2902,8 +3193,7 @@ var runStoreTck = (options) => {
2902
3193
  (0, import_vitest9.expect)(acked.find((l) => l.stream === ctl)).toBeDefined();
2903
3194
  (0, import_vitest9.expect)(acked.find((l) => l.stream === s)).toBeUndefined();
2904
3195
  await store.commit(
2905
- ctl,
2906
- [inc(2)],
3196
+ [{ stream: ctl, msgs: [inc(2)] }],
2907
3197
  make_meta({ stream: ctl })
2908
3198
  );
2909
3199
  await correlate();
@@ -2915,8 +3205,7 @@ var runStoreTck = (options) => {
2915
3205
  const s = `ackdefer-past-${uid()}`;
2916
3206
  await store.subscribe([{ stream: s }]);
2917
3207
  await store.commit(
2918
- s,
2919
- [inc(1)],
3208
+ [{ stream: s, msgs: [inc(1)] }],
2920
3209
  make_meta({ stream: s })
2921
3210
  );
2922
3211
  await correlate();
@@ -2935,8 +3224,7 @@ var runStoreTck = (options) => {
2935
3224
  const s = `ackdefer-retry-${uid()}`;
2936
3225
  await store.subscribe([{ stream: s }]);
2937
3226
  await store.commit(
2938
- s,
2939
- [inc(1)],
3227
+ [{ stream: s, msgs: [inc(1)] }],
2940
3228
  make_meta({ stream: s })
2941
3229
  );
2942
3230
  await correlate();
@@ -2955,13 +3243,11 @@ var runStoreTck = (options) => {
2955
3243
  const s = `ackadvance-${uid()}`;
2956
3244
  await store.subscribe([{ stream: s }]);
2957
3245
  const [e1] = await store.commit(
2958
- s,
2959
- [inc(1)],
3246
+ [{ stream: s, msgs: [inc(1)] }],
2960
3247
  make_meta({ stream: s })
2961
3248
  );
2962
3249
  await store.commit(
2963
- s,
2964
- [inc(1)],
3250
+ [{ stream: s, msgs: [inc(1)] }],
2965
3251
  make_meta({ stream: s })
2966
3252
  );
2967
3253
  await correlate();
@@ -2983,8 +3269,7 @@ var runStoreTck = (options) => {
2983
3269
  const s = `ackdefer-owner-${uid()}`;
2984
3270
  await store.subscribe([{ stream: s }]);
2985
3271
  await store.commit(
2986
- s,
2987
- [inc(1)],
3272
+ [{ stream: s, msgs: [inc(1)] }],
2988
3273
  make_meta({ stream: s })
2989
3274
  );
2990
3275
  await correlate();
@@ -3002,8 +3287,7 @@ var runStoreTck = (options) => {
3002
3287
  const s = `reset-${uid()}`;
3003
3288
  await store.subscribe([{ stream: s }]);
3004
3289
  await store.commit(
3005
- s,
3006
- [inc(1)],
3290
+ [{ stream: s, msgs: [inc(1)] }],
3007
3291
  make_meta({ stream: s })
3008
3292
  );
3009
3293
  await correlate();
@@ -3022,8 +3306,7 @@ var runStoreTck = (options) => {
3022
3306
  const s = `reset-blk-${uid()}`;
3023
3307
  await store.subscribe([{ stream: s }]);
3024
3308
  await store.commit(
3025
- s,
3026
- [inc(1)],
3309
+ [{ stream: s, msgs: [inc(1)] }],
3027
3310
  make_meta({ stream: s })
3028
3311
  );
3029
3312
  await correlate();
@@ -3045,8 +3328,7 @@ var runStoreTck = (options) => {
3045
3328
  const s = `reset-dup-${uid()}`;
3046
3329
  await store.subscribe([{ stream: s }]);
3047
3330
  await store.commit(
3048
- s,
3049
- [inc(1)],
3331
+ [{ stream: s, msgs: [inc(1)] }],
3050
3332
  make_meta({ stream: s })
3051
3333
  );
3052
3334
  (0, import_vitest9.expect)(await store.reset([s, s])).toBe(1);
@@ -3057,13 +3339,11 @@ var runStoreTck = (options) => {
3057
3339
  const s = `unblock-${uid()}`;
3058
3340
  await store.subscribe([{ stream: s }]);
3059
3341
  await store.commit(
3060
- s,
3061
- [inc(1)],
3342
+ [{ stream: s, msgs: [inc(1)] }],
3062
3343
  make_meta({ stream: s })
3063
3344
  );
3064
3345
  await store.commit(
3065
- s,
3066
- [inc(2)],
3346
+ [{ stream: s, msgs: [inc(2)] }],
3067
3347
  make_meta({ stream: s })
3068
3348
  );
3069
3349
  await correlate();
@@ -3096,8 +3376,7 @@ var runStoreTck = (options) => {
3096
3376
  const s = `unblock-noop-${uid()}`;
3097
3377
  await store.subscribe([{ stream: s }]);
3098
3378
  await store.commit(
3099
- s,
3100
- [inc(1)],
3379
+ [{ stream: s, msgs: [inc(1)] }],
3101
3380
  make_meta({ stream: s })
3102
3381
  );
3103
3382
  (0, import_vitest9.expect)(await store.unblock([s])).toBe(0);
@@ -3111,13 +3390,11 @@ var runStoreTck = (options) => {
3111
3390
  const s2 = `unblock-mix-b-${uid()}`;
3112
3391
  await store.subscribe([{ stream: s1 }, { stream: s2 }]);
3113
3392
  await store.commit(
3114
- s1,
3115
- [inc(1)],
3393
+ [{ stream: s1, msgs: [inc(1)] }],
3116
3394
  make_meta({ stream: s1 })
3117
3395
  );
3118
3396
  await store.commit(
3119
- s2,
3120
- [inc(1)],
3397
+ [{ stream: s2, msgs: [inc(1)] }],
3121
3398
  make_meta({ stream: s2 })
3122
3399
  );
3123
3400
  await correlate();
@@ -3135,18 +3412,15 @@ var runStoreTck = (options) => {
3135
3412
  const s3 = `other-${tag}`;
3136
3413
  await store.subscribe([{ stream: s1 }, { stream: s2 }, { stream: s3 }]);
3137
3414
  await store.commit(
3138
- s1,
3139
- [inc(1)],
3415
+ [{ stream: s1, msgs: [inc(1)] }],
3140
3416
  make_meta({ stream: s1 })
3141
3417
  );
3142
3418
  await store.commit(
3143
- s2,
3144
- [inc(1)],
3419
+ [{ stream: s2, msgs: [inc(1)] }],
3145
3420
  make_meta({ stream: s2 })
3146
3421
  );
3147
3422
  await store.commit(
3148
- s3,
3149
- [inc(1)],
3423
+ [{ stream: s3, msgs: [inc(1)] }],
3150
3424
  make_meta({ stream: s3 })
3151
3425
  );
3152
3426
  await correlate();
@@ -3174,13 +3448,11 @@ var runStoreTck = (options) => {
3174
3448
  const s2 = `unblock-empty-${tag}-b`;
3175
3449
  await store.subscribe([{ stream: s1 }, { stream: s2 }]);
3176
3450
  await store.commit(
3177
- s1,
3178
- [inc(1)],
3451
+ [{ stream: s1, msgs: [inc(1)] }],
3179
3452
  make_meta({ stream: s1 })
3180
3453
  );
3181
3454
  await store.commit(
3182
- s2,
3183
- [inc(1)],
3455
+ [{ stream: s2, msgs: [inc(1)] }],
3184
3456
  make_meta({ stream: s2 })
3185
3457
  );
3186
3458
  await correlate();
@@ -3200,8 +3472,7 @@ var runStoreTck = (options) => {
3200
3472
  const s = `unblock-blocked-false-${tag}`;
3201
3473
  await store.subscribe([{ stream: s }]);
3202
3474
  await store.commit(
3203
- s,
3204
- [inc(1)],
3475
+ [{ stream: s, msgs: [inc(1)] }],
3205
3476
  make_meta({ stream: s })
3206
3477
  );
3207
3478
  (0, import_vitest9.expect)(
@@ -3224,18 +3495,15 @@ var runStoreTck = (options) => {
3224
3495
  { stream: other }
3225
3496
  ]);
3226
3497
  await store.commit(
3227
- s1,
3228
- [inc(1)],
3498
+ [{ stream: s1, msgs: [inc(1)] }],
3229
3499
  make_meta({ stream: s1 })
3230
3500
  );
3231
3501
  await store.commit(
3232
- s2,
3233
- [inc(1)],
3502
+ [{ stream: s2, msgs: [inc(1)] }],
3234
3503
  make_meta({ stream: s2 })
3235
3504
  );
3236
3505
  await store.commit(
3237
- other,
3238
- [inc(1)],
3506
+ [{ stream: other, msgs: [inc(1)] }],
3239
3507
  make_meta({ stream: other })
3240
3508
  );
3241
3509
  await correlate();
@@ -3266,13 +3534,11 @@ var runStoreTck = (options) => {
3266
3534
  const s2 = `reset-blocked-${tag}-fine`;
3267
3535
  await store.subscribe([{ stream: s1 }, { stream: s2 }]);
3268
3536
  await store.commit(
3269
- s1,
3270
- [inc(1)],
3537
+ [{ stream: s1, msgs: [inc(1)] }],
3271
3538
  make_meta({ stream: s1 })
3272
3539
  );
3273
3540
  await store.commit(
3274
- s2,
3275
- [inc(1)],
3541
+ [{ stream: s2, msgs: [inc(1)] }],
3276
3542
  make_meta({ stream: s2 })
3277
3543
  );
3278
3544
  await correlate();
@@ -3351,8 +3617,7 @@ var runStoreTck = (options) => {
3351
3617
  const ok = `pri-${tag}-ok`;
3352
3618
  const bad = `pri-${tag}-bad`;
3353
3619
  await fresh.commit(
3354
- src,
3355
- [inc(1)],
3620
+ [{ stream: src, msgs: [inc(1)] }],
3356
3621
  make_meta({ stream: src })
3357
3622
  );
3358
3623
  await fresh.subscribe([
@@ -3398,8 +3663,7 @@ var runStoreTck = (options) => {
3398
3663
  const tag = uid();
3399
3664
  const src = `pri-src-${tag}`;
3400
3665
  await fresh.commit(
3401
- src,
3402
- [inc(1), inc(2)],
3666
+ [{ stream: src, msgs: [inc(1), inc(2)] }],
3403
3667
  make_meta({ stream: src })
3404
3668
  );
3405
3669
  await fresh.subscribe([
@@ -3497,13 +3761,11 @@ var runStoreTck = (options) => {
3497
3761
  const sub_default = `lane-claim-def-${tag}`;
3498
3762
  const sub_slow = `lane-claim-slow-${tag}`;
3499
3763
  await store.commit(
3500
- src1,
3501
- [inc(1)],
3764
+ [{ stream: src1, msgs: [inc(1)] }],
3502
3765
  make_meta({ stream: src1 })
3503
3766
  );
3504
3767
  await store.commit(
3505
- src2,
3506
- [inc(1)],
3768
+ [{ stream: src2, msgs: [inc(1)] }],
3507
3769
  make_meta({ stream: src2 })
3508
3770
  );
3509
3771
  await store.subscribe([
@@ -3570,8 +3832,7 @@ var runStoreTck = (options) => {
3570
3832
  const a = `lane-reset-a-${tag}`;
3571
3833
  const b = `lane-reset-b-${tag}`;
3572
3834
  await store.commit(
3573
- src,
3574
- [inc(1)],
3835
+ [{ stream: src, msgs: [inc(1)] }],
3575
3836
  make_meta({ stream: src })
3576
3837
  );
3577
3838
  await store.subscribe([
@@ -3600,8 +3861,7 @@ var runStoreTck = (options) => {
3600
3861
  const a = `lane-ub-a-${tag}`;
3601
3862
  const b = `lane-ub-b-${tag}`;
3602
3863
  await store.commit(
3603
- src,
3604
- [inc(1)],
3864
+ [{ stream: src, msgs: [inc(1)] }],
3605
3865
  make_meta({ stream: src })
3606
3866
  );
3607
3867
  await store.subscribe([
@@ -3630,8 +3890,7 @@ var runStoreTck = (options) => {
3630
3890
  const tag = uid();
3631
3891
  const long = `${tag}-${"x".repeat(140)}`;
3632
3892
  await store.commit(
3633
- long,
3634
- [inc(1)],
3893
+ [{ stream: long, msgs: [inc(1)] }],
3635
3894
  make_meta({ stream: long })
3636
3895
  );
3637
3896
  await store.subscribe([
@@ -3656,8 +3915,7 @@ var runStoreTck = (options) => {
3656
3915
  for (const s of [restarted, retired]) {
3657
3916
  await store.subscribe([{ stream: s }]);
3658
3917
  await store.commit(
3659
- s,
3660
- [inc(1)],
3918
+ [{ stream: s, msgs: [inc(1)] }],
3661
3919
  make_meta({ stream: s })
3662
3920
  );
3663
3921
  }
@@ -3681,8 +3939,7 @@ var runStoreTck = (options) => {
3681
3939
  const lane = `inert-lane-${uid()}`;
3682
3940
  const s = `trunc-inert-${uid()}`;
3683
3941
  const [e] = await store.commit(
3684
- s,
3685
- [inc(1)],
3942
+ [{ stream: s, msgs: [inc(1)] }],
3686
3943
  make_meta({ stream: s })
3687
3944
  );
3688
3945
  await store.subscribe([{ stream: s, correlated_at: e.id, lane }]);
@@ -3697,8 +3954,7 @@ var runStoreTck = (options) => {
3697
3954
  (0, import_vitest9.it)("seeds a tombstone when no snapshot is provided", async () => {
3698
3955
  const s = `trunc-tomb-${uid()}`;
3699
3956
  await store.commit(
3700
- s,
3701
- [inc(1), inc(2)],
3957
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
3702
3958
  make_meta({ stream: s })
3703
3959
  );
3704
3960
  const result = await store.truncate([{ stream: s }]);
@@ -3718,8 +3974,7 @@ var runStoreTck = (options) => {
3718
3974
  (0, import_vitest9.it)("seeds a snapshot when one is provided", async () => {
3719
3975
  const s = `trunc-snap-${uid()}`;
3720
3976
  await store.commit(
3721
- s,
3722
- [inc(1)],
3977
+ [{ stream: s, msgs: [inc(1)] }],
3723
3978
  make_meta({ stream: s })
3724
3979
  );
3725
3980
  const result = await store.truncate([
@@ -3742,8 +3997,7 @@ var runStoreTck = (options) => {
3742
3997
  (0, import_vitest9.it)("leaves exactly one seed when a stream is listed twice", async () => {
3743
3998
  const s = `trunc-dup-${uid()}`;
3744
3999
  await store.commit(
3745
- s,
3746
- [inc(1), inc(2)],
4000
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
3747
4001
  make_meta({ stream: s })
3748
4002
  );
3749
4003
  const result = await store.truncate([
@@ -3764,13 +4018,11 @@ var runStoreTck = (options) => {
3764
4018
  (0, import_vitest9.it)("applies windowed targets before full ones", async () => {
3765
4019
  const s = `trunc-mix-${uid()}`;
3766
4020
  await store.commit(
3767
- s,
3768
- [inc(1)],
4021
+ [{ stream: s, msgs: [inc(1)] }],
3769
4022
  make_meta({ stream: s })
3770
4023
  );
3771
4024
  await store.commit(
3772
- s,
3773
- [inc(2)],
4025
+ [{ stream: s, msgs: [inc(2)] }],
3774
4026
  make_meta({ stream: s })
3775
4027
  );
3776
4028
  await (0, import_act2.sleep)(5);
@@ -3807,28 +4059,23 @@ var runStoreTck = (options) => {
3807
4059
  (0, import_vitest9.describe)("windowed (before boundary)", () => {
3808
4060
  async function seed_windowed(s) {
3809
4061
  await store.commit(
3810
- s,
3811
- [inc(1), inc(1)],
4062
+ [{ stream: s, msgs: [inc(1), inc(1)] }],
3812
4063
  make_meta({ stream: s })
3813
4064
  );
3814
4065
  const [snap_a] = await store.commit(
3815
- s,
3816
- [{ name: import_act2.SNAP_EVENT, data: { count: 2 } }],
4066
+ [{ stream: s, msgs: [{ name: import_act2.SNAP_EVENT, data: { count: 2 } }] }],
3817
4067
  make_meta({ stream: s })
3818
4068
  );
3819
4069
  await store.commit(
3820
- s,
3821
- [inc(1)],
4070
+ [{ stream: s, msgs: [inc(1)] }],
3822
4071
  make_meta({ stream: s })
3823
4072
  );
3824
4073
  const [snap_b] = await store.commit(
3825
- s,
3826
- [{ name: import_act2.SNAP_EVENT, data: { count: 3 } }],
4074
+ [{ stream: s, msgs: [{ name: import_act2.SNAP_EVENT, data: { count: 3 } }] }],
3827
4075
  make_meta({ stream: s })
3828
4076
  );
3829
4077
  await store.commit(
3830
- s,
3831
- [inc(1)],
4078
+ [{ stream: s, msgs: [inc(1)] }],
3832
4079
  make_meta({ stream: s })
3833
4080
  );
3834
4081
  return { snap_a, snap_b };
@@ -3898,8 +4145,7 @@ var runStoreTck = (options) => {
3898
4145
  (0, import_vitest9.it)("no-ops when no snapshot qualifies \u2014 stream absent from the result, events untouched", async () => {
3899
4146
  const s = `trunc-win-noop-${uid()}`;
3900
4147
  await store.commit(
3901
- s,
3902
- [inc(1), inc(1)],
4148
+ [{ stream: s, msgs: [inc(1), inc(1)] }],
3903
4149
  make_meta({ stream: s })
3904
4150
  );
3905
4151
  const none = await store.truncate([
@@ -3907,8 +4153,7 @@ var runStoreTck = (options) => {
3907
4153
  ]);
3908
4154
  (0, import_vitest9.expect)(none.has(s)).toBe(false);
3909
4155
  await store.commit(
3910
- s,
3911
- [{ name: import_act2.SNAP_EVENT, data: { count: 2 } }],
4156
+ [{ stream: s, msgs: [{ name: import_act2.SNAP_EVENT, data: { count: 2 } }] }],
3912
4157
  make_meta({ stream: s })
3913
4158
  );
3914
4159
  const too_early = await store.truncate([
@@ -3946,8 +4191,7 @@ var runStoreTck = (options) => {
3946
4191
  const f = `trunc-win-mix-f-${uid()}`;
3947
4192
  const { snap_b } = await seed_windowed(w);
3948
4193
  await store.commit(
3949
- f,
3950
- [inc(1)],
4194
+ [{ stream: f, msgs: [inc(1)] }],
3951
4195
  make_meta({ stream: f })
3952
4196
  );
3953
4197
  const result = await store.truncate([
@@ -3967,8 +4211,7 @@ var runStoreTck = (options) => {
3967
4211
  { stream: s, before: new Date(Date.now() + 6e4) }
3968
4212
  ]);
3969
4213
  const [next] = await store.commit(
3970
- s,
3971
- [inc(1)],
4214
+ [{ stream: s, msgs: [inc(1)] }],
3972
4215
  make_meta({ stream: s })
3973
4216
  );
3974
4217
  (0, import_vitest9.expect)(next.version).toBe(6);
@@ -4106,8 +4349,7 @@ var runStoreTck = (options) => {
4106
4349
  const sibling = `qb-${tag}-other`;
4107
4350
  await store.subscribe([{ stream: s }, { stream: sibling }]);
4108
4351
  await store.commit(
4109
- s,
4110
- [inc(1)],
4352
+ [{ stream: s, msgs: [inc(1)] }],
4111
4353
  make_meta({ stream: s })
4112
4354
  );
4113
4355
  await correlate();
@@ -4138,18 +4380,15 @@ var runStoreTck = (options) => {
4138
4380
  const sB = `qst-${tag}-b`;
4139
4381
  const sUnasked = `qst-${tag}-unasked`;
4140
4382
  await store.commit(
4141
- sA,
4142
- [inc(1), inc(2)],
4383
+ [{ stream: sA, msgs: [inc(1), inc(2)] }],
4143
4384
  make_meta({ stream: sA })
4144
4385
  );
4145
4386
  await store.commit(
4146
- sB,
4147
- [dec(5)],
4387
+ [{ stream: sB, msgs: [dec(5)] }],
4148
4388
  make_meta({ stream: sB })
4149
4389
  );
4150
4390
  await store.commit(
4151
- sUnasked,
4152
- [inc(99)],
4391
+ [{ stream: sUnasked, msgs: [inc(99)] }],
4153
4392
  make_meta({ stream: sUnasked })
4154
4393
  );
4155
4394
  const stats = await store.query_stats([sA, sB]);
@@ -4167,8 +4406,7 @@ var runStoreTck = (options) => {
4167
4406
  (0, import_vitest9.it)("limit: 0 returns no rows", async () => {
4168
4407
  const s = `qst-zero-${uid()}`;
4169
4408
  await store.commit(
4170
- s,
4171
- [inc(1)],
4409
+ [{ stream: s, msgs: [inc(1)] }],
4172
4410
  make_meta({ stream: s })
4173
4411
  );
4174
4412
  const filter = { stream: s, stream_exact: true };
@@ -4189,8 +4427,7 @@ var runStoreTck = (options) => {
4189
4427
  ];
4190
4428
  for (const s of names)
4191
4429
  await store.commit(
4192
- s,
4193
- [inc(1)],
4430
+ [{ stream: s, msgs: [inc(1)] }],
4194
4431
  make_meta({ stream: s })
4195
4432
  );
4196
4433
  const visited = [];
@@ -4213,18 +4450,15 @@ var runStoreTck = (options) => {
4213
4450
  const tag = uid();
4214
4451
  const s = `qst-tail-${tag}`;
4215
4452
  await store.commit(
4216
- s,
4217
- [inc(1)],
4453
+ [{ stream: s, msgs: [inc(1)] }],
4218
4454
  make_meta({ stream: s })
4219
4455
  );
4220
4456
  await store.commit(
4221
- s,
4222
- [inc(2)],
4457
+ [{ stream: s, msgs: [inc(2)] }],
4223
4458
  make_meta({ stream: s })
4224
4459
  );
4225
4460
  await store.commit(
4226
- s,
4227
- [inc(3)],
4461
+ [{ stream: s, msgs: [inc(3)] }],
4228
4462
  make_meta({ stream: s })
4229
4463
  );
4230
4464
  const stats = await store.query_stats([s], {
@@ -4240,14 +4474,12 @@ var runStoreTck = (options) => {
4240
4474
  const tag = uid();
4241
4475
  const s = `qst-cn-${tag}`;
4242
4476
  await store.commit(
4243
- s,
4244
- [inc(1)],
4477
+ [{ stream: s, msgs: [inc(1)] }],
4245
4478
  make_meta({ stream: s })
4246
4479
  );
4247
4480
  await store.truncate([{ stream: s, snapshot: { count: 99 } }]);
4248
4481
  await store.commit(
4249
- s,
4250
- [inc(2), inc(3), dec(1)],
4482
+ [{ stream: s, msgs: [inc(2), inc(3), dec(1)] }],
4251
4483
  make_meta({ stream: s })
4252
4484
  );
4253
4485
  const stats = await store.query_stats([s], {
@@ -4266,13 +4498,11 @@ var runStoreTck = (options) => {
4266
4498
  const s = `qst-excl-${tag}`;
4267
4499
  const sAllOut = `qst-allout-${tag}`;
4268
4500
  await store.commit(
4269
- s,
4270
- [inc(1), dec(2), inc(3)],
4501
+ [{ stream: s, msgs: [inc(1), dec(2), inc(3)] }],
4271
4502
  make_meta({ stream: s })
4272
4503
  );
4273
4504
  await store.commit(
4274
- sAllOut,
4275
- [inc(7)],
4505
+ [{ stream: sAllOut, msgs: [inc(7)] }],
4276
4506
  make_meta({ stream: sAllOut })
4277
4507
  );
4278
4508
  const all = await store.query_stats([s]);
@@ -4296,18 +4526,15 @@ var runStoreTck = (options) => {
4296
4526
  const tag = uid();
4297
4527
  const s = `qst-tt-${tag}`;
4298
4528
  const c1 = await store.commit(
4299
- s,
4300
- [inc(1)],
4529
+ [{ stream: s, msgs: [inc(1)] }],
4301
4530
  make_meta({ stream: s })
4302
4531
  );
4303
4532
  const c2 = await store.commit(
4304
- s,
4305
- [inc(2)],
4533
+ [{ stream: s, msgs: [inc(2)] }],
4306
4534
  make_meta({ stream: s })
4307
4535
  );
4308
4536
  await store.commit(
4309
- s,
4310
- [inc(3)],
4537
+ [{ stream: s, msgs: [inc(3)] }],
4311
4538
  make_meta({ stream: s })
4312
4539
  );
4313
4540
  const before = c2[0].id;
@@ -4331,18 +4558,15 @@ var runStoreTck = (options) => {
4331
4558
  const sB = `qsf-${tag}-orders-2`;
4332
4559
  const sOther = `qsf-${tag}-users-1`;
4333
4560
  await store.commit(
4334
- sA,
4335
- [inc(1)],
4561
+ [{ stream: sA, msgs: [inc(1)] }],
4336
4562
  make_meta({ stream: sA })
4337
4563
  );
4338
4564
  await store.commit(
4339
- sB,
4340
- [inc(2)],
4565
+ [{ stream: sB, msgs: [inc(2)] }],
4341
4566
  make_meta({ stream: sB })
4342
4567
  );
4343
4568
  await store.commit(
4344
- sOther,
4345
- [inc(3)],
4569
+ [{ stream: sOther, msgs: [inc(3)] }],
4346
4570
  make_meta({ stream: sOther })
4347
4571
  );
4348
4572
  const orders = await store.query_stats({
@@ -4365,13 +4589,11 @@ var runStoreTck = (options) => {
4365
4589
  const b = `qsc-${tag}-b`;
4366
4590
  await store.subscribe([{ stream: a }, { stream: b }]);
4367
4591
  await store.commit(
4368
- a,
4369
- [inc(1)],
4592
+ [{ stream: a, msgs: [inc(1)] }],
4370
4593
  make_meta({ stream: a })
4371
4594
  );
4372
4595
  await store.commit(
4373
- b,
4374
- [inc(2)],
4596
+ [{ stream: b, msgs: [inc(2)] }],
4375
4597
  make_meta({ stream: b })
4376
4598
  );
4377
4599
  await correlate();
@@ -4396,13 +4618,11 @@ var runStoreTck = (options) => {
4396
4618
  const a = `qse-${tag}-a`;
4397
4619
  const b = `qse-${tag}-b`;
4398
4620
  await store.commit(
4399
- a,
4400
- [inc(1)],
4621
+ [{ stream: a, msgs: [inc(1)] }],
4401
4622
  make_meta({ stream: a })
4402
4623
  );
4403
4624
  await store.commit(
4404
- b,
4405
- [dec(2)],
4625
+ [{ stream: b, msgs: [dec(2)] }],
4406
4626
  make_meta({ stream: b })
4407
4627
  );
4408
4628
  const all = await store.query_stats({});
@@ -4413,8 +4633,7 @@ var runStoreTck = (options) => {
4413
4633
  const tag = uid();
4414
4634
  const s = `qsfl-${tag}`;
4415
4635
  await store.commit(
4416
- s,
4417
- [inc(1), inc(2), dec(3)],
4636
+ [{ stream: s, msgs: [inc(1), inc(2), dec(3)] }],
4418
4637
  make_meta({ stream: s })
4419
4638
  );
4420
4639
  const c = await store.query_stats([s], {
@@ -4445,8 +4664,7 @@ var runStoreTck = (options) => {
4445
4664
  ];
4446
4665
  for (const s of streams) {
4447
4666
  await store.commit(
4448
- s,
4449
- [inc(1)],
4667
+ [{ stream: s, msgs: [inc(1)] }],
4450
4668
  make_meta({ stream: s })
4451
4669
  );
4452
4670
  }
@@ -4476,8 +4694,7 @@ var runStoreTck = (options) => {
4476
4694
  const tag = uid();
4477
4695
  for (const s of [`qsc-${tag}-c`, `qsc-${tag}-a`, `qsc-${tag}-b`])
4478
4696
  await store.commit(
4479
- s,
4480
- [inc(1), inc(2)],
4697
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
4481
4698
  make_meta({ stream: s })
4482
4699
  );
4483
4700
  const pattern = { stream: `qsc-${tag}-.*` };
@@ -4626,8 +4843,7 @@ var runStoreTck = (options) => {
4626
4843
  const s = `head-${uid()}`;
4627
4844
  await store.subscribe([{ stream: s }]);
4628
4845
  await store.commit(
4629
- s,
4630
- [inc(1)],
4846
+ [{ stream: s, msgs: [inc(1)] }],
4631
4847
  make_meta({ stream: s })
4632
4848
  );
4633
4849
  const positions = [];
@@ -4643,7 +4859,7 @@ var runStoreTck = (options) => {
4643
4859
  const actor2 = { id: "reader", name: "Reader" };
4644
4860
  const reader = (shape) => {
4645
4861
  const Happened = import_zod2.z.object(shape);
4646
- return (0, import_act2.state)({ Reader: import_zod2.z.object({ n: import_zod2.z.number() }) }).init(() => ({ n: 0 })).emits({ Happened }).patch({ Happened: (_, s) => ({ n: s.n + 1 }) }).on({ Do: Happened }).emit((p) => ["Happened", p]).build();
4862
+ return (0, import_act2.state)({ Reader: import_zod2.z.object({ n: import_zod2.z.number() }) }).init(() => ({ n: 0 })).emits({ Happened }).patch({ Happened: (_, s) => ({ n: s.n + 1 }) }).on({ Do: Happened }).emit("Happened").build();
4647
4863
  };
4648
4864
  const over = async (S) => {
4649
4865
  const fresh = await options.factory();
@@ -4736,10 +4952,16 @@ var runStoreTck = (options) => {
4736
4952
  const t = await over(S);
4737
4953
  try {
4738
4954
  await t.fresh.commit(
4739
- t.stream,
4740
- [{ name: "Happened", data: { at: /* @__PURE__ */ new Date("2020-01-01") } }],
4741
- raw,
4742
- -1
4955
+ [
4956
+ {
4957
+ stream: t.stream,
4958
+ msgs: [
4959
+ { name: "Happened", data: { at: /* @__PURE__ */ new Date("2020-01-01") } }
4960
+ ],
4961
+ expectedVersion: -1
4962
+ }
4963
+ ],
4964
+ raw
4743
4965
  );
4744
4966
  const d = await t.read();
4745
4967
  (0, import_vitest9.expect)(d.label).toBeUndefined();
@@ -4753,10 +4975,14 @@ var runStoreTck = (options) => {
4753
4975
  const t = await over(S);
4754
4976
  try {
4755
4977
  await t.fresh.commit(
4756
- t.stream,
4757
- [{ name: "Happened", data: { at: "not a date" } }],
4758
- raw,
4759
- -1
4978
+ [
4979
+ {
4980
+ stream: t.stream,
4981
+ msgs: [{ name: "Happened", data: { at: "not a date" } }],
4982
+ expectedVersion: -1
4983
+ }
4984
+ ],
4985
+ raw
4760
4986
  );
4761
4987
  (0, import_vitest9.expect)((await t.read()).at).toBe("not a date");
4762
4988
  } finally {
@@ -4896,6 +5122,19 @@ var runStoreTck = (options) => {
4896
5122
  (0, import_vitest9.expect)((await take(k, who)).correlating).toBe(true);
4897
5123
  await release(k, who);
4898
5124
  });
5125
+ (0, import_vitest9.it)("advances the checkpoint for a caller refused the lease", async () => {
5126
+ const k = key();
5127
+ const holder = by();
5128
+ (0, import_vitest9.expect)((await take(k, holder)).correlating).toBe(true);
5129
+ const refused = await store.subscribe([], 123456, {
5130
+ key: k,
5131
+ by: by(),
5132
+ millis: 1e4
5133
+ });
5134
+ (0, import_vitest9.expect)(refused.correlating).toBe(false);
5135
+ (0, import_vitest9.expect)((await take(k, holder)).correlated_at).toBe(123456);
5136
+ await release(k, holder);
5137
+ });
4899
5138
  (0, import_vitest9.it)("answers undefined when no correlator is supplied", async () => {
4900
5139
  (0, import_vitest9.expect)(await store.subscribe([])).not.toHaveProperty("correlating");
4901
5140
  });
@@ -4977,7 +5216,234 @@ var runStoreTck = (options) => {
4977
5216
  });
4978
5217
  }
4979
5218
  );
5219
+ (0, import_vitest9.describe)("an action across streams", () => {
5220
+ const SeatTaken = import_zod2.z.object({ student: import_zod2.z.string() });
5221
+ const Seats = (0, import_act2.state)({
5222
+ Seats: import_zod2.z.object({ taken: import_zod2.z.number() }).refine((s) => s.taken <= 3, "the course is full")
5223
+ }).init(() => ({ taken: 0 })).emits({ SeatTaken }).patch({ SeatTaken: (_, s) => ({ taken: s.taken + 1 }) }).on({ TakeSeat: SeatTaken }).emit("SeatTaken").build();
5224
+ const Enroll = import_zod2.z.object({ course: import_zod2.z.string(), student: import_zod2.z.string() });
5225
+ const course = (data) => `course-${data.course}`;
5226
+ const Enrollment = (0, import_act2.state)({ Enrollment: Enroll }).init(() => ({ course: "", student: "" })).emits({ Enrolled: Enroll }).on({ Enroll }, { maxRetries: 8 }).loads(Seats, course, ["SeatTaken"]).requires([
5227
+ {
5228
+ description: "the course has a free seat",
5229
+ holds: (_, __, ___, { Seats: Seats2 }) => Seats2.taken < 3
5230
+ }
5231
+ ]).emit((data) => [
5232
+ ["Enrolled", data],
5233
+ ["SeatTaken", { student: data.student }]
5234
+ ]).on({ Overfill: Enroll }).loads(Seats, course, ["SeatTaken"]).emit((data) => [
5235
+ ["Enrolled", data],
5236
+ ["SeatTaken", { student: data.student }]
5237
+ ]).build();
5238
+ const actor2 = { id: "a", name: "a" };
5239
+ const at = (stream) => ({ stream, actor: actor2 });
5240
+ const over = async (run) => {
5241
+ const fresh = await options.factory();
5242
+ await fresh.seed();
5243
+ const cache = new import_act2.InMemoryCache();
5244
+ const app = (0, import_act2.act)().withState(Seats).withState(Enrollment).build({ scoped: { store: fresh, cache } });
5245
+ const c = `c${uid()}`;
5246
+ const events2 = async (stream) => {
5247
+ const found = [];
5248
+ await fresh.query((e) => found.push(e), {
5249
+ stream,
5250
+ stream_exact: true
5251
+ });
5252
+ return found;
5253
+ };
5254
+ try {
5255
+ await run({
5256
+ app,
5257
+ fresh,
5258
+ c,
5259
+ events: events2,
5260
+ seat: (student) => ({ course: c, student })
5261
+ });
5262
+ } finally {
5263
+ import_vitest9.vi.restoreAllMocks();
5264
+ await app.shutdown();
5265
+ await cache.dispose();
5266
+ await fresh.dispose();
5267
+ }
5268
+ };
5269
+ (0, import_vitest9.it)("commits both streams at once and fires committed once per stream", () => over(async ({ app, c, events: events2, seat }) => {
5270
+ const stream = `enr-${uid()}`;
5271
+ const heard = [];
5272
+ app.on("committed", (s) => heard.push(s));
5273
+ const snapshots = await app.do("Enroll", at(stream), seat("s1"));
5274
+ (0, import_vitest9.expect)(snapshots.map((s) => s.event?.name)).toEqual([
5275
+ "Enrolled"
5276
+ ]);
5277
+ const [own] = await events2(stream);
5278
+ const [other] = await events2(`course-${c}`);
5279
+ (0, import_vitest9.expect)(own.name).toBe("Enrolled");
5280
+ (0, import_vitest9.expect)(other.name).toBe("SeatTaken");
5281
+ (0, import_vitest9.expect)(other.id).toBeGreaterThan(own.id);
5282
+ (0, import_vitest9.expect)(other.meta.correlation).toBe(own.meta.correlation);
5283
+ (0, import_vitest9.expect)(heard.map((h) => h[0].event?.stream)).toEqual([
5284
+ stream,
5285
+ `course-${c}`
5286
+ ]);
5287
+ (0, import_vitest9.expect)((await app.load(Seats, `course-${c}`)).state.taken).toBe(1);
5288
+ }));
5289
+ (0, import_vitest9.it)("stores nothing on either stream when a rule or a schema refuses", () => over(async ({ app, c, events: events2, seat }) => {
5290
+ for (const i of [1, 2, 3])
5291
+ await app.do("TakeSeat", at(`course-${c}`), { student: `s${i}` });
5292
+ const [a, b] = [`enr-${uid()}`, `enr-${uid()}`];
5293
+ await (0, import_vitest9.expect)(app.do("Enroll", at(a), seat("late"))).rejects.toThrow(
5294
+ import_act2.PreconditionError
5295
+ );
5296
+ await (0, import_vitest9.expect)(app.do("Overfill", at(b), seat("late"))).rejects.toThrow(
5297
+ import_act2.ValidationError
5298
+ );
5299
+ (0, import_vitest9.expect)(await events2(a)).toEqual([]);
5300
+ (0, import_vitest9.expect)(await events2(b)).toEqual([]);
5301
+ (0, import_vitest9.expect)(await events2(`course-${c}`)).toHaveLength(3);
5302
+ }));
5303
+ (0, import_vitest9.it)("a rival on the loaded stream refuses the whole commit, naming that stream", () => over(async ({ app, fresh, c, events: events2, seat }) => {
5304
+ const commit = fresh.commit.bind(fresh);
5305
+ import_vitest9.vi.spyOn(fresh, "commit").mockImplementationOnce(
5306
+ async (...args) => {
5307
+ await commit(
5308
+ [
5309
+ {
5310
+ stream: `course-${c}`,
5311
+ msgs: [{ name: "SeatTaken", data: { student: "rival" } }]
5312
+ }
5313
+ ],
5314
+ { correlation: "rival", causation: {} }
5315
+ );
5316
+ return commit(...args);
5317
+ }
5318
+ );
5319
+ const stream = `enr-${uid()}`;
5320
+ const err = await app.do("Overfill", at(stream), seat("s")).catch((e) => e);
5321
+ (0, import_vitest9.expect)(err).toBeInstanceOf(import_act2.ConcurrencyError);
5322
+ (0, import_vitest9.expect)(err.stream).toBe(`course-${c}`);
5323
+ (0, import_vitest9.expect)(await events2(stream)).toEqual([]);
5324
+ (0, import_vitest9.expect)(await events2(`course-${c}`)).toHaveLength(1);
5325
+ }));
5326
+ (0, import_vitest9.it)("racing enrollments fill the seats exactly: the rest are refused by the rule", () => over(async ({ app, c, events: events2, seat }) => {
5327
+ const results = await Promise.allSettled(
5328
+ [1, 2, 3, 4, 5, 6].map(
5329
+ (i) => app.do("Enroll", at(`race-${uid()}-${i}`), seat(`s${i}`))
5330
+ )
5331
+ );
5332
+ (0, import_vitest9.expect)(results.filter((r) => r.status === "fulfilled")).toHaveLength(
5333
+ 3
5334
+ );
5335
+ for (const r of results.filter((r2) => r2.status === "rejected"))
5336
+ (0, import_vitest9.expect)(r.reason).toBeInstanceOf(
5337
+ import_act2.PreconditionError
5338
+ );
5339
+ (0, import_vitest9.expect)(await events2(`course-${c}`)).toHaveLength(3);
5340
+ }));
5341
+ });
4980
5342
  import_vitest9.describe.skipIf(!caps.restore)("restore (capability)", () => {
5343
+ (0, import_vitest9.describe)("what a restore must not get wrong", () => {
5344
+ const edge_uid = () => uid();
5345
+ const E = (data = { a: 1 }, pii) => ({
5346
+ name: "E",
5347
+ data,
5348
+ ...pii ? { pii } : {}
5349
+ });
5350
+ const edge_meta = (correlation = "m") => ({ correlation, causation: {} });
5351
+ const rows = async (query) => {
5352
+ const found = [];
5353
+ await store.query((e) => found.push(e), query);
5354
+ return found;
5355
+ };
5356
+ const one = (stream) => ({ stream, stream_exact: true });
5357
+ (0, import_vitest9.it)("a commit after restore continues each stream from its restored head", async () => {
5358
+ const a = `a-${edge_uid()}`;
5359
+ await store.commit(
5360
+ [{ stream: a, msgs: [E(), E()] }],
5361
+ edge_meta()
5362
+ );
5363
+ const log = await rows({ with_snaps: true });
5364
+ await store.restore(async (cb) => {
5365
+ for (const e of log) await cb(e);
5366
+ });
5367
+ const [c] = await store.commit(
5368
+ [{ stream: a, msgs: [E()], expectedVersion: 1 }],
5369
+ edge_meta()
5370
+ );
5371
+ (0, import_vitest9.expect)(c.version).toBe(2);
5372
+ });
5373
+ (0, import_vitest9.it)("restore renumbers: ids are dense whatever the source ids were", async () => {
5374
+ const a = `a-${edge_uid()}`;
5375
+ const ev = (version, id) => ({
5376
+ id,
5377
+ stream: a,
5378
+ version,
5379
+ name: "E",
5380
+ data: {},
5381
+ meta: edge_meta(),
5382
+ created: /* @__PURE__ */ new Date()
5383
+ });
5384
+ const ids = [];
5385
+ await store.restore(async (cb) => {
5386
+ ids.push(await cb(ev(0, 500)));
5387
+ ids.push(await cb(ev(1, 900)));
5388
+ });
5389
+ (0, import_vitest9.expect)(ids[1]).toBe(ids[0] + 1);
5390
+ (0, import_vitest9.expect)(ids[0]).toBeLessThanOrEqual(1);
5391
+ });
5392
+ (0, import_vitest9.it)("changes nothing when it fails", async () => {
5393
+ const a = `rb-${edge_uid()}`;
5394
+ const key = `k-${edge_uid()}`;
5395
+ const sub = `sub-${edge_uid()}`;
5396
+ const committed = await store.commit(
5397
+ [{ stream: a, msgs: [E({}, { email: "x" }), E()] }],
5398
+ edge_meta()
5399
+ );
5400
+ const head = committed.at(-1).id;
5401
+ await store.subscribe([{ stream: sub }], head);
5402
+ const keyed = await store.subscribe([], head, {
5403
+ key,
5404
+ by: "w",
5405
+ millis: 1
5406
+ });
5407
+ const shared = await store.subscribe([]);
5408
+ await (0, import_vitest9.expect)(
5409
+ store.restore(async (callback) => {
5410
+ await callback({
5411
+ id: 1,
5412
+ stream: "other",
5413
+ version: 0,
5414
+ name: "E",
5415
+ data: {},
5416
+ meta: edge_meta(),
5417
+ created: /* @__PURE__ */ new Date()
5418
+ });
5419
+ throw new Error("mid-restore");
5420
+ })
5421
+ ).rejects.toThrow();
5422
+ const subscriptions = [];
5423
+ await store.query_streams((p) => subscriptions.push(p.stream), {
5424
+ stream: sub,
5425
+ stream_exact: true
5426
+ });
5427
+ (0, import_vitest9.expect)(subscriptions).toEqual([sub]);
5428
+ (0, import_vitest9.expect)((await store.subscribe([])).correlated_at).toBe(
5429
+ shared.correlated_at
5430
+ );
5431
+ await (0, import_act2.sleep)(5);
5432
+ const keyed_after = await store.subscribe([], void 0, {
5433
+ key,
5434
+ by: "w2",
5435
+ millis: 1
5436
+ });
5437
+ (0, import_vitest9.expect)(keyed_after.correlated_at).toBe(keyed.correlated_at);
5438
+ (0, import_vitest9.expect)((await rows(one(a)))[0].pii).toEqual({ email: "x" });
5439
+ const [next] = await store.commit(
5440
+ [{ stream: a, msgs: [E()], expectedVersion: 1 }],
5441
+ edge_meta()
5442
+ );
5443
+ (0, import_vitest9.expect)(next.version).toBe(2);
5444
+ (0, import_vitest9.expect)(next.id).toBeGreaterThan(head);
5445
+ });
5446
+ });
4981
5447
  beforeEach(async () => {
4982
5448
  await store.drop();
4983
5449
  await store.seed();
@@ -5163,8 +5629,7 @@ var runStoreTck = (options) => {
5163
5629
  (0, import_vitest9.it)("wipes pre-existing events before inserting", async () => {
5164
5630
  const old = `restore-old-${uid()}`;
5165
5631
  await store.commit(
5166
- old,
5167
- [inc(1), inc(2)],
5632
+ [{ stream: old, msgs: [inc(1), inc(2)] }],
5168
5633
  make_meta({ stream: old })
5169
5634
  );
5170
5635
  const fresh = `restore-fresh-${uid()}`;
@@ -5311,8 +5776,7 @@ var runStoreTck = (options) => {
5311
5776
  (0, import_vitest9.it)("rolls back atomically when the source throws mid-iteration", async () => {
5312
5777
  const original = `restore-pre-${uid()}`;
5313
5778
  const committed = await store.commit(
5314
- original,
5315
- [inc(1), inc(2), inc(3)],
5779
+ [{ stream: original, msgs: [inc(1), inc(2), inc(3)] }],
5316
5780
  make_meta({ stream: original })
5317
5781
  );
5318
5782
  const explosive = {
@@ -5485,12 +5949,16 @@ var runStoreTck = (options) => {
5485
5949
  (0, import_vitest9.it)("commits and loads pii alongside data", async () => {
5486
5950
  const s = `pii-roundtrip-${uid()}`;
5487
5951
  const committed = await store.commit(
5488
- s,
5489
5952
  [
5490
5953
  {
5491
- name: "Incremented",
5492
- data: { amount: 1 },
5493
- pii: { email: "u@example.com", name: "Ursula" }
5954
+ stream: s,
5955
+ msgs: [
5956
+ {
5957
+ name: "Incremented",
5958
+ data: { amount: 1 },
5959
+ pii: { email: "u@example.com", name: "Ursula" }
5960
+ }
5961
+ ]
5494
5962
  }
5495
5963
  ],
5496
5964
  make_meta({ stream: s })
@@ -5513,12 +5981,16 @@ var runStoreTck = (options) => {
5513
5981
  });
5514
5982
  (0, import_vitest9.describe)("with_pii", () => {
5515
5983
  const seed_pii = async (s) => await store.commit(
5516
- s,
5517
5984
  [
5518
5985
  {
5519
- name: "Incremented",
5520
- data: { amount: 1 },
5521
- pii: { email: "w@example.com" }
5986
+ stream: s,
5987
+ msgs: [
5988
+ {
5989
+ name: "Incremented",
5990
+ data: { amount: 1 },
5991
+ pii: { email: "w@example.com" }
5992
+ }
5993
+ ]
5522
5994
  }
5523
5995
  ],
5524
5996
  make_meta({ stream: s })
@@ -5579,12 +6051,16 @@ var runStoreTck = (options) => {
5579
6051
  const s = `pii-date-${uid()}`;
5580
6052
  const when = /* @__PURE__ */ new Date("2024-03-01T10:20:30.000Z");
5581
6053
  await store.commit(
5582
- s,
5583
6054
  [
5584
6055
  {
5585
- name: "Incremented",
5586
- data: { amount: 1, at: when },
5587
- pii: { born: when }
6056
+ stream: s,
6057
+ msgs: [
6058
+ {
6059
+ name: "Incremented",
6060
+ data: { amount: 1, at: when },
6061
+ pii: { born: when }
6062
+ }
6063
+ ]
5588
6064
  }
5589
6065
  ],
5590
6066
  make_meta({ stream: s })
@@ -5607,17 +6083,21 @@ var runStoreTck = (options) => {
5607
6083
  (0, import_vitest9.it)("query_stats head/tail never carry pii \u2014 introspection surface is pii-safe", async () => {
5608
6084
  const s = `pii-stats-${uid()}`;
5609
6085
  await store.commit(
5610
- s,
5611
6086
  [
5612
6087
  {
5613
- name: "Incremented",
5614
- data: { amount: 1 },
5615
- pii: { email: "head@example.com" }
5616
- },
5617
- {
5618
- name: "Incremented",
5619
- data: { amount: 2 },
5620
- pii: { email: "tail@example.com" }
6088
+ stream: s,
6089
+ msgs: [
6090
+ {
6091
+ name: "Incremented",
6092
+ data: { amount: 1 },
6093
+ pii: { email: "head@example.com" }
6094
+ },
6095
+ {
6096
+ name: "Incremented",
6097
+ data: { amount: 2 },
6098
+ pii: { email: "tail@example.com" }
6099
+ }
6100
+ ]
5621
6101
  }
5622
6102
  ],
5623
6103
  make_meta({ stream: s })
@@ -5641,8 +6121,7 @@ var runStoreTck = (options) => {
5641
6121
  (0, import_vitest9.it)("passes through events without pii (pii is null or undefined on load)", async () => {
5642
6122
  const s = `pii-none-${uid()}`;
5643
6123
  await store.commit(
5644
- s,
5645
- [inc(1)],
6124
+ [{ stream: s, msgs: [inc(1)] }],
5646
6125
  make_meta({ stream: s })
5647
6126
  );
5648
6127
  const seen = [];
@@ -5658,17 +6137,21 @@ var runStoreTck = (options) => {
5658
6137
  (0, import_vitest9.it)("wipes pii for every event on the stream via forget_pii", async () => {
5659
6138
  const s = `pii-forget-${uid()}`;
5660
6139
  await store.commit(
5661
- s,
5662
6140
  [
5663
6141
  {
5664
- name: "Incremented",
5665
- data: { amount: 1 },
5666
- pii: { email: "a@example.com" }
5667
- },
5668
- {
5669
- name: "Incremented",
5670
- data: { amount: 2 },
5671
- pii: { email: "b@example.com" }
6142
+ stream: s,
6143
+ msgs: [
6144
+ {
6145
+ name: "Incremented",
6146
+ data: { amount: 1 },
6147
+ pii: { email: "a@example.com" }
6148
+ },
6149
+ {
6150
+ name: "Incremented",
6151
+ data: { amount: 2 },
6152
+ pii: { email: "b@example.com" }
6153
+ }
6154
+ ]
5672
6155
  }
5673
6156
  ],
5674
6157
  make_meta({ stream: s })
@@ -5693,12 +6176,16 @@ var runStoreTck = (options) => {
5693
6176
  (0, import_vitest9.it)("is idempotent \u2014 second forget_pii returns 0, no error", async () => {
5694
6177
  const s = `pii-forget-idem-${uid()}`;
5695
6178
  await store.commit(
5696
- s,
5697
6179
  [
5698
6180
  {
5699
- name: "Incremented",
5700
- data: { amount: 1 },
5701
- pii: { email: "u@example.com" }
6181
+ stream: s,
6182
+ msgs: [
6183
+ {
6184
+ name: "Incremented",
6185
+ data: { amount: 1 },
6186
+ pii: { email: "u@example.com" }
6187
+ }
6188
+ ]
5702
6189
  }
5703
6190
  ],
5704
6191
  make_meta({ stream: s })
@@ -5713,23 +6200,31 @@ var runStoreTck = (options) => {
5713
6200
  const sA = `pii-iso-a-${uid()}`;
5714
6201
  const sB = `pii-iso-b-${uid()}`;
5715
6202
  await store.commit(
5716
- sA,
5717
6203
  [
5718
6204
  {
5719
- name: "Incremented",
5720
- data: { amount: 1 },
5721
- pii: { email: "alice@example.com" }
6205
+ stream: sA,
6206
+ msgs: [
6207
+ {
6208
+ name: "Incremented",
6209
+ data: { amount: 1 },
6210
+ pii: { email: "alice@example.com" }
6211
+ }
6212
+ ]
5722
6213
  }
5723
6214
  ],
5724
6215
  make_meta({ stream: sA })
5725
6216
  );
5726
6217
  await store.commit(
5727
- sB,
5728
6218
  [
5729
6219
  {
5730
- name: "Incremented",
5731
- data: { amount: 1 },
5732
- pii: { email: "bob@example.com" }
6220
+ stream: sB,
6221
+ msgs: [
6222
+ {
6223
+ name: "Incremented",
6224
+ data: { amount: 1 },
6225
+ pii: { email: "bob@example.com" }
6226
+ }
6227
+ ]
5733
6228
  }
5734
6229
  ],
5735
6230
  make_meta({ stream: sB })
@@ -5755,8 +6250,7 @@ var runStoreTck = (options) => {
5755
6250
  (0, import_vitest9.it)("forget_pii on a stream with no pii events returns 0", async () => {
5756
6251
  const s = `pii-forget-empty-${uid()}`;
5757
6252
  await store.commit(
5758
- s,
5759
- [inc(1)],
6253
+ [{ stream: s, msgs: [inc(1)] }],
5760
6254
  make_meta({ stream: s })
5761
6255
  );
5762
6256
  const wiped = await store.forget_pii.call(store, s);
@@ -5781,8 +6275,7 @@ var runStoreTck = (options) => {
5781
6275
  try {
5782
6276
  const stream = `notify-${uid()}`;
5783
6277
  await writer.commit(
5784
- stream,
5785
- [inc(1)],
6278
+ [{ stream, msgs: [inc(1)] }],
5786
6279
  make_meta({ stream })
5787
6280
  );
5788
6281
  await arrived;
@@ -5810,18 +6303,15 @@ var runStoreTck = (options) => {
5810
6303
  const writer = await options.factory();
5811
6304
  try {
5812
6305
  await store.commit(
5813
- own,
5814
- [inc(1)],
6306
+ [{ stream: own, msgs: [inc(1)] }],
5815
6307
  make_meta({ stream: own })
5816
6308
  );
5817
6309
  await writer.commit(
5818
- remote,
5819
- [inc(1)],
6310
+ [{ stream: remote, msgs: [inc(1)] }],
5820
6311
  make_meta({ stream: remote })
5821
6312
  );
5822
6313
  await writer.commit(
5823
- sentinel,
5824
- [inc(1)],
6314
+ [{ stream: sentinel, msgs: [inc(1)] }],
5825
6315
  make_meta({ stream: sentinel })
5826
6316
  );
5827
6317
  await sentinel_arrived;
@@ -5833,7 +6323,7 @@ var runStoreTck = (options) => {
5833
6323
  await Promise.resolve(disposer());
5834
6324
  }
5835
6325
  });
5836
- (0, import_vitest9.it)("delivers one notification per commit transaction carrying the full event batch", async () => {
6326
+ (0, import_vitest9.it)("delivers one notification carrying a stream's full batch from one commit", async () => {
5837
6327
  const received = [];
5838
6328
  let resolve_sentinel;
5839
6329
  const sentinel_arrived = new Promise((res) => {
@@ -5848,13 +6338,11 @@ var runStoreTck = (options) => {
5848
6338
  const writer = await options.factory();
5849
6339
  try {
5850
6340
  await writer.commit(
5851
- batch,
5852
- [inc(1), inc(2), inc(3)],
6341
+ [{ stream: batch, msgs: [inc(1), inc(2), inc(3)] }],
5853
6342
  make_meta({ stream: batch })
5854
6343
  );
5855
6344
  await writer.commit(
5856
- sentinel,
5857
- [inc(1)],
6345
+ [{ stream: sentinel, msgs: [inc(1)] }],
5858
6346
  make_meta({ stream: sentinel })
5859
6347
  );
5860
6348
  await sentinel_arrived;
@@ -5877,6 +6365,45 @@ var runStoreTck = (options) => {
5877
6365
  await Promise.resolve(disposer());
5878
6366
  }
5879
6367
  });
6368
+ (0, import_vitest9.it)("delivers one notification per stream a commit appends to, with that stream's events", async () => {
6369
+ const received = [];
6370
+ let resolve_sentinel;
6371
+ const sentinel_arrived = new Promise((res) => {
6372
+ resolve_sentinel = res;
6373
+ });
6374
+ const a = `notify-across-a-${uid()}`;
6375
+ const b = `notify-across-b-${uid()}`;
6376
+ const sentinel = `notify-across-sentinel-${uid()}`;
6377
+ const disposer = await store.notify.call(store, (n) => {
6378
+ received.push(n);
6379
+ if (n.stream === sentinel) resolve_sentinel();
6380
+ });
6381
+ const writer = await options.factory();
6382
+ try {
6383
+ const committed = await writer.commit(
6384
+ [
6385
+ { stream: a, msgs: [inc(1), inc(2)] },
6386
+ { stream: b, msgs: [dec(3)] }
6387
+ ],
6388
+ make_meta({ stream: a })
6389
+ );
6390
+ await writer.commit(
6391
+ [{ stream: sentinel, msgs: [inc(1)] }],
6392
+ make_meta({ stream: sentinel })
6393
+ );
6394
+ await sentinel_arrived;
6395
+ for (const stream of [a, b]) {
6396
+ const of_stream = received.filter((n) => n.stream === stream);
6397
+ (0, import_vitest9.expect)(of_stream).toHaveLength(1);
6398
+ (0, import_vitest9.expect)(of_stream[0].events.map((e) => e.id)).toEqual(
6399
+ committed.filter((e) => e.stream === stream).map((e) => e.id)
6400
+ );
6401
+ }
6402
+ } finally {
6403
+ await writer.dispose();
6404
+ await Promise.resolve(disposer());
6405
+ }
6406
+ });
5880
6407
  });
5881
6408
  }
5882
6409
  });