@rotorsoft/act-tck 1.41.4 → 2.1.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
@@ -28,8 +28,7 @@ var seed_stream = async (store, stream, count, correlation) => {
28
28
  const out = [];
29
29
  for (let i = 0; i < count; i++) {
30
30
  const committed = await store.commit(
31
- stream,
32
- [inc(1)],
31
+ [{ stream, msgs: [inc(1)] }],
33
32
  make_meta({ correlation, stream })
34
33
  );
35
34
  out.push(...committed);
@@ -592,14 +591,17 @@ var apply_plan = async (store, plan, pii_isolation) => {
592
591
  if (op.t === "commit") {
593
592
  const msgs = op.pii && pii_isolation ? op.msgs.map((m, i) => i === 0 ? { ...m, pii: op.pii } : m) : op.msgs;
594
593
  await store.commit(
595
- op.stream,
596
- msgs,
594
+ [{ stream: op.stream, msgs }],
597
595
  make_meta({ stream: op.stream, correlation: plan.correlation })
598
596
  );
599
597
  } else if (op.t === "snapshot") {
600
598
  await store.commit(
601
- op.stream,
602
- [{ name: SNAP_EVENT, data: { count: op.count } }],
599
+ [
600
+ {
601
+ stream: op.stream,
602
+ msgs: [{ name: SNAP_EVENT, data: { count: op.count } }]
603
+ }
604
+ ],
603
605
  make_meta({ stream: op.stream, correlation: plan.correlation })
604
606
  );
605
607
  } else if (op.t === "truncate") {
@@ -608,8 +610,12 @@ var apply_plan = async (store, plan, pii_isolation) => {
608
610
  ]);
609
611
  } else if (op.t === "truncate_windowed") {
610
612
  await store.commit(
611
- op.stream,
612
- [{ name: SNAP_EVENT, data: { count: 1 } }],
613
+ [
614
+ {
615
+ stream: op.stream,
616
+ msgs: [{ name: SNAP_EVENT, data: { count: 1 } }]
617
+ }
618
+ ],
613
619
  make_meta({ stream: op.stream, correlation: plan.correlation })
614
620
  );
615
621
  await store.truncate([
@@ -920,8 +926,7 @@ var runStorePropertyTck = (options) => {
920
926
  for (const { stream, count } of commits) {
921
927
  const before = expected.get(stream) ?? -1;
922
928
  const committed = await store.commit(
923
- stream,
924
- events(count),
929
+ [{ stream, msgs: events(count) }],
925
930
  make_meta({ stream })
926
931
  );
927
932
  committed.forEach((e, i) => {
@@ -946,8 +951,7 @@ var runStorePropertyTck = (options) => {
946
951
  await reset2();
947
952
  for (const { stream: stream2, count } of commits) {
948
953
  await store.commit(
949
- stream2,
950
- events(count),
954
+ [{ stream: stream2, msgs: events(count) }],
951
955
  make_meta({ stream: stream2 })
952
956
  );
953
957
  }
@@ -955,10 +959,8 @@ var runStorePropertyTck = (options) => {
955
959
  const before = await collect(store, { stream, stream_exact: true });
956
960
  await expect7(
957
961
  store.commit(
958
- stream,
959
- [inc(1)],
960
- make_meta({ stream }),
961
- before.length
962
+ [{ stream, msgs: [inc(1)], expectedVersion: before.length }],
963
+ make_meta({ stream })
962
964
  )
963
965
  ).rejects.toThrow();
964
966
  const after = await collect(store, { stream, stream_exact: true });
@@ -985,8 +987,7 @@ var runStorePropertyTck = (options) => {
985
987
  for (const op of ops) {
986
988
  if (op.kind === "commit") {
987
989
  await store.commit(
988
- op.stream,
989
- [inc(1)],
990
+ [{ stream: op.stream, msgs: [inc(1)] }],
990
991
  make_meta({ stream: op.stream })
991
992
  );
992
993
  } else if (op.kind === "claim") {
@@ -1028,8 +1029,7 @@ var runStorePropertyTck = (options) => {
1028
1029
  await store.subscribe(all.map((stream) => ({ stream })));
1029
1030
  for (const stream of commitsA) {
1030
1031
  await store.commit(
1031
- stream,
1032
- [inc(1)],
1032
+ [{ stream, msgs: [inc(1)] }],
1033
1033
  make_meta({ stream })
1034
1034
  );
1035
1035
  }
@@ -1041,8 +1041,7 @@ var runStorePropertyTck = (options) => {
1041
1041
  for (const l of acked1) watermark1.set(l.stream, l.at);
1042
1042
  for (const stream of commitsB) {
1043
1043
  await store.commit(
1044
- stream,
1045
- [inc(1)],
1044
+ [{ stream, msgs: [inc(1)] }],
1046
1045
  make_meta({ stream })
1047
1046
  );
1048
1047
  }
@@ -1068,8 +1067,7 @@ var runStorePropertyTck = (options) => {
1068
1067
  await store.subscribe(streams.map((stream) => ({ stream })));
1069
1068
  for (const stream of commits) {
1070
1069
  await store.commit(
1071
- stream,
1072
- [inc(1)],
1070
+ [{ stream, msgs: [inc(1)] }],
1073
1071
  make_meta({ stream })
1074
1072
  );
1075
1073
  }
@@ -1079,8 +1077,7 @@ var runStorePropertyTck = (options) => {
1079
1077
  const blockedSet = new Set(claimed.map((l) => l.stream));
1080
1078
  await store.subscribe([{ stream: "ctrl" }]);
1081
1079
  await store.commit(
1082
- "ctrl",
1083
- [inc(1)],
1080
+ [{ stream: "ctrl", msgs: [inc(1)] }],
1084
1081
  make_meta({ stream: "ctrl" })
1085
1082
  );
1086
1083
  await mark_all(store);
@@ -1098,7 +1095,7 @@ import {
1098
1095
  act,
1099
1096
  ConcurrencyError,
1100
1097
  InMemoryCache,
1101
- is_literal_source,
1098
+ PreconditionError,
1102
1099
  SNAP_EVENT as SNAP_EVENT2,
1103
1100
  sensitive,
1104
1101
  sleep,
@@ -1106,6 +1103,7 @@ import {
1106
1103
  TOMBSTONE_EVENT,
1107
1104
  ValidationError
1108
1105
  } from "@rotorsoft/act";
1106
+ import { isStreamName } from "@rotorsoft/act/adapter";
1109
1107
  import { afterAll as afterAll4, beforeAll as beforeAll4, describe as describe8, expect as expect8, it as it7, vi } from "vitest";
1110
1108
  import { z as z2 } from "zod";
1111
1109
  var runStoreTck = (options) => {
@@ -1133,7 +1131,7 @@ var runStoreTck = (options) => {
1133
1131
  },
1134
1132
  row.source !== void 0 ? {
1135
1133
  stream: row.source,
1136
- stream_exact: is_literal_source(row.source, row.stream)
1134
+ stream_exact: isStreamName(row.source, row.stream)
1137
1135
  } : {}
1138
1136
  );
1139
1137
  if (mark > row.at)
@@ -1170,14 +1168,12 @@ var runStoreTck = (options) => {
1170
1168
  await zero_config.seed();
1171
1169
  const s = `default-config-${uid()}`;
1172
1170
  const first = await zero_config.commit(
1173
- s,
1174
- [inc(1)],
1171
+ [{ stream: s, msgs: [inc(1)] }],
1175
1172
  make_meta({ stream: s })
1176
1173
  );
1177
1174
  expect8(first[0].version).toBe(0);
1178
1175
  const second = await zero_config.commit(
1179
- s,
1180
- [inc(2)],
1176
+ [{ stream: s, msgs: [inc(2)] }],
1181
1177
  make_meta({ stream: s })
1182
1178
  );
1183
1179
  expect8(second[0].version).toBe(1);
@@ -1193,12 +1189,371 @@ var runStoreTck = (options) => {
1193
1189
  });
1194
1190
  });
1195
1191
  }
1192
+ describe8("commit across streams", () => {
1193
+ const meta = make_meta({ stream: "across" });
1194
+ const count = (stream) => store.query(() => {
1195
+ }, { stream, stream_exact: true });
1196
+ it7("lands every append, each from its own head, with ids in append order", async () => {
1197
+ const a = `across-a-${uid()}`;
1198
+ const b = `across-b-${uid()}`;
1199
+ await store.commit(
1200
+ [{ stream: a, msgs: [inc(1)] }],
1201
+ meta
1202
+ );
1203
+ const committed = await store.commit(
1204
+ [
1205
+ { stream: a, msgs: [inc(2), inc(3)], expectedVersion: 0 },
1206
+ { stream: b, msgs: [dec(4)], expectedVersion: -1 }
1207
+ ],
1208
+ meta
1209
+ );
1210
+ expect8(committed.map((e) => [e.stream, e.version])).toEqual([
1211
+ [a, 1],
1212
+ [a, 2],
1213
+ [b, 0]
1214
+ ]);
1215
+ for (let i = 1; i < committed.length; i++)
1216
+ expect8(committed[i].id).toBeGreaterThan(committed[i - 1].id);
1217
+ const read = [];
1218
+ await store.query((e) => read.push(e.id), {
1219
+ after: committed[0].id - 1
1220
+ });
1221
+ expect8(read.filter((id) => committed.some((e) => e.id === id))).toEqual(
1222
+ committed.map((e) => e.id)
1223
+ );
1224
+ });
1225
+ it7("stores nothing when the first, a middle or the last append is stale, and names its stream", async () => {
1226
+ for (const at of [0, 1, 2]) {
1227
+ const streams = [0, 1, 2].map((i) => `across-stale-${i}-${uid()}`);
1228
+ for (const stream of streams)
1229
+ await store.commit(
1230
+ [{ stream, msgs: [inc(1)] }],
1231
+ meta
1232
+ );
1233
+ const refused = store.commit(
1234
+ streams.map((stream, i) => ({
1235
+ stream,
1236
+ msgs: [inc(2)],
1237
+ expectedVersion: i === at ? 5 : 0
1238
+ })),
1239
+ meta
1240
+ );
1241
+ await expect8(refused).rejects.toBeInstanceOf(ConcurrencyError);
1242
+ await expect8(refused).rejects.toMatchObject({ stream: streams[at] });
1243
+ for (const stream of streams) expect8(await count(stream)).toBe(1);
1244
+ }
1245
+ });
1246
+ it7("refuses a stream named twice and an append with no events, storing nothing", async () => {
1247
+ const a = `across-twice-${uid()}`;
1248
+ const b = `across-empty-${uid()}`;
1249
+ await expect8(
1250
+ store.commit(
1251
+ [
1252
+ { stream: a, msgs: [inc(1)] },
1253
+ { stream: a, msgs: [inc(2)] }
1254
+ ],
1255
+ meta
1256
+ )
1257
+ ).rejects.toThrow(`stream "${a}" is named twice`);
1258
+ await expect8(
1259
+ store.commit(
1260
+ [
1261
+ { stream: a, msgs: [inc(1)] },
1262
+ { stream: b, msgs: [] }
1263
+ ],
1264
+ meta
1265
+ )
1266
+ ).rejects.toThrow(`the append to "${b}" has no events`);
1267
+ expect8(await count(a)).toBe(0);
1268
+ expect8(await count(b)).toBe(0);
1269
+ });
1270
+ it7("commits nothing for no appends", async () => {
1271
+ expect8(await store.commit([], meta)).toEqual([]);
1272
+ });
1273
+ it7("lands an unguarded append beside guarded ones", async () => {
1274
+ const a = `across-guarded-${uid()}`;
1275
+ const b = `across-free-${uid()}`;
1276
+ await store.commit(
1277
+ [{ stream: b, msgs: [inc(1)] }],
1278
+ meta
1279
+ );
1280
+ const committed = await store.commit(
1281
+ [
1282
+ { stream: a, msgs: [inc(1)], expectedVersion: -1 },
1283
+ { stream: b, msgs: [inc(2)] }
1284
+ ],
1285
+ meta
1286
+ );
1287
+ expect8(committed.map((e) => [e.stream, e.version])).toEqual([
1288
+ [a, 0],
1289
+ [b, 1]
1290
+ ]);
1291
+ });
1292
+ it7("stores every event of every append, whole", async () => {
1293
+ const a = `across-whole-a-${uid()}`;
1294
+ const b = `across-whole-b-${uid()}`;
1295
+ const across = make_meta({ stream: a });
1296
+ const committed = await store.commit(
1297
+ [
1298
+ { stream: a, msgs: [inc(1)] },
1299
+ { stream: b, msgs: [inc(2), dec(3)] }
1300
+ ],
1301
+ across
1302
+ );
1303
+ const shape = (e) => [
1304
+ e.name,
1305
+ e.data,
1306
+ e.meta
1307
+ ];
1308
+ const expected = [
1309
+ ["Incremented", { amount: 2 }, across],
1310
+ ["Decremented", { amount: 3 }, across]
1311
+ ];
1312
+ expect8(committed.filter((e) => e.stream === b).map(shape)).toEqual(
1313
+ expected
1314
+ );
1315
+ const stored = [];
1316
+ await store.query((e) => stored.push(e), {
1317
+ stream: b,
1318
+ stream_exact: true
1319
+ });
1320
+ expect8(stored.map(shape)).toEqual(expected);
1321
+ });
1322
+ it7.skipIf(!caps.pii_isolation)(
1323
+ "keeps the sensitive fields of a later append",
1324
+ async () => {
1325
+ const a = `across-pii-a-${uid()}`;
1326
+ const b = `across-pii-b-${uid()}`;
1327
+ await store.commit(
1328
+ [
1329
+ { stream: a, msgs: [inc(1)] },
1330
+ { stream: b, msgs: [{ ...inc(2), pii: { email: "e" } }] }
1331
+ ],
1332
+ make_meta({ stream: a })
1333
+ );
1334
+ const stored = [];
1335
+ await store.query((e) => stored.push(e.pii), {
1336
+ stream: b,
1337
+ stream_exact: true
1338
+ });
1339
+ expect8(stored).toEqual([{ email: "e" }]);
1340
+ }
1341
+ );
1342
+ it7("leaves nothing of the commit that lost a race, on any of its streams", async () => {
1343
+ const shared = `across-all-shared-${uid()}`;
1344
+ const [x, y] = [`across-all-x-${uid()}`, `across-all-y-${uid()}`];
1345
+ const race = (own) => store.commit(
1346
+ [
1347
+ { stream: own, msgs: [inc(1)], expectedVersion: -1 },
1348
+ { stream: shared, msgs: [inc(1)], expectedVersion: -1 }
1349
+ ],
1350
+ meta
1351
+ );
1352
+ const rs = await Promise.allSettled([race(x), race(y)]);
1353
+ expect8(rs.filter((r) => r.status === "fulfilled")).toHaveLength(1);
1354
+ expect8(await count(x) + await count(y)).toBe(1);
1355
+ });
1356
+ it7("lets one of two racing commits on a shared stream win, and both on disjoint streams", async () => {
1357
+ const shared = `across-shared-${uid()}`;
1358
+ const [x, y] = [`across-x-${uid()}`, `across-y-${uid()}`];
1359
+ const race = (own, other) => store.commit(
1360
+ [
1361
+ { stream: own, msgs: [inc(1)], expectedVersion: -1 },
1362
+ { stream: other, msgs: [inc(1)], expectedVersion: -1 }
1363
+ ],
1364
+ meta
1365
+ );
1366
+ const shared_race = await Promise.allSettled([
1367
+ race(x, shared),
1368
+ race(y, shared)
1369
+ ]);
1370
+ expect8(
1371
+ shared_race.filter((r2) => r2.status === "fulfilled")
1372
+ ).toHaveLength(1);
1373
+ const lost = shared_race.find((r2) => r2.status === "rejected");
1374
+ expect8(lost.reason).toBeInstanceOf(
1375
+ ConcurrencyError
1376
+ );
1377
+ expect8(await count(shared)).toBe(1);
1378
+ const [p, q, r, t] = [0, 1, 2, 3].map((i) => `across-d${i}-${uid()}`);
1379
+ const disjoint = await Promise.allSettled([race(p, q), race(r, t)]);
1380
+ expect8(disjoint.every((d) => d.status === "fulfilled")).toBe(true);
1381
+ });
1382
+ });
1383
+ describe8("what a commit, guard or truncate must not get wrong", () => {
1384
+ const edge_uid = () => uid();
1385
+ const E = (data = { a: 1 }, pii) => ({
1386
+ name: "E",
1387
+ data,
1388
+ ...pii ? { pii } : {}
1389
+ });
1390
+ const edge_meta = (correlation = "m") => ({ correlation, causation: {} });
1391
+ const rows = async (query) => {
1392
+ const found = [];
1393
+ await store.query((e) => found.push(e), query);
1394
+ return found;
1395
+ };
1396
+ const one = (stream) => ({ stream, stream_exact: true });
1397
+ it7("names the FIRST stale stream when two are stale", async () => {
1398
+ const [a, b] = [`a-${edge_uid()}`, `b-${edge_uid()}`];
1399
+ const e = await store.commit(
1400
+ [
1401
+ { stream: a, msgs: [E()], expectedVersion: 3 },
1402
+ { stream: b, msgs: [E()], expectedVersion: 4 }
1403
+ ],
1404
+ edge_meta()
1405
+ ).catch((x) => x);
1406
+ expect8(e).toBeInstanceOf(ConcurrencyError);
1407
+ expect8(e.stream).toBe(a);
1408
+ });
1409
+ it7("checks a stale expectedVersion of 0 inside a multi-stream commit", async () => {
1410
+ const [a, b] = [`a-${edge_uid()}`, `b-${edge_uid()}`];
1411
+ await store.commit(
1412
+ [{ stream: b, msgs: [E(), E()] }],
1413
+ edge_meta()
1414
+ );
1415
+ await expect8(
1416
+ store.commit(
1417
+ [
1418
+ { stream: a, msgs: [E()], expectedVersion: -1 },
1419
+ { stream: b, msgs: [E()], expectedVersion: 0 }
1420
+ ],
1421
+ edge_meta()
1422
+ )
1423
+ ).rejects.toBeInstanceOf(ConcurrencyError);
1424
+ expect8(await rows(one(a))).toHaveLength(0);
1425
+ });
1426
+ it7("attaches meta to every append, not just the last", async () => {
1427
+ const ss = [0, 1, 2].map((i) => `m${i}-${edge_uid()}`);
1428
+ const c = `corr-${edge_uid()}`;
1429
+ await store.commit(
1430
+ ss.map((stream) => ({ stream, msgs: [E()] })),
1431
+ edge_meta(c)
1432
+ );
1433
+ for (const stream of ss)
1434
+ expect8((await rows(one(stream)))[0].meta.correlation).toBe(c);
1435
+ });
1436
+ it7("refuses a stream named twice when not adjacent", async () => {
1437
+ const [a, b] = [`a-${edge_uid()}`, `b-${edge_uid()}`];
1438
+ await expect8(
1439
+ store.commit(
1440
+ [
1441
+ { stream: a, msgs: [E()] },
1442
+ { stream: b, msgs: [E()] },
1443
+ { stream: a, msgs: [E()] }
1444
+ ],
1445
+ edge_meta()
1446
+ )
1447
+ ).rejects.toThrow("named twice");
1448
+ expect8(await rows(one(a))).toHaveLength(0);
1449
+ });
1450
+ it7("refuses a lone empty append (a version check alone)", async () => {
1451
+ const a = `a-${edge_uid()}`;
1452
+ await expect8(
1453
+ store.commit(
1454
+ [{ stream: a, msgs: [], expectedVersion: -1 }],
1455
+ edge_meta()
1456
+ )
1457
+ ).rejects.toThrow("no events");
1458
+ });
1459
+ it7("refuses an empty FIRST append", async () => {
1460
+ const [a, b] = [`a-${edge_uid()}`, `b-${edge_uid()}`];
1461
+ await expect8(
1462
+ store.commit(
1463
+ [
1464
+ { stream: a, msgs: [] },
1465
+ { stream: b, msgs: [E()] }
1466
+ ],
1467
+ edge_meta()
1468
+ )
1469
+ ).rejects.toThrow("no events");
1470
+ expect8(await rows(one(b))).toHaveLength(0);
1471
+ });
1472
+ it7("keeps the sensitive fields of the FIRST append", async () => {
1473
+ const [a, b] = [`a-${edge_uid()}`, `b-${edge_uid()}`];
1474
+ await store.commit(
1475
+ [
1476
+ { stream: a, msgs: [E({}, { email: "x" })] },
1477
+ { stream: b, msgs: [E()] }
1478
+ ],
1479
+ edge_meta()
1480
+ );
1481
+ expect8((await rows(one(a)))[0].pii).toEqual({ email: "x" });
1482
+ });
1483
+ it7("a guard below a trailing snapshot is stale", async () => {
1484
+ const a = `a-${edge_uid()}`;
1485
+ await store.commit([{ stream: a, msgs: [E()] }], edge_meta());
1486
+ await store.commit(
1487
+ [
1488
+ {
1489
+ stream: a,
1490
+ msgs: [{ name: SNAP_EVENT2, data: {} }],
1491
+ expectedVersion: 0
1492
+ }
1493
+ ],
1494
+ edge_meta()
1495
+ );
1496
+ await expect8(
1497
+ store.commit(
1498
+ [{ stream: a, msgs: [E()], expectedVersion: 0 }],
1499
+ edge_meta()
1500
+ )
1501
+ ).rejects.toBeInstanceOf(ConcurrencyError);
1502
+ });
1503
+ it7("a guard below a trailing tombstone is stale (what stops a writer during a close)", async () => {
1504
+ const a = `a-${edge_uid()}`;
1505
+ await store.commit([{ stream: a, msgs: [E()] }], edge_meta());
1506
+ await store.commit(
1507
+ [
1508
+ {
1509
+ stream: a,
1510
+ msgs: [{ name: "__tombstone__", data: {} }],
1511
+ expectedVersion: 0
1512
+ }
1513
+ ],
1514
+ edge_meta()
1515
+ );
1516
+ await expect8(
1517
+ store.commit(
1518
+ [{ stream: a, msgs: [E()], expectedVersion: 0 }],
1519
+ edge_meta()
1520
+ )
1521
+ ).rejects.toBeInstanceOf(ConcurrencyError);
1522
+ });
1523
+ it7("a commit after a full truncate continues from the seed at version 0", async () => {
1524
+ const a = `a-${edge_uid()}`;
1525
+ await store.commit(
1526
+ [{ stream: a, msgs: [E(), E(), E()] }],
1527
+ edge_meta()
1528
+ );
1529
+ await store.truncate([{ stream: a, snapshot: { n: 3 } }]);
1530
+ const [c] = await store.commit(
1531
+ [{ stream: a, msgs: [E()], expectedVersion: 0 }],
1532
+ edge_meta()
1533
+ );
1534
+ expect8(c.version).toBe(1);
1535
+ });
1536
+ it7("seeds a full truncate at version 0, carrying the meta it was given", async () => {
1537
+ const a = `a-${edge_uid()}`;
1538
+ await store.commit(
1539
+ [{ stream: a, msgs: [E(), E(), E()] }],
1540
+ edge_meta()
1541
+ );
1542
+ const c = `close-${edge_uid()}`;
1543
+ const r = await store.truncate([
1544
+ { stream: a, snapshot: { n: 3 }, meta: edge_meta(c) }
1545
+ ]);
1546
+ expect8(r.get(a).committed.version).toBe(0);
1547
+ const [seed] = await rows({ ...one(a), with_snaps: true });
1548
+ expect8(seed.version).toBe(0);
1549
+ expect8(seed.meta.correlation).toBe(c);
1550
+ });
1551
+ });
1196
1552
  describe8("commit", () => {
1197
1553
  it7("returns committed events with sequenced ids and versions", async () => {
1198
1554
  const s = `commit-seq-${uid()}`;
1199
1555
  const committed = await store.commit(
1200
- s,
1201
- [inc(1), inc(2), dec(3)],
1556
+ [{ stream: s, msgs: [inc(1), inc(2), dec(3)] }],
1202
1557
  make_meta({ stream: s })
1203
1558
  );
1204
1559
  expect8(committed).toHaveLength(3);
@@ -1215,8 +1570,7 @@ var runStoreTck = (options) => {
1215
1570
  const s = `commit-meta-${uid()}`;
1216
1571
  const correlation = `cor-${uid()}`;
1217
1572
  const committed = await store.commit(
1218
- s,
1219
- [inc(1)],
1573
+ [{ stream: s, msgs: [inc(1)] }],
1220
1574
  make_meta({ stream: s, correlation })
1221
1575
  );
1222
1576
  expect8(committed[0].stream).toBe(s);
@@ -1225,64 +1579,50 @@ var runStoreTck = (options) => {
1225
1579
  it7("throws ConcurrencyError when expectedVersion is wrong", async () => {
1226
1580
  const s = `commit-cc-${uid()}`;
1227
1581
  await store.commit(
1228
- s,
1229
- [inc(1)],
1582
+ [{ stream: s, msgs: [inc(1)] }],
1230
1583
  make_meta({ stream: s })
1231
1584
  );
1232
1585
  await store.commit(
1233
- s,
1234
- [inc(1)],
1235
- make_meta({ stream: s }),
1236
- 0
1586
+ [{ stream: s, msgs: [inc(1)], expectedVersion: 0 }],
1587
+ make_meta({ stream: s })
1237
1588
  );
1238
1589
  await expect8(
1239
- store.commit(s, [inc(1)], make_meta({ stream: s }), 0)
1590
+ store.commit(
1591
+ [{ stream: s, msgs: [inc(1)], expectedVersion: 0 }],
1592
+ make_meta({ stream: s })
1593
+ )
1240
1594
  ).rejects.toBeInstanceOf(ConcurrencyError);
1241
1595
  });
1242
1596
  it7("treats expectedVersion -1 as an empty stream", async () => {
1243
1597
  const s = `commit-new-cc-${uid()}`;
1244
1598
  const [first] = await store.commit(
1245
- s,
1246
- [inc(1)],
1247
- make_meta({ stream: s }),
1248
- -1
1249
- );
1250
- expect8(first.version).toBe(0);
1251
- await expect8(
1252
- store.commit(s, [inc(1)], make_meta({ stream: s }), -1)
1253
- ).rejects.toBeInstanceOf(ConcurrencyError);
1254
- });
1255
- it7("checks expectedVersion even when committing no events", async () => {
1256
- const s = `commit-empty-cc-${uid()}`;
1257
- await store.commit(
1258
- s,
1259
- [inc(1)],
1599
+ [{ stream: s, msgs: [inc(1)], expectedVersion: -1 }],
1260
1600
  make_meta({ stream: s })
1261
1601
  );
1602
+ expect8(first.version).toBe(0);
1262
1603
  await expect8(
1263
- store.commit(s, [], make_meta({ stream: s }), 5)
1604
+ store.commit(
1605
+ [{ stream: s, msgs: [inc(1)], expectedVersion: -1 }],
1606
+ make_meta({ stream: s })
1607
+ )
1264
1608
  ).rejects.toBeInstanceOf(ConcurrencyError);
1265
- expect8(
1266
- await store.commit(s, [], make_meta({ stream: s }), 0)
1267
- ).toEqual([]);
1268
1609
  });
1269
1610
  it7("takes concurrent commits, reads and writes on one instance", async () => {
1270
1611
  const x = `commit-concurrent-x-${uid()}`;
1271
1612
  const y = `commit-concurrent-y-${uid()}`;
1272
1613
  const [first, second, other, read, subscribed] = await Promise.allSettled([
1273
1614
  store.commit(
1274
- x,
1275
- [inc(1)],
1276
- make_meta({ stream: x }),
1277
- -1
1615
+ [{ stream: x, msgs: [inc(1)], expectedVersion: -1 }],
1616
+ make_meta({ stream: x })
1278
1617
  ),
1279
1618
  store.commit(
1280
- x,
1281
- [inc(2)],
1282
- make_meta({ stream: x }),
1283
- -1
1619
+ [{ stream: x, msgs: [inc(2)], expectedVersion: -1 }],
1620
+ make_meta({ stream: x })
1621
+ ),
1622
+ store.commit(
1623
+ [{ stream: y, msgs: [inc(1)] }],
1624
+ make_meta({ stream: y })
1284
1625
  ),
1285
- store.commit(y, [inc(1)], make_meta({ stream: y })),
1286
1626
  store.query(() => {
1287
1627
  }, { stream: x, stream_exact: true }),
1288
1628
  // A source nothing writes, so later claims never see it.
@@ -1304,12 +1644,14 @@ var runStoreTck = (options) => {
1304
1644
  it7("preserves prior events when a concurrent commit is rejected", async () => {
1305
1645
  const s = `commit-cc-preserve-${uid()}`;
1306
1646
  await store.commit(
1307
- s,
1308
- [inc(1), inc(2)],
1647
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
1309
1648
  make_meta({ stream: s })
1310
1649
  );
1311
1650
  await expect8(
1312
- store.commit(s, [inc(3)], make_meta({ stream: s }), 0)
1651
+ store.commit(
1652
+ [{ stream: s, msgs: [inc(3)], expectedVersion: 0 }],
1653
+ make_meta({ stream: s })
1654
+ )
1313
1655
  ).rejects.toBeInstanceOf(ConcurrencyError);
1314
1656
  const found = await collect(store, { stream: s, stream_exact: true });
1315
1657
  expect8(found).toHaveLength(2);
@@ -1321,13 +1663,11 @@ var runStoreTck = (options) => {
1321
1663
  const s2 = `q-s2-${uid()}`;
1322
1664
  const cor = `q-cor-${uid()}`;
1323
1665
  await store.commit(
1324
- s1,
1325
- [inc(1), dec(1)],
1666
+ [{ stream: s1, msgs: [inc(1), dec(1)] }],
1326
1667
  make_meta({ stream: s1, correlation: cor })
1327
1668
  );
1328
1669
  await store.commit(
1329
- s2,
1330
- [inc(2), dec(2), reset()],
1670
+ [{ stream: s2, msgs: [inc(2), dec(2), reset()] }],
1331
1671
  make_meta({ stream: s2, correlation: cor })
1332
1672
  );
1333
1673
  const by_stream = await collect(store, {
@@ -1353,8 +1693,7 @@ var runStoreTck = (options) => {
1353
1693
  it7("excludes snapshots when called with no query object", async () => {
1354
1694
  const s = `q-nofilter-${uid()}`;
1355
1695
  await store.commit(
1356
- s,
1357
- [{ name: SNAP_EVENT2, data: { count: 0 } }],
1696
+ [{ stream: s, msgs: [{ name: SNAP_EVENT2, data: { count: 0 } }] }],
1358
1697
  make_meta({ stream: s })
1359
1698
  );
1360
1699
  const names = [];
@@ -1366,18 +1705,15 @@ var runStoreTck = (options) => {
1366
1705
  it7("with_snaps resumes from the latest snapshot per stream", async () => {
1367
1706
  const s = `q-snap-${uid()}`;
1368
1707
  await store.commit(
1369
- s,
1370
- [inc(1), inc(1)],
1708
+ [{ stream: s, msgs: [inc(1), inc(1)] }],
1371
1709
  make_meta({ stream: s })
1372
1710
  );
1373
1711
  const [snap] = await store.commit(
1374
- s,
1375
- [{ name: SNAP_EVENT2, data: { count: 2 } }],
1712
+ [{ stream: s, msgs: [{ name: SNAP_EVENT2, data: { count: 2 } }] }],
1376
1713
  make_meta({ stream: s })
1377
1714
  );
1378
1715
  await store.commit(
1379
- s,
1380
- [inc(1), inc(1), inc(1)],
1716
+ [{ stream: s, msgs: [inc(1), inc(1), inc(1)] }],
1381
1717
  make_meta({ stream: s })
1382
1718
  );
1383
1719
  const from_snap = await collect(store, {
@@ -1398,8 +1734,7 @@ var runStoreTck = (options) => {
1398
1734
  expect8(after_snap).toHaveLength(3);
1399
1735
  const s2 = `q-nosnap-${uid()}`;
1400
1736
  await store.commit(
1401
- s2,
1402
- [inc(1), inc(1)],
1737
+ [{ stream: s2, msgs: [inc(1), inc(1)] }],
1403
1738
  make_meta({ stream: s2 })
1404
1739
  );
1405
1740
  const full = await collect(store, {
@@ -1412,18 +1747,15 @@ var runStoreTck = (options) => {
1412
1747
  it7("with_snaps applies the resume floor on a backward scan too", async () => {
1413
1748
  const s = `q-snap-back-${uid()}`;
1414
1749
  await store.commit(
1415
- s,
1416
- [inc(1), inc(1)],
1750
+ [{ stream: s, msgs: [inc(1), inc(1)] }],
1417
1751
  make_meta({ stream: s })
1418
1752
  );
1419
1753
  const [snap] = await store.commit(
1420
- s,
1421
- [{ name: SNAP_EVENT2, data: { count: 2 } }],
1754
+ [{ stream: s, msgs: [{ name: SNAP_EVENT2, data: { count: 2 } }] }],
1422
1755
  make_meta({ stream: s })
1423
1756
  );
1424
1757
  await store.commit(
1425
- s,
1426
- [inc(1), inc(1), inc(1)],
1758
+ [{ stream: s, msgs: [inc(1), inc(1), inc(1)] }],
1427
1759
  make_meta({ stream: s })
1428
1760
  );
1429
1761
  const backward = await collect(store, {
@@ -1437,8 +1769,7 @@ var runStoreTck = (options) => {
1437
1769
  expect8(backward.every((e) => e.id >= snap.id)).toBe(true);
1438
1770
  const s2 = `q-nosnap-back-${uid()}`;
1439
1771
  await store.commit(
1440
- s2,
1441
- [inc(1), inc(1)],
1772
+ [{ stream: s2, msgs: [inc(1), inc(1)] }],
1442
1773
  make_meta({ stream: s2 })
1443
1774
  );
1444
1775
  const full = await collect(store, {
@@ -1452,8 +1783,7 @@ var runStoreTck = (options) => {
1452
1783
  it7("supports backward traversal", async () => {
1453
1784
  const s = `q-back-${uid()}`;
1454
1785
  const committed = await store.commit(
1455
- s,
1456
- [inc(1), inc(2), inc(3)],
1786
+ [{ stream: s, msgs: [inc(1), inc(2), inc(3)] }],
1457
1787
  make_meta({ stream: s })
1458
1788
  );
1459
1789
  const forward = await collect(store, { stream: s, stream_exact: true });
@@ -1478,8 +1808,7 @@ var runStoreTck = (options) => {
1478
1808
  it7("after/before bound the id range", async () => {
1479
1809
  const s = `q-bounds-${uid()}`;
1480
1810
  const committed = await store.commit(
1481
- s,
1482
- [inc(1), inc(2), inc(3), inc(4)],
1811
+ [{ stream: s, msgs: [inc(1), inc(2), inc(3), inc(4)] }],
1483
1812
  make_meta({ stream: s })
1484
1813
  );
1485
1814
  const after_first = await collect(store, {
@@ -1502,8 +1831,7 @@ var runStoreTck = (options) => {
1502
1831
  it7("names:[] matches no events", async () => {
1503
1832
  const s = `q-names-empty-${uid()}`;
1504
1833
  await store.commit(
1505
- s,
1506
- [inc(1), dec(1)],
1834
+ [{ stream: s, msgs: [inc(1), dec(1)] }],
1507
1835
  make_meta({ stream: s })
1508
1836
  );
1509
1837
  const none = await collect(store, {
@@ -1518,8 +1846,7 @@ var runStoreTck = (options) => {
1518
1846
  it7("after:0 and before:0 are honored as id bounds", async () => {
1519
1847
  const s = `q-zero-bound-${uid()}`;
1520
1848
  await store.commit(
1521
- s,
1522
- [inc(1), inc(2), inc(3)],
1849
+ [{ stream: s, msgs: [inc(1), inc(2), inc(3)] }],
1523
1850
  make_meta({ stream: s })
1524
1851
  );
1525
1852
  expect8(
@@ -1550,8 +1877,7 @@ var runStoreTck = (options) => {
1550
1877
  it7("created_after/created_before filter by timestamp", async () => {
1551
1878
  const s = `q-ts-${uid()}`;
1552
1879
  const committed = await store.commit(
1553
- s,
1554
- [inc(1)],
1880
+ [{ stream: s, msgs: [inc(1)] }],
1555
1881
  make_meta({ stream: s })
1556
1882
  );
1557
1883
  const ts = committed[0].created.getTime();
@@ -1574,8 +1900,7 @@ var runStoreTck = (options) => {
1574
1900
  it7("created bounds exclude the event they are pinned to", async () => {
1575
1901
  const s = `q-ts-self-${uid()}`;
1576
1902
  const committed = await store.commit(
1577
- s,
1578
- [inc(1)],
1903
+ [{ stream: s, msgs: [inc(1)] }],
1579
1904
  make_meta({ stream: s })
1580
1905
  );
1581
1906
  const own = committed[0].created;
@@ -1595,8 +1920,7 @@ var runStoreTck = (options) => {
1595
1920
  it7("backward traversal short-circuits at `after` id boundary", async () => {
1596
1921
  const s = `q-back-after-${uid()}`;
1597
1922
  const committed = await store.commit(
1598
- s,
1599
- [inc(1), inc(2), inc(3)],
1923
+ [{ stream: s, msgs: [inc(1), inc(2), inc(3)] }],
1600
1924
  make_meta({ stream: s })
1601
1925
  );
1602
1926
  const got = await collect(store, {
@@ -1613,8 +1937,7 @@ var runStoreTck = (options) => {
1613
1937
  it7("backward traversal short-circuits at `created_after` boundary", async () => {
1614
1938
  const s = `q-back-cafter-${uid()}`;
1615
1939
  await store.commit(
1616
- s,
1617
- [inc(1)],
1940
+ [{ stream: s, msgs: [inc(1)] }],
1618
1941
  make_meta({ stream: s })
1619
1942
  );
1620
1943
  const future = new Date(Date.now() + 6e4);
@@ -1629,8 +1952,7 @@ var runStoreTck = (options) => {
1629
1952
  it7("backward traversal honors created_before by skipping newer events", async () => {
1630
1953
  const s = `q-back-ts-${uid()}`;
1631
1954
  const committed = await store.commit(
1632
- s,
1633
- [inc(1)],
1955
+ [{ stream: s, msgs: [inc(1)] }],
1634
1956
  make_meta()
1635
1957
  );
1636
1958
  const past = new Date(committed[0].created.getTime() - 6e4);
@@ -1647,13 +1969,11 @@ var runStoreTck = (options) => {
1647
1969
  const a = `q-exact-${tag}`;
1648
1970
  const b = `q-exact-${tag}-extra`;
1649
1971
  await store.commit(
1650
- a,
1651
- [inc(1)],
1972
+ [{ stream: a, msgs: [inc(1)] }],
1652
1973
  make_meta({ stream: a })
1653
1974
  );
1654
1975
  await store.commit(
1655
- b,
1656
- [inc(2)],
1976
+ [{ stream: b, msgs: [inc(2)] }],
1657
1977
  make_meta({ stream: b })
1658
1978
  );
1659
1979
  const exact = await collect(store, { stream: a, stream_exact: true });
@@ -1665,13 +1985,11 @@ var runStoreTck = (options) => {
1665
1985
  const inner = `qr-${tag}-inner`;
1666
1986
  const longer = `qr-${tag}-inner-extra`;
1667
1987
  await store.commit(
1668
- inner,
1669
- [inc(1)],
1988
+ [{ stream: inner, msgs: [inc(1)] }],
1670
1989
  make_meta({ stream: inner })
1671
1990
  );
1672
1991
  await store.commit(
1673
- longer,
1674
- [inc(2)],
1992
+ [{ stream: longer, msgs: [inc(2)] }],
1675
1993
  make_meta({ stream: longer })
1676
1994
  );
1677
1995
  const got = await collect(store, { stream: `qr-${tag}-inner` });
@@ -1682,13 +2000,11 @@ var runStoreTck = (options) => {
1682
2000
  const inner = `qr-${tag}-anchor`;
1683
2001
  const longer = `qr-${tag}-anchor-extra`;
1684
2002
  await store.commit(
1685
- inner,
1686
- [inc(1)],
2003
+ [{ stream: inner, msgs: [inc(1)] }],
1687
2004
  make_meta({ stream: inner })
1688
2005
  );
1689
2006
  await store.commit(
1690
- longer,
1691
- [inc(2)],
2007
+ [{ stream: longer, msgs: [inc(2)] }],
1692
2008
  make_meta({ stream: longer })
1693
2009
  );
1694
2010
  const got = await collect(store, { stream: `^qr-${tag}-anchor$` });
@@ -1701,18 +2017,15 @@ var runStoreTck = (options) => {
1701
2017
  const b = `qr-${tag}-pfx-b`;
1702
2018
  const other = `zz-${tag}-other`;
1703
2019
  await store.commit(
1704
- a,
1705
- [inc(1)],
2020
+ [{ stream: a, msgs: [inc(1)] }],
1706
2021
  make_meta({ stream: a })
1707
2022
  );
1708
2023
  await store.commit(
1709
- b,
1710
- [inc(2)],
2024
+ [{ stream: b, msgs: [inc(2)] }],
1711
2025
  make_meta({ stream: b })
1712
2026
  );
1713
2027
  await store.commit(
1714
- other,
1715
- [inc(3)],
2028
+ [{ stream: other, msgs: [inc(3)] }],
1716
2029
  make_meta({ stream: other })
1717
2030
  );
1718
2031
  const got = await collect(store, { stream: `^qr-${tag}-pfx-` });
@@ -1723,8 +2036,7 @@ var runStoreTck = (options) => {
1723
2036
  const seed_streams = async (streams) => {
1724
2037
  for (const s of streams)
1725
2038
  await store.commit(
1726
- s,
1727
- [inc(1)],
2039
+ [{ stream: s, msgs: [inc(1)] }],
1728
2040
  make_meta({ stream: s })
1729
2041
  );
1730
2042
  };
@@ -1903,8 +2215,7 @@ var runStoreTck = (options) => {
1903
2215
  const src = `fresh-first-src-${uid()}`;
1904
2216
  await store.subscribe([{ stream: s, source: src }]);
1905
2217
  const [first] = await store.commit(
1906
- src,
1907
- [inc(1)],
2218
+ [{ stream: src, msgs: [inc(1)] }],
1908
2219
  make_meta({ stream: src })
1909
2220
  );
1910
2221
  await store.subscribe([{ stream: s, correlated_at: first.id }]);
@@ -2093,8 +2404,7 @@ var runStoreTck = (options) => {
2093
2404
  const src = `ws-unmarked-src-${uid()}`;
2094
2405
  await store.subscribe([{ stream: s, source: src }]);
2095
2406
  const [event] = await store.commit(
2096
- src,
2097
- [inc(1)],
2407
+ [{ stream: src, msgs: [inc(1)] }],
2098
2408
  make_meta({ stream: src })
2099
2409
  );
2100
2410
  expect8(await claimable(s)).toBe(false);
@@ -2126,8 +2436,7 @@ var runStoreTck = (options) => {
2126
2436
  const s = `claim-${uid()}`;
2127
2437
  await store.subscribe([{ stream: s }]);
2128
2438
  await store.commit(
2129
- s,
2130
- [inc(1)],
2439
+ [{ stream: s, msgs: [inc(1)] }],
2131
2440
  make_meta({ stream: s })
2132
2441
  );
2133
2442
  const by = `worker-${uid()}`;
@@ -2150,8 +2459,7 @@ var runStoreTck = (options) => {
2150
2459
  const other = `claim-other-${uid()}`;
2151
2460
  await store.subscribe([{ stream: s }]);
2152
2461
  await store.commit(
2153
- s,
2154
- [inc(1)],
2462
+ [{ stream: s, msgs: [inc(1)] }],
2155
2463
  make_meta({ stream: s })
2156
2464
  );
2157
2465
  await correlate();
@@ -2160,8 +2468,7 @@ var runStoreTck = (options) => {
2160
2468
  expect8(targetA).toBeDefined();
2161
2469
  await store.subscribe([{ stream: other }]);
2162
2470
  await store.commit(
2163
- other,
2164
- [inc(2)],
2471
+ [{ stream: other, msgs: [inc(2)] }],
2165
2472
  make_meta({ stream: other })
2166
2473
  );
2167
2474
  await correlate();
@@ -2174,8 +2481,7 @@ var runStoreTck = (options) => {
2174
2481
  const s = `claim-stolen-${uid()}`;
2175
2482
  await store.subscribe([{ stream: s }]);
2176
2483
  await store.commit(
2177
- s,
2178
- [inc(1), inc(2)],
2484
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
2179
2485
  make_meta({ stream: s })
2180
2486
  );
2181
2487
  await correlate();
@@ -2201,8 +2507,7 @@ var runStoreTck = (options) => {
2201
2507
  const s = `claim-dual-${uid()}`;
2202
2508
  await store.subscribe([{ stream: s }]);
2203
2509
  await store.commit(
2204
- s,
2205
- [inc(1), inc(2)],
2510
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
2206
2511
  make_meta({ stream: s })
2207
2512
  );
2208
2513
  await correlate();
@@ -2231,13 +2536,11 @@ var runStoreTck = (options) => {
2231
2536
  ]);
2232
2537
  for (const stream of highs)
2233
2538
  await fresh.commit(
2234
- stream,
2235
- [inc(1)],
2539
+ [{ stream, msgs: [inc(1)] }],
2236
2540
  make_meta({ stream })
2237
2541
  );
2238
2542
  await fresh.commit(
2239
- low,
2240
- [inc(1)],
2543
+ [{ stream: low, msgs: [inc(1)] }],
2241
2544
  make_meta({ stream: low })
2242
2545
  );
2243
2546
  const by = `w-${uid()}`;
@@ -2253,8 +2556,7 @@ var runStoreTck = (options) => {
2253
2556
  for (const l of leases) {
2254
2557
  await fresh.ack([{ ...l, at: l.at + 1 }]);
2255
2558
  await fresh.commit(
2256
- l.stream,
2257
- [inc(1)],
2559
+ [{ stream: l.stream, msgs: [inc(1)] }],
2258
2560
  make_meta({ stream: l.stream })
2259
2561
  );
2260
2562
  }
@@ -2268,8 +2570,7 @@ var runStoreTck = (options) => {
2268
2570
  const s = `claim-dedup-${uid()}`;
2269
2571
  await store.subscribe([{ stream: s }]);
2270
2572
  await store.commit(
2271
- s,
2272
- [inc(1)],
2573
+ [{ stream: s, msgs: [inc(1)] }],
2273
2574
  make_meta({ stream: s })
2274
2575
  );
2275
2576
  await correlate();
@@ -2282,13 +2583,11 @@ var runStoreTck = (options) => {
2282
2583
  const sibling = `ack-sibling-${uid()}`;
2283
2584
  await store.subscribe([{ stream: s }, { stream: sibling }]);
2284
2585
  await store.commit(
2285
- s,
2286
- [inc(1)],
2586
+ [{ stream: s, msgs: [inc(1)] }],
2287
2587
  make_meta({ stream: s })
2288
2588
  );
2289
2589
  await store.commit(
2290
- sibling,
2291
- [inc(2)],
2590
+ [{ stream: sibling, msgs: [inc(2)] }],
2292
2591
  make_meta({ stream: sibling })
2293
2592
  );
2294
2593
  await correlate();
@@ -2338,8 +2637,7 @@ var runStoreTck = (options) => {
2338
2637
  const s = `lease-retry-${uid()}`;
2339
2638
  await fresh.subscribe([{ stream: s }]);
2340
2639
  await fresh.commit(
2341
- s,
2342
- [inc(1)],
2640
+ [{ stream: s, msgs: [inc(1)] }],
2343
2641
  make_meta({ stream: s })
2344
2642
  );
2345
2643
  await correlate(fresh);
@@ -2364,8 +2662,7 @@ var runStoreTck = (options) => {
2364
2662
  const s = `lease-zero-${uid()}`;
2365
2663
  await fresh.subscribe([{ stream: s }]);
2366
2664
  const [committed] = await fresh.commit(
2367
- s,
2368
- [inc(1)],
2665
+ [{ stream: s, msgs: [inc(1)] }],
2369
2666
  make_meta({ stream: s })
2370
2667
  );
2371
2668
  await correlate(fresh);
@@ -2394,8 +2691,7 @@ var runStoreTck = (options) => {
2394
2691
  const s = `lease-lag-${uid()}`;
2395
2692
  await fresh.subscribe([{ stream: s }]);
2396
2693
  await fresh.commit(
2397
- s,
2398
- [inc(1)],
2694
+ [{ stream: s, msgs: [inc(1)] }],
2399
2695
  make_meta({ stream: s })
2400
2696
  );
2401
2697
  await correlate(fresh);
@@ -2524,8 +2820,7 @@ var runStoreTck = (options) => {
2524
2820
  await fresh.subscribe(streams.map((stream) => ({ stream })));
2525
2821
  for (const stream of streams) {
2526
2822
  await fresh.commit(
2527
- stream,
2528
- [inc(1)],
2823
+ [{ stream, msgs: [inc(1)] }],
2529
2824
  make_meta({ stream })
2530
2825
  );
2531
2826
  }
@@ -2597,8 +2892,7 @@ var runStoreTck = (options) => {
2597
2892
  const s = `block-${uid()}`;
2598
2893
  await store.subscribe([{ stream: s }]);
2599
2894
  await store.commit(
2600
- s,
2601
- [inc(1)],
2895
+ [{ stream: s, msgs: [inc(1)] }],
2602
2896
  make_meta({ stream: s })
2603
2897
  );
2604
2898
  await correlate();
@@ -2620,8 +2914,7 @@ var runStoreTck = (options) => {
2620
2914
  const s = `block-returns-${uid()}`;
2621
2915
  await store.subscribe([{ stream: s }]);
2622
2916
  await store.commit(
2623
- s,
2624
- [inc(1)],
2917
+ [{ stream: s, msgs: [inc(1)] }],
2625
2918
  make_meta({ stream: s })
2626
2919
  );
2627
2920
  await correlate();
@@ -2646,8 +2939,7 @@ var runStoreTck = (options) => {
2646
2939
  const s = `block-wrong-${uid()}`;
2647
2940
  await store.subscribe([{ stream: s }]);
2648
2941
  await store.commit(
2649
- s,
2650
- [inc(1)],
2942
+ [{ stream: s, msgs: [inc(1)] }],
2651
2943
  make_meta({ stream: s })
2652
2944
  );
2653
2945
  await correlate();
@@ -2665,8 +2957,7 @@ var runStoreTck = (options) => {
2665
2957
  const s = `block-twice-${uid()}`;
2666
2958
  await store.subscribe([{ stream: s }]);
2667
2959
  await store.commit(
2668
- s,
2669
- [inc(1)],
2960
+ [{ stream: s, msgs: [inc(1)] }],
2670
2961
  make_meta({ stream: s })
2671
2962
  );
2672
2963
  await correlate();
@@ -2691,8 +2982,7 @@ var runStoreTck = (options) => {
2691
2982
  await store.subscribe([{ stream: s }, { stream: ctl }]);
2692
2983
  for (const st of [s, ctl])
2693
2984
  await store.commit(
2694
- st,
2695
- [inc(1)],
2985
+ [{ stream: st, msgs: [inc(1)] }],
2696
2986
  make_meta({ stream: st })
2697
2987
  );
2698
2988
  expect8(await store.defer([s], Date.now() + 36e5)).toBe(1);
@@ -2705,8 +2995,7 @@ var runStoreTck = (options) => {
2705
2995
  const s = `defer-clock-${uid()}`;
2706
2996
  await store.subscribe([{ stream: s }]);
2707
2997
  await store.commit(
2708
- s,
2709
- [inc(1)],
2998
+ [{ stream: s, msgs: [inc(1)] }],
2710
2999
  make_meta({ stream: s })
2711
3000
  );
2712
3001
  expect8(await store.defer([s], Date.now() + 6e4)).toBe(1);
@@ -2729,8 +3018,7 @@ var runStoreTck = (options) => {
2729
3018
  const s = `defer-past-${uid()}`;
2730
3019
  await store.subscribe([{ stream: s }]);
2731
3020
  await store.commit(
2732
- s,
2733
- [inc(1)],
3021
+ [{ stream: s, msgs: [inc(1)] }],
2734
3022
  make_meta({ stream: s })
2735
3023
  );
2736
3024
  expect8(await store.defer([s], Date.now() - 1e3)).toBe(1);
@@ -2746,8 +3034,7 @@ var runStoreTck = (options) => {
2746
3034
  const s = `defer-retry-${uid()}`;
2747
3035
  await store.subscribe([{ stream: s }]);
2748
3036
  await store.commit(
2749
- s,
2750
- [inc(1)],
3037
+ [{ stream: s, msgs: [inc(1)] }],
2751
3038
  make_meta({ stream: s })
2752
3039
  );
2753
3040
  await store.defer([s], Date.now() + 36e5);
@@ -2769,8 +3056,7 @@ var runStoreTck = (options) => {
2769
3056
  const s = `defer-reset-${uid()}`;
2770
3057
  await store.subscribe([{ stream: s }]);
2771
3058
  await store.commit(
2772
- s,
2773
- [inc(1)],
3059
+ [{ stream: s, msgs: [inc(1)] }],
2774
3060
  make_meta({ stream: s })
2775
3061
  );
2776
3062
  await store.defer([s], Date.now() + 36e5);
@@ -2785,8 +3071,7 @@ var runStoreTck = (options) => {
2785
3071
  await store.subscribe([{ stream: deferred }, { stream: plain }]);
2786
3072
  for (const s of [deferred, plain])
2787
3073
  await store.commit(
2788
- s,
2789
- [inc(1)],
3074
+ [{ stream: s, msgs: [inc(1)] }],
2790
3075
  make_meta({ stream: s })
2791
3076
  );
2792
3077
  const due = Date.now() + 36e5;
@@ -2808,15 +3093,13 @@ var runStoreTck = (options) => {
2808
3093
  await store.subscribe([{ stream: a }, { stream: b }]);
2809
3094
  for (const s of [a, b])
2810
3095
  await store.commit(
2811
- s,
2812
- [inc(1)],
3096
+ [{ stream: s, msgs: [inc(1)] }],
2813
3097
  make_meta({ stream: s })
2814
3098
  );
2815
3099
  const ctl = `defer-filterctl-${tag}`;
2816
3100
  await store.subscribe([{ stream: ctl }]);
2817
3101
  await store.commit(
2818
- ctl,
2819
- [inc(1)],
3102
+ [{ stream: ctl, msgs: [inc(1)] }],
2820
3103
  make_meta({ stream: ctl })
2821
3104
  );
2822
3105
  const n = await store.defer(
@@ -2838,8 +3121,7 @@ var runStoreTck = (options) => {
2838
3121
  const s = `defer-dup-${uid()}`;
2839
3122
  await store.subscribe([{ stream: s }]);
2840
3123
  await store.commit(
2841
- s,
2842
- [inc(1)],
3124
+ [{ stream: s, msgs: [inc(1)] }],
2843
3125
  make_meta({ stream: s })
2844
3126
  );
2845
3127
  expect8(await store.defer([s, s], Date.now() + 36e5)).toBe(1);
@@ -2851,13 +3133,11 @@ var runStoreTck = (options) => {
2851
3133
  const ctl = `ackdefer-ctl-${uid()}`;
2852
3134
  await store.subscribe([{ stream: s }, { stream: ctl }]);
2853
3135
  await store.commit(
2854
- s,
2855
- [inc(1)],
3136
+ [{ stream: s, msgs: [inc(1)] }],
2856
3137
  make_meta({ stream: s })
2857
3138
  );
2858
3139
  const [ctl_head] = await store.commit(
2859
- ctl,
2860
- [inc(1)],
3140
+ [{ stream: ctl, msgs: [inc(1)] }],
2861
3141
  make_meta({ stream: ctl })
2862
3142
  );
2863
3143
  await correlate();
@@ -2871,8 +3151,7 @@ var runStoreTck = (options) => {
2871
3151
  expect8(acked.find((l) => l.stream === ctl)).toBeDefined();
2872
3152
  expect8(acked.find((l) => l.stream === s)).toBeUndefined();
2873
3153
  await store.commit(
2874
- ctl,
2875
- [inc(2)],
3154
+ [{ stream: ctl, msgs: [inc(2)] }],
2876
3155
  make_meta({ stream: ctl })
2877
3156
  );
2878
3157
  await correlate();
@@ -2884,8 +3163,7 @@ var runStoreTck = (options) => {
2884
3163
  const s = `ackdefer-past-${uid()}`;
2885
3164
  await store.subscribe([{ stream: s }]);
2886
3165
  await store.commit(
2887
- s,
2888
- [inc(1)],
3166
+ [{ stream: s, msgs: [inc(1)] }],
2889
3167
  make_meta({ stream: s })
2890
3168
  );
2891
3169
  await correlate();
@@ -2904,8 +3182,7 @@ var runStoreTck = (options) => {
2904
3182
  const s = `ackdefer-retry-${uid()}`;
2905
3183
  await store.subscribe([{ stream: s }]);
2906
3184
  await store.commit(
2907
- s,
2908
- [inc(1)],
3185
+ [{ stream: s, msgs: [inc(1)] }],
2909
3186
  make_meta({ stream: s })
2910
3187
  );
2911
3188
  await correlate();
@@ -2924,13 +3201,11 @@ var runStoreTck = (options) => {
2924
3201
  const s = `ackadvance-${uid()}`;
2925
3202
  await store.subscribe([{ stream: s }]);
2926
3203
  const [e1] = await store.commit(
2927
- s,
2928
- [inc(1)],
3204
+ [{ stream: s, msgs: [inc(1)] }],
2929
3205
  make_meta({ stream: s })
2930
3206
  );
2931
3207
  await store.commit(
2932
- s,
2933
- [inc(1)],
3208
+ [{ stream: s, msgs: [inc(1)] }],
2934
3209
  make_meta({ stream: s })
2935
3210
  );
2936
3211
  await correlate();
@@ -2952,8 +3227,7 @@ var runStoreTck = (options) => {
2952
3227
  const s = `ackdefer-owner-${uid()}`;
2953
3228
  await store.subscribe([{ stream: s }]);
2954
3229
  await store.commit(
2955
- s,
2956
- [inc(1)],
3230
+ [{ stream: s, msgs: [inc(1)] }],
2957
3231
  make_meta({ stream: s })
2958
3232
  );
2959
3233
  await correlate();
@@ -2971,8 +3245,7 @@ var runStoreTck = (options) => {
2971
3245
  const s = `reset-${uid()}`;
2972
3246
  await store.subscribe([{ stream: s }]);
2973
3247
  await store.commit(
2974
- s,
2975
- [inc(1)],
3248
+ [{ stream: s, msgs: [inc(1)] }],
2976
3249
  make_meta({ stream: s })
2977
3250
  );
2978
3251
  await correlate();
@@ -2991,8 +3264,7 @@ var runStoreTck = (options) => {
2991
3264
  const s = `reset-blk-${uid()}`;
2992
3265
  await store.subscribe([{ stream: s }]);
2993
3266
  await store.commit(
2994
- s,
2995
- [inc(1)],
3267
+ [{ stream: s, msgs: [inc(1)] }],
2996
3268
  make_meta({ stream: s })
2997
3269
  );
2998
3270
  await correlate();
@@ -3014,8 +3286,7 @@ var runStoreTck = (options) => {
3014
3286
  const s = `reset-dup-${uid()}`;
3015
3287
  await store.subscribe([{ stream: s }]);
3016
3288
  await store.commit(
3017
- s,
3018
- [inc(1)],
3289
+ [{ stream: s, msgs: [inc(1)] }],
3019
3290
  make_meta({ stream: s })
3020
3291
  );
3021
3292
  expect8(await store.reset([s, s])).toBe(1);
@@ -3026,13 +3297,11 @@ var runStoreTck = (options) => {
3026
3297
  const s = `unblock-${uid()}`;
3027
3298
  await store.subscribe([{ stream: s }]);
3028
3299
  await store.commit(
3029
- s,
3030
- [inc(1)],
3300
+ [{ stream: s, msgs: [inc(1)] }],
3031
3301
  make_meta({ stream: s })
3032
3302
  );
3033
3303
  await store.commit(
3034
- s,
3035
- [inc(2)],
3304
+ [{ stream: s, msgs: [inc(2)] }],
3036
3305
  make_meta({ stream: s })
3037
3306
  );
3038
3307
  await correlate();
@@ -3065,8 +3334,7 @@ var runStoreTck = (options) => {
3065
3334
  const s = `unblock-noop-${uid()}`;
3066
3335
  await store.subscribe([{ stream: s }]);
3067
3336
  await store.commit(
3068
- s,
3069
- [inc(1)],
3337
+ [{ stream: s, msgs: [inc(1)] }],
3070
3338
  make_meta({ stream: s })
3071
3339
  );
3072
3340
  expect8(await store.unblock([s])).toBe(0);
@@ -3080,13 +3348,11 @@ var runStoreTck = (options) => {
3080
3348
  const s2 = `unblock-mix-b-${uid()}`;
3081
3349
  await store.subscribe([{ stream: s1 }, { stream: s2 }]);
3082
3350
  await store.commit(
3083
- s1,
3084
- [inc(1)],
3351
+ [{ stream: s1, msgs: [inc(1)] }],
3085
3352
  make_meta({ stream: s1 })
3086
3353
  );
3087
3354
  await store.commit(
3088
- s2,
3089
- [inc(1)],
3355
+ [{ stream: s2, msgs: [inc(1)] }],
3090
3356
  make_meta({ stream: s2 })
3091
3357
  );
3092
3358
  await correlate();
@@ -3104,18 +3370,15 @@ var runStoreTck = (options) => {
3104
3370
  const s3 = `other-${tag}`;
3105
3371
  await store.subscribe([{ stream: s1 }, { stream: s2 }, { stream: s3 }]);
3106
3372
  await store.commit(
3107
- s1,
3108
- [inc(1)],
3373
+ [{ stream: s1, msgs: [inc(1)] }],
3109
3374
  make_meta({ stream: s1 })
3110
3375
  );
3111
3376
  await store.commit(
3112
- s2,
3113
- [inc(1)],
3377
+ [{ stream: s2, msgs: [inc(1)] }],
3114
3378
  make_meta({ stream: s2 })
3115
3379
  );
3116
3380
  await store.commit(
3117
- s3,
3118
- [inc(1)],
3381
+ [{ stream: s3, msgs: [inc(1)] }],
3119
3382
  make_meta({ stream: s3 })
3120
3383
  );
3121
3384
  await correlate();
@@ -3143,13 +3406,11 @@ var runStoreTck = (options) => {
3143
3406
  const s2 = `unblock-empty-${tag}-b`;
3144
3407
  await store.subscribe([{ stream: s1 }, { stream: s2 }]);
3145
3408
  await store.commit(
3146
- s1,
3147
- [inc(1)],
3409
+ [{ stream: s1, msgs: [inc(1)] }],
3148
3410
  make_meta({ stream: s1 })
3149
3411
  );
3150
3412
  await store.commit(
3151
- s2,
3152
- [inc(1)],
3413
+ [{ stream: s2, msgs: [inc(1)] }],
3153
3414
  make_meta({ stream: s2 })
3154
3415
  );
3155
3416
  await correlate();
@@ -3169,8 +3430,7 @@ var runStoreTck = (options) => {
3169
3430
  const s = `unblock-blocked-false-${tag}`;
3170
3431
  await store.subscribe([{ stream: s }]);
3171
3432
  await store.commit(
3172
- s,
3173
- [inc(1)],
3433
+ [{ stream: s, msgs: [inc(1)] }],
3174
3434
  make_meta({ stream: s })
3175
3435
  );
3176
3436
  expect8(
@@ -3193,18 +3453,15 @@ var runStoreTck = (options) => {
3193
3453
  { stream: other }
3194
3454
  ]);
3195
3455
  await store.commit(
3196
- s1,
3197
- [inc(1)],
3456
+ [{ stream: s1, msgs: [inc(1)] }],
3198
3457
  make_meta({ stream: s1 })
3199
3458
  );
3200
3459
  await store.commit(
3201
- s2,
3202
- [inc(1)],
3460
+ [{ stream: s2, msgs: [inc(1)] }],
3203
3461
  make_meta({ stream: s2 })
3204
3462
  );
3205
3463
  await store.commit(
3206
- other,
3207
- [inc(1)],
3464
+ [{ stream: other, msgs: [inc(1)] }],
3208
3465
  make_meta({ stream: other })
3209
3466
  );
3210
3467
  await correlate();
@@ -3235,13 +3492,11 @@ var runStoreTck = (options) => {
3235
3492
  const s2 = `reset-blocked-${tag}-fine`;
3236
3493
  await store.subscribe([{ stream: s1 }, { stream: s2 }]);
3237
3494
  await store.commit(
3238
- s1,
3239
- [inc(1)],
3495
+ [{ stream: s1, msgs: [inc(1)] }],
3240
3496
  make_meta({ stream: s1 })
3241
3497
  );
3242
3498
  await store.commit(
3243
- s2,
3244
- [inc(1)],
3499
+ [{ stream: s2, msgs: [inc(1)] }],
3245
3500
  make_meta({ stream: s2 })
3246
3501
  );
3247
3502
  await correlate();
@@ -3320,8 +3575,7 @@ var runStoreTck = (options) => {
3320
3575
  const ok = `pri-${tag}-ok`;
3321
3576
  const bad = `pri-${tag}-bad`;
3322
3577
  await fresh.commit(
3323
- src,
3324
- [inc(1)],
3578
+ [{ stream: src, msgs: [inc(1)] }],
3325
3579
  make_meta({ stream: src })
3326
3580
  );
3327
3581
  await fresh.subscribe([
@@ -3367,8 +3621,7 @@ var runStoreTck = (options) => {
3367
3621
  const tag = uid();
3368
3622
  const src = `pri-src-${tag}`;
3369
3623
  await fresh.commit(
3370
- src,
3371
- [inc(1), inc(2)],
3624
+ [{ stream: src, msgs: [inc(1), inc(2)] }],
3372
3625
  make_meta({ stream: src })
3373
3626
  );
3374
3627
  await fresh.subscribe([
@@ -3466,13 +3719,11 @@ var runStoreTck = (options) => {
3466
3719
  const sub_default = `lane-claim-def-${tag}`;
3467
3720
  const sub_slow = `lane-claim-slow-${tag}`;
3468
3721
  await store.commit(
3469
- src1,
3470
- [inc(1)],
3722
+ [{ stream: src1, msgs: [inc(1)] }],
3471
3723
  make_meta({ stream: src1 })
3472
3724
  );
3473
3725
  await store.commit(
3474
- src2,
3475
- [inc(1)],
3726
+ [{ stream: src2, msgs: [inc(1)] }],
3476
3727
  make_meta({ stream: src2 })
3477
3728
  );
3478
3729
  await store.subscribe([
@@ -3539,8 +3790,7 @@ var runStoreTck = (options) => {
3539
3790
  const a = `lane-reset-a-${tag}`;
3540
3791
  const b = `lane-reset-b-${tag}`;
3541
3792
  await store.commit(
3542
- src,
3543
- [inc(1)],
3793
+ [{ stream: src, msgs: [inc(1)] }],
3544
3794
  make_meta({ stream: src })
3545
3795
  );
3546
3796
  await store.subscribe([
@@ -3569,8 +3819,7 @@ var runStoreTck = (options) => {
3569
3819
  const a = `lane-ub-a-${tag}`;
3570
3820
  const b = `lane-ub-b-${tag}`;
3571
3821
  await store.commit(
3572
- src,
3573
- [inc(1)],
3822
+ [{ stream: src, msgs: [inc(1)] }],
3574
3823
  make_meta({ stream: src })
3575
3824
  );
3576
3825
  await store.subscribe([
@@ -3599,8 +3848,7 @@ var runStoreTck = (options) => {
3599
3848
  const tag = uid();
3600
3849
  const long = `${tag}-${"x".repeat(140)}`;
3601
3850
  await store.commit(
3602
- long,
3603
- [inc(1)],
3851
+ [{ stream: long, msgs: [inc(1)] }],
3604
3852
  make_meta({ stream: long })
3605
3853
  );
3606
3854
  await store.subscribe([
@@ -3625,8 +3873,7 @@ var runStoreTck = (options) => {
3625
3873
  for (const s of [restarted, retired]) {
3626
3874
  await store.subscribe([{ stream: s }]);
3627
3875
  await store.commit(
3628
- s,
3629
- [inc(1)],
3876
+ [{ stream: s, msgs: [inc(1)] }],
3630
3877
  make_meta({ stream: s })
3631
3878
  );
3632
3879
  }
@@ -3650,8 +3897,7 @@ var runStoreTck = (options) => {
3650
3897
  const lane = `inert-lane-${uid()}`;
3651
3898
  const s = `trunc-inert-${uid()}`;
3652
3899
  const [e] = await store.commit(
3653
- s,
3654
- [inc(1)],
3900
+ [{ stream: s, msgs: [inc(1)] }],
3655
3901
  make_meta({ stream: s })
3656
3902
  );
3657
3903
  await store.subscribe([{ stream: s, correlated_at: e.id, lane }]);
@@ -3666,8 +3912,7 @@ var runStoreTck = (options) => {
3666
3912
  it7("seeds a tombstone when no snapshot is provided", async () => {
3667
3913
  const s = `trunc-tomb-${uid()}`;
3668
3914
  await store.commit(
3669
- s,
3670
- [inc(1), inc(2)],
3915
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
3671
3916
  make_meta({ stream: s })
3672
3917
  );
3673
3918
  const result = await store.truncate([{ stream: s }]);
@@ -3687,8 +3932,7 @@ var runStoreTck = (options) => {
3687
3932
  it7("seeds a snapshot when one is provided", async () => {
3688
3933
  const s = `trunc-snap-${uid()}`;
3689
3934
  await store.commit(
3690
- s,
3691
- [inc(1)],
3935
+ [{ stream: s, msgs: [inc(1)] }],
3692
3936
  make_meta({ stream: s })
3693
3937
  );
3694
3938
  const result = await store.truncate([
@@ -3711,8 +3955,7 @@ var runStoreTck = (options) => {
3711
3955
  it7("leaves exactly one seed when a stream is listed twice", async () => {
3712
3956
  const s = `trunc-dup-${uid()}`;
3713
3957
  await store.commit(
3714
- s,
3715
- [inc(1), inc(2)],
3958
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
3716
3959
  make_meta({ stream: s })
3717
3960
  );
3718
3961
  const result = await store.truncate([
@@ -3733,13 +3976,11 @@ var runStoreTck = (options) => {
3733
3976
  it7("applies windowed targets before full ones", async () => {
3734
3977
  const s = `trunc-mix-${uid()}`;
3735
3978
  await store.commit(
3736
- s,
3737
- [inc(1)],
3979
+ [{ stream: s, msgs: [inc(1)] }],
3738
3980
  make_meta({ stream: s })
3739
3981
  );
3740
3982
  await store.commit(
3741
- s,
3742
- [inc(2)],
3983
+ [{ stream: s, msgs: [inc(2)] }],
3743
3984
  make_meta({ stream: s })
3744
3985
  );
3745
3986
  await sleep(5);
@@ -3776,28 +4017,23 @@ var runStoreTck = (options) => {
3776
4017
  describe8("windowed (before boundary)", () => {
3777
4018
  async function seed_windowed(s) {
3778
4019
  await store.commit(
3779
- s,
3780
- [inc(1), inc(1)],
4020
+ [{ stream: s, msgs: [inc(1), inc(1)] }],
3781
4021
  make_meta({ stream: s })
3782
4022
  );
3783
4023
  const [snap_a] = await store.commit(
3784
- s,
3785
- [{ name: SNAP_EVENT2, data: { count: 2 } }],
4024
+ [{ stream: s, msgs: [{ name: SNAP_EVENT2, data: { count: 2 } }] }],
3786
4025
  make_meta({ stream: s })
3787
4026
  );
3788
4027
  await store.commit(
3789
- s,
3790
- [inc(1)],
4028
+ [{ stream: s, msgs: [inc(1)] }],
3791
4029
  make_meta({ stream: s })
3792
4030
  );
3793
4031
  const [snap_b] = await store.commit(
3794
- s,
3795
- [{ name: SNAP_EVENT2, data: { count: 3 } }],
4032
+ [{ stream: s, msgs: [{ name: SNAP_EVENT2, data: { count: 3 } }] }],
3796
4033
  make_meta({ stream: s })
3797
4034
  );
3798
4035
  await store.commit(
3799
- s,
3800
- [inc(1)],
4036
+ [{ stream: s, msgs: [inc(1)] }],
3801
4037
  make_meta({ stream: s })
3802
4038
  );
3803
4039
  return { snap_a, snap_b };
@@ -3867,8 +4103,7 @@ var runStoreTck = (options) => {
3867
4103
  it7("no-ops when no snapshot qualifies \u2014 stream absent from the result, events untouched", async () => {
3868
4104
  const s = `trunc-win-noop-${uid()}`;
3869
4105
  await store.commit(
3870
- s,
3871
- [inc(1), inc(1)],
4106
+ [{ stream: s, msgs: [inc(1), inc(1)] }],
3872
4107
  make_meta({ stream: s })
3873
4108
  );
3874
4109
  const none = await store.truncate([
@@ -3876,8 +4111,7 @@ var runStoreTck = (options) => {
3876
4111
  ]);
3877
4112
  expect8(none.has(s)).toBe(false);
3878
4113
  await store.commit(
3879
- s,
3880
- [{ name: SNAP_EVENT2, data: { count: 2 } }],
4114
+ [{ stream: s, msgs: [{ name: SNAP_EVENT2, data: { count: 2 } }] }],
3881
4115
  make_meta({ stream: s })
3882
4116
  );
3883
4117
  const too_early = await store.truncate([
@@ -3915,8 +4149,7 @@ var runStoreTck = (options) => {
3915
4149
  const f = `trunc-win-mix-f-${uid()}`;
3916
4150
  const { snap_b } = await seed_windowed(w);
3917
4151
  await store.commit(
3918
- f,
3919
- [inc(1)],
4152
+ [{ stream: f, msgs: [inc(1)] }],
3920
4153
  make_meta({ stream: f })
3921
4154
  );
3922
4155
  const result = await store.truncate([
@@ -3936,8 +4169,7 @@ var runStoreTck = (options) => {
3936
4169
  { stream: s, before: new Date(Date.now() + 6e4) }
3937
4170
  ]);
3938
4171
  const [next] = await store.commit(
3939
- s,
3940
- [inc(1)],
4172
+ [{ stream: s, msgs: [inc(1)] }],
3941
4173
  make_meta({ stream: s })
3942
4174
  );
3943
4175
  expect8(next.version).toBe(6);
@@ -4075,8 +4307,7 @@ var runStoreTck = (options) => {
4075
4307
  const sibling = `qb-${tag}-other`;
4076
4308
  await store.subscribe([{ stream: s }, { stream: sibling }]);
4077
4309
  await store.commit(
4078
- s,
4079
- [inc(1)],
4310
+ [{ stream: s, msgs: [inc(1)] }],
4080
4311
  make_meta({ stream: s })
4081
4312
  );
4082
4313
  await correlate();
@@ -4107,18 +4338,15 @@ var runStoreTck = (options) => {
4107
4338
  const sB = `qst-${tag}-b`;
4108
4339
  const sUnasked = `qst-${tag}-unasked`;
4109
4340
  await store.commit(
4110
- sA,
4111
- [inc(1), inc(2)],
4341
+ [{ stream: sA, msgs: [inc(1), inc(2)] }],
4112
4342
  make_meta({ stream: sA })
4113
4343
  );
4114
4344
  await store.commit(
4115
- sB,
4116
- [dec(5)],
4345
+ [{ stream: sB, msgs: [dec(5)] }],
4117
4346
  make_meta({ stream: sB })
4118
4347
  );
4119
4348
  await store.commit(
4120
- sUnasked,
4121
- [inc(99)],
4349
+ [{ stream: sUnasked, msgs: [inc(99)] }],
4122
4350
  make_meta({ stream: sUnasked })
4123
4351
  );
4124
4352
  const stats = await store.query_stats([sA, sB]);
@@ -4136,8 +4364,7 @@ var runStoreTck = (options) => {
4136
4364
  it7("limit: 0 returns no rows", async () => {
4137
4365
  const s = `qst-zero-${uid()}`;
4138
4366
  await store.commit(
4139
- s,
4140
- [inc(1)],
4367
+ [{ stream: s, msgs: [inc(1)] }],
4141
4368
  make_meta({ stream: s })
4142
4369
  );
4143
4370
  const filter = { stream: s, stream_exact: true };
@@ -4158,8 +4385,7 @@ var runStoreTck = (options) => {
4158
4385
  ];
4159
4386
  for (const s of names)
4160
4387
  await store.commit(
4161
- s,
4162
- [inc(1)],
4388
+ [{ stream: s, msgs: [inc(1)] }],
4163
4389
  make_meta({ stream: s })
4164
4390
  );
4165
4391
  const visited = [];
@@ -4182,18 +4408,15 @@ var runStoreTck = (options) => {
4182
4408
  const tag = uid();
4183
4409
  const s = `qst-tail-${tag}`;
4184
4410
  await store.commit(
4185
- s,
4186
- [inc(1)],
4411
+ [{ stream: s, msgs: [inc(1)] }],
4187
4412
  make_meta({ stream: s })
4188
4413
  );
4189
4414
  await store.commit(
4190
- s,
4191
- [inc(2)],
4415
+ [{ stream: s, msgs: [inc(2)] }],
4192
4416
  make_meta({ stream: s })
4193
4417
  );
4194
4418
  await store.commit(
4195
- s,
4196
- [inc(3)],
4419
+ [{ stream: s, msgs: [inc(3)] }],
4197
4420
  make_meta({ stream: s })
4198
4421
  );
4199
4422
  const stats = await store.query_stats([s], {
@@ -4209,14 +4432,12 @@ var runStoreTck = (options) => {
4209
4432
  const tag = uid();
4210
4433
  const s = `qst-cn-${tag}`;
4211
4434
  await store.commit(
4212
- s,
4213
- [inc(1)],
4435
+ [{ stream: s, msgs: [inc(1)] }],
4214
4436
  make_meta({ stream: s })
4215
4437
  );
4216
4438
  await store.truncate([{ stream: s, snapshot: { count: 99 } }]);
4217
4439
  await store.commit(
4218
- s,
4219
- [inc(2), inc(3), dec(1)],
4440
+ [{ stream: s, msgs: [inc(2), inc(3), dec(1)] }],
4220
4441
  make_meta({ stream: s })
4221
4442
  );
4222
4443
  const stats = await store.query_stats([s], {
@@ -4235,13 +4456,11 @@ var runStoreTck = (options) => {
4235
4456
  const s = `qst-excl-${tag}`;
4236
4457
  const sAllOut = `qst-allout-${tag}`;
4237
4458
  await store.commit(
4238
- s,
4239
- [inc(1), dec(2), inc(3)],
4459
+ [{ stream: s, msgs: [inc(1), dec(2), inc(3)] }],
4240
4460
  make_meta({ stream: s })
4241
4461
  );
4242
4462
  await store.commit(
4243
- sAllOut,
4244
- [inc(7)],
4463
+ [{ stream: sAllOut, msgs: [inc(7)] }],
4245
4464
  make_meta({ stream: sAllOut })
4246
4465
  );
4247
4466
  const all = await store.query_stats([s]);
@@ -4265,18 +4484,15 @@ var runStoreTck = (options) => {
4265
4484
  const tag = uid();
4266
4485
  const s = `qst-tt-${tag}`;
4267
4486
  const c1 = await store.commit(
4268
- s,
4269
- [inc(1)],
4487
+ [{ stream: s, msgs: [inc(1)] }],
4270
4488
  make_meta({ stream: s })
4271
4489
  );
4272
4490
  const c2 = await store.commit(
4273
- s,
4274
- [inc(2)],
4491
+ [{ stream: s, msgs: [inc(2)] }],
4275
4492
  make_meta({ stream: s })
4276
4493
  );
4277
4494
  await store.commit(
4278
- s,
4279
- [inc(3)],
4495
+ [{ stream: s, msgs: [inc(3)] }],
4280
4496
  make_meta({ stream: s })
4281
4497
  );
4282
4498
  const before = c2[0].id;
@@ -4300,18 +4516,15 @@ var runStoreTck = (options) => {
4300
4516
  const sB = `qsf-${tag}-orders-2`;
4301
4517
  const sOther = `qsf-${tag}-users-1`;
4302
4518
  await store.commit(
4303
- sA,
4304
- [inc(1)],
4519
+ [{ stream: sA, msgs: [inc(1)] }],
4305
4520
  make_meta({ stream: sA })
4306
4521
  );
4307
4522
  await store.commit(
4308
- sB,
4309
- [inc(2)],
4523
+ [{ stream: sB, msgs: [inc(2)] }],
4310
4524
  make_meta({ stream: sB })
4311
4525
  );
4312
4526
  await store.commit(
4313
- sOther,
4314
- [inc(3)],
4527
+ [{ stream: sOther, msgs: [inc(3)] }],
4315
4528
  make_meta({ stream: sOther })
4316
4529
  );
4317
4530
  const orders = await store.query_stats({
@@ -4334,13 +4547,11 @@ var runStoreTck = (options) => {
4334
4547
  const b = `qsc-${tag}-b`;
4335
4548
  await store.subscribe([{ stream: a }, { stream: b }]);
4336
4549
  await store.commit(
4337
- a,
4338
- [inc(1)],
4550
+ [{ stream: a, msgs: [inc(1)] }],
4339
4551
  make_meta({ stream: a })
4340
4552
  );
4341
4553
  await store.commit(
4342
- b,
4343
- [inc(2)],
4554
+ [{ stream: b, msgs: [inc(2)] }],
4344
4555
  make_meta({ stream: b })
4345
4556
  );
4346
4557
  await correlate();
@@ -4365,13 +4576,11 @@ var runStoreTck = (options) => {
4365
4576
  const a = `qse-${tag}-a`;
4366
4577
  const b = `qse-${tag}-b`;
4367
4578
  await store.commit(
4368
- a,
4369
- [inc(1)],
4579
+ [{ stream: a, msgs: [inc(1)] }],
4370
4580
  make_meta({ stream: a })
4371
4581
  );
4372
4582
  await store.commit(
4373
- b,
4374
- [dec(2)],
4583
+ [{ stream: b, msgs: [dec(2)] }],
4375
4584
  make_meta({ stream: b })
4376
4585
  );
4377
4586
  const all = await store.query_stats({});
@@ -4382,8 +4591,7 @@ var runStoreTck = (options) => {
4382
4591
  const tag = uid();
4383
4592
  const s = `qsfl-${tag}`;
4384
4593
  await store.commit(
4385
- s,
4386
- [inc(1), inc(2), dec(3)],
4594
+ [{ stream: s, msgs: [inc(1), inc(2), dec(3)] }],
4387
4595
  make_meta({ stream: s })
4388
4596
  );
4389
4597
  const c = await store.query_stats([s], {
@@ -4414,8 +4622,7 @@ var runStoreTck = (options) => {
4414
4622
  ];
4415
4623
  for (const s of streams) {
4416
4624
  await store.commit(
4417
- s,
4418
- [inc(1)],
4625
+ [{ stream: s, msgs: [inc(1)] }],
4419
4626
  make_meta({ stream: s })
4420
4627
  );
4421
4628
  }
@@ -4445,8 +4652,7 @@ var runStoreTck = (options) => {
4445
4652
  const tag = uid();
4446
4653
  for (const s of [`qsc-${tag}-c`, `qsc-${tag}-a`, `qsc-${tag}-b`])
4447
4654
  await store.commit(
4448
- s,
4449
- [inc(1), inc(2)],
4655
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
4450
4656
  make_meta({ stream: s })
4451
4657
  );
4452
4658
  const pattern = { stream: `qsc-${tag}-.*` };
@@ -4595,8 +4801,7 @@ var runStoreTck = (options) => {
4595
4801
  const s = `head-${uid()}`;
4596
4802
  await store.subscribe([{ stream: s }]);
4597
4803
  await store.commit(
4598
- s,
4599
- [inc(1)],
4804
+ [{ stream: s, msgs: [inc(1)] }],
4600
4805
  make_meta({ stream: s })
4601
4806
  );
4602
4807
  const positions = [];
@@ -4612,7 +4817,7 @@ var runStoreTck = (options) => {
4612
4817
  const actor2 = { id: "reader", name: "Reader" };
4613
4818
  const reader = (shape) => {
4614
4819
  const Happened = z2.object(shape);
4615
- return state({ Reader: z2.object({ n: z2.number() }) }).init(() => ({ n: 0 })).emits({ Happened }).patch({ Happened: (_, s) => ({ n: s.n + 1 }) }).on({ Do: Happened }).emit((p) => ["Happened", p]).build();
4820
+ return state({ Reader: z2.object({ n: z2.number() }) }).init(() => ({ n: 0 })).emits({ Happened }).patch({ Happened: (_, s) => ({ n: s.n + 1 }) }).on({ Do: Happened }).emit("Happened").build();
4616
4821
  };
4617
4822
  const over = async (S) => {
4618
4823
  const fresh = await options.factory();
@@ -4705,10 +4910,16 @@ var runStoreTck = (options) => {
4705
4910
  const t = await over(S);
4706
4911
  try {
4707
4912
  await t.fresh.commit(
4708
- t.stream,
4709
- [{ name: "Happened", data: { at: /* @__PURE__ */ new Date("2020-01-01") } }],
4710
- raw,
4711
- -1
4913
+ [
4914
+ {
4915
+ stream: t.stream,
4916
+ msgs: [
4917
+ { name: "Happened", data: { at: /* @__PURE__ */ new Date("2020-01-01") } }
4918
+ ],
4919
+ expectedVersion: -1
4920
+ }
4921
+ ],
4922
+ raw
4712
4923
  );
4713
4924
  const d = await t.read();
4714
4925
  expect8(d.label).toBeUndefined();
@@ -4722,10 +4933,14 @@ var runStoreTck = (options) => {
4722
4933
  const t = await over(S);
4723
4934
  try {
4724
4935
  await t.fresh.commit(
4725
- t.stream,
4726
- [{ name: "Happened", data: { at: "not a date" } }],
4727
- raw,
4728
- -1
4936
+ [
4937
+ {
4938
+ stream: t.stream,
4939
+ msgs: [{ name: "Happened", data: { at: "not a date" } }],
4940
+ expectedVersion: -1
4941
+ }
4942
+ ],
4943
+ raw
4729
4944
  );
4730
4945
  expect8((await t.read()).at).toBe("not a date");
4731
4946
  } finally {
@@ -4865,6 +5080,19 @@ var runStoreTck = (options) => {
4865
5080
  expect8((await take(k, who)).correlating).toBe(true);
4866
5081
  await release(k, who);
4867
5082
  });
5083
+ it7("advances the checkpoint for a caller refused the lease", async () => {
5084
+ const k = key();
5085
+ const holder = by();
5086
+ expect8((await take(k, holder)).correlating).toBe(true);
5087
+ const refused = await store.subscribe([], 123456, {
5088
+ key: k,
5089
+ by: by(),
5090
+ millis: 1e4
5091
+ });
5092
+ expect8(refused.correlating).toBe(false);
5093
+ expect8((await take(k, holder)).correlated_at).toBe(123456);
5094
+ await release(k, holder);
5095
+ });
4868
5096
  it7("answers undefined when no correlator is supplied", async () => {
4869
5097
  expect8(await store.subscribe([])).not.toHaveProperty("correlating");
4870
5098
  });
@@ -4946,7 +5174,234 @@ var runStoreTck = (options) => {
4946
5174
  });
4947
5175
  }
4948
5176
  );
5177
+ describe8("an action across streams", () => {
5178
+ const SeatTaken = z2.object({ student: z2.string() });
5179
+ const Seats = state({
5180
+ Seats: z2.object({ taken: z2.number() }).refine((s) => s.taken <= 3, "the course is full")
5181
+ }).init(() => ({ taken: 0 })).emits({ SeatTaken }).patch({ SeatTaken: (_, s) => ({ taken: s.taken + 1 }) }).on({ TakeSeat: SeatTaken }).emit("SeatTaken").build();
5182
+ const Enroll = z2.object({ course: z2.string(), student: z2.string() });
5183
+ const course = (data) => `course-${data.course}`;
5184
+ const Enrollment = state({ Enrollment: Enroll }).init(() => ({ course: "", student: "" })).emits({ Enrolled: Enroll }).on({ Enroll }, { maxRetries: 8 }).loads(Seats, course, ["SeatTaken"]).requires([
5185
+ {
5186
+ description: "the course has a free seat",
5187
+ holds: (_, __, ___, { Seats: Seats2 }) => Seats2.taken < 3
5188
+ }
5189
+ ]).emit((data) => [
5190
+ ["Enrolled", data],
5191
+ ["SeatTaken", { student: data.student }]
5192
+ ]).on({ Overfill: Enroll }).loads(Seats, course, ["SeatTaken"]).emit((data) => [
5193
+ ["Enrolled", data],
5194
+ ["SeatTaken", { student: data.student }]
5195
+ ]).build();
5196
+ const actor2 = { id: "a", name: "a" };
5197
+ const at = (stream) => ({ stream, actor: actor2 });
5198
+ const over = async (run) => {
5199
+ const fresh = await options.factory();
5200
+ await fresh.seed();
5201
+ const cache = new InMemoryCache();
5202
+ const app = act().withState(Seats).withState(Enrollment).build({ scoped: { store: fresh, cache } });
5203
+ const c = `c${uid()}`;
5204
+ const events2 = async (stream) => {
5205
+ const found = [];
5206
+ await fresh.query((e) => found.push(e), {
5207
+ stream,
5208
+ stream_exact: true
5209
+ });
5210
+ return found;
5211
+ };
5212
+ try {
5213
+ await run({
5214
+ app,
5215
+ fresh,
5216
+ c,
5217
+ events: events2,
5218
+ seat: (student) => ({ course: c, student })
5219
+ });
5220
+ } finally {
5221
+ vi.restoreAllMocks();
5222
+ await app.shutdown();
5223
+ await cache.dispose();
5224
+ await fresh.dispose();
5225
+ }
5226
+ };
5227
+ it7("commits both streams at once and fires committed once per stream", () => over(async ({ app, c, events: events2, seat }) => {
5228
+ const stream = `enr-${uid()}`;
5229
+ const heard = [];
5230
+ app.on("committed", (s) => heard.push(s));
5231
+ const snapshots = await app.do("Enroll", at(stream), seat("s1"));
5232
+ expect8(snapshots.map((s) => s.event?.name)).toEqual([
5233
+ "Enrolled"
5234
+ ]);
5235
+ const [own] = await events2(stream);
5236
+ const [other] = await events2(`course-${c}`);
5237
+ expect8(own.name).toBe("Enrolled");
5238
+ expect8(other.name).toBe("SeatTaken");
5239
+ expect8(other.id).toBeGreaterThan(own.id);
5240
+ expect8(other.meta.correlation).toBe(own.meta.correlation);
5241
+ expect8(heard.map((h) => h[0].event?.stream)).toEqual([
5242
+ stream,
5243
+ `course-${c}`
5244
+ ]);
5245
+ expect8((await app.load(Seats, `course-${c}`)).state.taken).toBe(1);
5246
+ }));
5247
+ it7("stores nothing on either stream when a rule or a schema refuses", () => over(async ({ app, c, events: events2, seat }) => {
5248
+ for (const i of [1, 2, 3])
5249
+ await app.do("TakeSeat", at(`course-${c}`), { student: `s${i}` });
5250
+ const [a, b] = [`enr-${uid()}`, `enr-${uid()}`];
5251
+ await expect8(app.do("Enroll", at(a), seat("late"))).rejects.toThrow(
5252
+ PreconditionError
5253
+ );
5254
+ await expect8(app.do("Overfill", at(b), seat("late"))).rejects.toThrow(
5255
+ ValidationError
5256
+ );
5257
+ expect8(await events2(a)).toEqual([]);
5258
+ expect8(await events2(b)).toEqual([]);
5259
+ expect8(await events2(`course-${c}`)).toHaveLength(3);
5260
+ }));
5261
+ it7("a rival on the loaded stream refuses the whole commit, naming that stream", () => over(async ({ app, fresh, c, events: events2, seat }) => {
5262
+ const commit = fresh.commit.bind(fresh);
5263
+ vi.spyOn(fresh, "commit").mockImplementationOnce(
5264
+ async (...args) => {
5265
+ await commit(
5266
+ [
5267
+ {
5268
+ stream: `course-${c}`,
5269
+ msgs: [{ name: "SeatTaken", data: { student: "rival" } }]
5270
+ }
5271
+ ],
5272
+ { correlation: "rival", causation: {} }
5273
+ );
5274
+ return commit(...args);
5275
+ }
5276
+ );
5277
+ const stream = `enr-${uid()}`;
5278
+ const err = await app.do("Overfill", at(stream), seat("s")).catch((e) => e);
5279
+ expect8(err).toBeInstanceOf(ConcurrencyError);
5280
+ expect8(err.stream).toBe(`course-${c}`);
5281
+ expect8(await events2(stream)).toEqual([]);
5282
+ expect8(await events2(`course-${c}`)).toHaveLength(1);
5283
+ }));
5284
+ it7("racing enrollments fill the seats exactly: the rest are refused by the rule", () => over(async ({ app, c, events: events2, seat }) => {
5285
+ const results = await Promise.allSettled(
5286
+ [1, 2, 3, 4, 5, 6].map(
5287
+ (i) => app.do("Enroll", at(`race-${uid()}-${i}`), seat(`s${i}`))
5288
+ )
5289
+ );
5290
+ expect8(results.filter((r) => r.status === "fulfilled")).toHaveLength(
5291
+ 3
5292
+ );
5293
+ for (const r of results.filter((r2) => r2.status === "rejected"))
5294
+ expect8(r.reason).toBeInstanceOf(
5295
+ PreconditionError
5296
+ );
5297
+ expect8(await events2(`course-${c}`)).toHaveLength(3);
5298
+ }));
5299
+ });
4949
5300
  describe8.skipIf(!caps.restore)("restore (capability)", () => {
5301
+ describe8("what a restore must not get wrong", () => {
5302
+ const edge_uid = () => uid();
5303
+ const E = (data = { a: 1 }, pii) => ({
5304
+ name: "E",
5305
+ data,
5306
+ ...pii ? { pii } : {}
5307
+ });
5308
+ const edge_meta = (correlation = "m") => ({ correlation, causation: {} });
5309
+ const rows = async (query) => {
5310
+ const found = [];
5311
+ await store.query((e) => found.push(e), query);
5312
+ return found;
5313
+ };
5314
+ const one = (stream) => ({ stream, stream_exact: true });
5315
+ it7("a commit after restore continues each stream from its restored head", async () => {
5316
+ const a = `a-${edge_uid()}`;
5317
+ await store.commit(
5318
+ [{ stream: a, msgs: [E(), E()] }],
5319
+ edge_meta()
5320
+ );
5321
+ const log = await rows({ with_snaps: true });
5322
+ await store.restore(async (cb) => {
5323
+ for (const e of log) await cb(e);
5324
+ });
5325
+ const [c] = await store.commit(
5326
+ [{ stream: a, msgs: [E()], expectedVersion: 1 }],
5327
+ edge_meta()
5328
+ );
5329
+ expect8(c.version).toBe(2);
5330
+ });
5331
+ it7("restore renumbers: ids are dense whatever the source ids were", async () => {
5332
+ const a = `a-${edge_uid()}`;
5333
+ const ev = (version, id) => ({
5334
+ id,
5335
+ stream: a,
5336
+ version,
5337
+ name: "E",
5338
+ data: {},
5339
+ meta: edge_meta(),
5340
+ created: /* @__PURE__ */ new Date()
5341
+ });
5342
+ const ids = [];
5343
+ await store.restore(async (cb) => {
5344
+ ids.push(await cb(ev(0, 500)));
5345
+ ids.push(await cb(ev(1, 900)));
5346
+ });
5347
+ expect8(ids[1]).toBe(ids[0] + 1);
5348
+ expect8(ids[0]).toBeLessThanOrEqual(1);
5349
+ });
5350
+ it7("changes nothing when it fails", async () => {
5351
+ const a = `rb-${edge_uid()}`;
5352
+ const key = `k-${edge_uid()}`;
5353
+ const sub = `sub-${edge_uid()}`;
5354
+ const committed = await store.commit(
5355
+ [{ stream: a, msgs: [E({}, { email: "x" }), E()] }],
5356
+ edge_meta()
5357
+ );
5358
+ const head = committed.at(-1).id;
5359
+ await store.subscribe([{ stream: sub }], head);
5360
+ const keyed = await store.subscribe([], head, {
5361
+ key,
5362
+ by: "w",
5363
+ millis: 1
5364
+ });
5365
+ const shared = await store.subscribe([]);
5366
+ await expect8(
5367
+ store.restore(async (callback) => {
5368
+ await callback({
5369
+ id: 1,
5370
+ stream: "other",
5371
+ version: 0,
5372
+ name: "E",
5373
+ data: {},
5374
+ meta: edge_meta(),
5375
+ created: /* @__PURE__ */ new Date()
5376
+ });
5377
+ throw new Error("mid-restore");
5378
+ })
5379
+ ).rejects.toThrow();
5380
+ const subscriptions = [];
5381
+ await store.query_streams((p) => subscriptions.push(p.stream), {
5382
+ stream: sub,
5383
+ stream_exact: true
5384
+ });
5385
+ expect8(subscriptions).toEqual([sub]);
5386
+ expect8((await store.subscribe([])).correlated_at).toBe(
5387
+ shared.correlated_at
5388
+ );
5389
+ await sleep(5);
5390
+ const keyed_after = await store.subscribe([], void 0, {
5391
+ key,
5392
+ by: "w2",
5393
+ millis: 1
5394
+ });
5395
+ expect8(keyed_after.correlated_at).toBe(keyed.correlated_at);
5396
+ expect8((await rows(one(a)))[0].pii).toEqual({ email: "x" });
5397
+ const [next] = await store.commit(
5398
+ [{ stream: a, msgs: [E()], expectedVersion: 1 }],
5399
+ edge_meta()
5400
+ );
5401
+ expect8(next.version).toBe(2);
5402
+ expect8(next.id).toBeGreaterThan(head);
5403
+ });
5404
+ });
4950
5405
  beforeEach(async () => {
4951
5406
  await store.drop();
4952
5407
  await store.seed();
@@ -5132,8 +5587,7 @@ var runStoreTck = (options) => {
5132
5587
  it7("wipes pre-existing events before inserting", async () => {
5133
5588
  const old = `restore-old-${uid()}`;
5134
5589
  await store.commit(
5135
- old,
5136
- [inc(1), inc(2)],
5590
+ [{ stream: old, msgs: [inc(1), inc(2)] }],
5137
5591
  make_meta({ stream: old })
5138
5592
  );
5139
5593
  const fresh = `restore-fresh-${uid()}`;
@@ -5280,8 +5734,7 @@ var runStoreTck = (options) => {
5280
5734
  it7("rolls back atomically when the source throws mid-iteration", async () => {
5281
5735
  const original = `restore-pre-${uid()}`;
5282
5736
  const committed = await store.commit(
5283
- original,
5284
- [inc(1), inc(2), inc(3)],
5737
+ [{ stream: original, msgs: [inc(1), inc(2), inc(3)] }],
5285
5738
  make_meta({ stream: original })
5286
5739
  );
5287
5740
  const explosive = {
@@ -5454,12 +5907,16 @@ var runStoreTck = (options) => {
5454
5907
  it7("commits and loads pii alongside data", async () => {
5455
5908
  const s = `pii-roundtrip-${uid()}`;
5456
5909
  const committed = await store.commit(
5457
- s,
5458
5910
  [
5459
5911
  {
5460
- name: "Incremented",
5461
- data: { amount: 1 },
5462
- pii: { email: "u@example.com", name: "Ursula" }
5912
+ stream: s,
5913
+ msgs: [
5914
+ {
5915
+ name: "Incremented",
5916
+ data: { amount: 1 },
5917
+ pii: { email: "u@example.com", name: "Ursula" }
5918
+ }
5919
+ ]
5463
5920
  }
5464
5921
  ],
5465
5922
  make_meta({ stream: s })
@@ -5482,12 +5939,16 @@ var runStoreTck = (options) => {
5482
5939
  });
5483
5940
  describe8("with_pii", () => {
5484
5941
  const seed_pii = async (s) => await store.commit(
5485
- s,
5486
5942
  [
5487
5943
  {
5488
- name: "Incremented",
5489
- data: { amount: 1 },
5490
- pii: { email: "w@example.com" }
5944
+ stream: s,
5945
+ msgs: [
5946
+ {
5947
+ name: "Incremented",
5948
+ data: { amount: 1 },
5949
+ pii: { email: "w@example.com" }
5950
+ }
5951
+ ]
5491
5952
  }
5492
5953
  ],
5493
5954
  make_meta({ stream: s })
@@ -5548,12 +6009,16 @@ var runStoreTck = (options) => {
5548
6009
  const s = `pii-date-${uid()}`;
5549
6010
  const when = /* @__PURE__ */ new Date("2024-03-01T10:20:30.000Z");
5550
6011
  await store.commit(
5551
- s,
5552
6012
  [
5553
6013
  {
5554
- name: "Incremented",
5555
- data: { amount: 1, at: when },
5556
- pii: { born: when }
6014
+ stream: s,
6015
+ msgs: [
6016
+ {
6017
+ name: "Incremented",
6018
+ data: { amount: 1, at: when },
6019
+ pii: { born: when }
6020
+ }
6021
+ ]
5557
6022
  }
5558
6023
  ],
5559
6024
  make_meta({ stream: s })
@@ -5576,17 +6041,21 @@ var runStoreTck = (options) => {
5576
6041
  it7("query_stats head/tail never carry pii \u2014 introspection surface is pii-safe", async () => {
5577
6042
  const s = `pii-stats-${uid()}`;
5578
6043
  await store.commit(
5579
- s,
5580
6044
  [
5581
6045
  {
5582
- name: "Incremented",
5583
- data: { amount: 1 },
5584
- pii: { email: "head@example.com" }
5585
- },
5586
- {
5587
- name: "Incremented",
5588
- data: { amount: 2 },
5589
- pii: { email: "tail@example.com" }
6046
+ stream: s,
6047
+ msgs: [
6048
+ {
6049
+ name: "Incremented",
6050
+ data: { amount: 1 },
6051
+ pii: { email: "head@example.com" }
6052
+ },
6053
+ {
6054
+ name: "Incremented",
6055
+ data: { amount: 2 },
6056
+ pii: { email: "tail@example.com" }
6057
+ }
6058
+ ]
5590
6059
  }
5591
6060
  ],
5592
6061
  make_meta({ stream: s })
@@ -5610,8 +6079,7 @@ var runStoreTck = (options) => {
5610
6079
  it7("passes through events without pii (pii is null or undefined on load)", async () => {
5611
6080
  const s = `pii-none-${uid()}`;
5612
6081
  await store.commit(
5613
- s,
5614
- [inc(1)],
6082
+ [{ stream: s, msgs: [inc(1)] }],
5615
6083
  make_meta({ stream: s })
5616
6084
  );
5617
6085
  const seen = [];
@@ -5627,17 +6095,21 @@ var runStoreTck = (options) => {
5627
6095
  it7("wipes pii for every event on the stream via forget_pii", async () => {
5628
6096
  const s = `pii-forget-${uid()}`;
5629
6097
  await store.commit(
5630
- s,
5631
6098
  [
5632
6099
  {
5633
- name: "Incremented",
5634
- data: { amount: 1 },
5635
- pii: { email: "a@example.com" }
5636
- },
5637
- {
5638
- name: "Incremented",
5639
- data: { amount: 2 },
5640
- pii: { email: "b@example.com" }
6100
+ stream: s,
6101
+ msgs: [
6102
+ {
6103
+ name: "Incremented",
6104
+ data: { amount: 1 },
6105
+ pii: { email: "a@example.com" }
6106
+ },
6107
+ {
6108
+ name: "Incremented",
6109
+ data: { amount: 2 },
6110
+ pii: { email: "b@example.com" }
6111
+ }
6112
+ ]
5641
6113
  }
5642
6114
  ],
5643
6115
  make_meta({ stream: s })
@@ -5662,12 +6134,16 @@ var runStoreTck = (options) => {
5662
6134
  it7("is idempotent \u2014 second forget_pii returns 0, no error", async () => {
5663
6135
  const s = `pii-forget-idem-${uid()}`;
5664
6136
  await store.commit(
5665
- s,
5666
6137
  [
5667
6138
  {
5668
- name: "Incremented",
5669
- data: { amount: 1 },
5670
- pii: { email: "u@example.com" }
6139
+ stream: s,
6140
+ msgs: [
6141
+ {
6142
+ name: "Incremented",
6143
+ data: { amount: 1 },
6144
+ pii: { email: "u@example.com" }
6145
+ }
6146
+ ]
5671
6147
  }
5672
6148
  ],
5673
6149
  make_meta({ stream: s })
@@ -5682,23 +6158,31 @@ var runStoreTck = (options) => {
5682
6158
  const sA = `pii-iso-a-${uid()}`;
5683
6159
  const sB = `pii-iso-b-${uid()}`;
5684
6160
  await store.commit(
5685
- sA,
5686
6161
  [
5687
6162
  {
5688
- name: "Incremented",
5689
- data: { amount: 1 },
5690
- pii: { email: "alice@example.com" }
6163
+ stream: sA,
6164
+ msgs: [
6165
+ {
6166
+ name: "Incremented",
6167
+ data: { amount: 1 },
6168
+ pii: { email: "alice@example.com" }
6169
+ }
6170
+ ]
5691
6171
  }
5692
6172
  ],
5693
6173
  make_meta({ stream: sA })
5694
6174
  );
5695
6175
  await store.commit(
5696
- sB,
5697
6176
  [
5698
6177
  {
5699
- name: "Incremented",
5700
- data: { amount: 1 },
5701
- pii: { email: "bob@example.com" }
6178
+ stream: sB,
6179
+ msgs: [
6180
+ {
6181
+ name: "Incremented",
6182
+ data: { amount: 1 },
6183
+ pii: { email: "bob@example.com" }
6184
+ }
6185
+ ]
5702
6186
  }
5703
6187
  ],
5704
6188
  make_meta({ stream: sB })
@@ -5724,8 +6208,7 @@ var runStoreTck = (options) => {
5724
6208
  it7("forget_pii on a stream with no pii events returns 0", async () => {
5725
6209
  const s = `pii-forget-empty-${uid()}`;
5726
6210
  await store.commit(
5727
- s,
5728
- [inc(1)],
6211
+ [{ stream: s, msgs: [inc(1)] }],
5729
6212
  make_meta({ stream: s })
5730
6213
  );
5731
6214
  const wiped = await store.forget_pii.call(store, s);
@@ -5750,8 +6233,7 @@ var runStoreTck = (options) => {
5750
6233
  try {
5751
6234
  const stream = `notify-${uid()}`;
5752
6235
  await writer.commit(
5753
- stream,
5754
- [inc(1)],
6236
+ [{ stream, msgs: [inc(1)] }],
5755
6237
  make_meta({ stream })
5756
6238
  );
5757
6239
  await arrived;
@@ -5779,18 +6261,15 @@ var runStoreTck = (options) => {
5779
6261
  const writer = await options.factory();
5780
6262
  try {
5781
6263
  await store.commit(
5782
- own,
5783
- [inc(1)],
6264
+ [{ stream: own, msgs: [inc(1)] }],
5784
6265
  make_meta({ stream: own })
5785
6266
  );
5786
6267
  await writer.commit(
5787
- remote,
5788
- [inc(1)],
6268
+ [{ stream: remote, msgs: [inc(1)] }],
5789
6269
  make_meta({ stream: remote })
5790
6270
  );
5791
6271
  await writer.commit(
5792
- sentinel,
5793
- [inc(1)],
6272
+ [{ stream: sentinel, msgs: [inc(1)] }],
5794
6273
  make_meta({ stream: sentinel })
5795
6274
  );
5796
6275
  await sentinel_arrived;
@@ -5802,7 +6281,7 @@ var runStoreTck = (options) => {
5802
6281
  await Promise.resolve(disposer());
5803
6282
  }
5804
6283
  });
5805
- it7("delivers one notification per commit transaction carrying the full event batch", async () => {
6284
+ it7("delivers one notification carrying a stream's full batch from one commit", async () => {
5806
6285
  const received = [];
5807
6286
  let resolve_sentinel;
5808
6287
  const sentinel_arrived = new Promise((res) => {
@@ -5817,13 +6296,11 @@ var runStoreTck = (options) => {
5817
6296
  const writer = await options.factory();
5818
6297
  try {
5819
6298
  await writer.commit(
5820
- batch,
5821
- [inc(1), inc(2), inc(3)],
6299
+ [{ stream: batch, msgs: [inc(1), inc(2), inc(3)] }],
5822
6300
  make_meta({ stream: batch })
5823
6301
  );
5824
6302
  await writer.commit(
5825
- sentinel,
5826
- [inc(1)],
6303
+ [{ stream: sentinel, msgs: [inc(1)] }],
5827
6304
  make_meta({ stream: sentinel })
5828
6305
  );
5829
6306
  await sentinel_arrived;
@@ -5846,6 +6323,45 @@ var runStoreTck = (options) => {
5846
6323
  await Promise.resolve(disposer());
5847
6324
  }
5848
6325
  });
6326
+ it7("delivers one notification per stream a commit appends to, with that stream's events", async () => {
6327
+ const received = [];
6328
+ let resolve_sentinel;
6329
+ const sentinel_arrived = new Promise((res) => {
6330
+ resolve_sentinel = res;
6331
+ });
6332
+ const a = `notify-across-a-${uid()}`;
6333
+ const b = `notify-across-b-${uid()}`;
6334
+ const sentinel = `notify-across-sentinel-${uid()}`;
6335
+ const disposer = await store.notify.call(store, (n) => {
6336
+ received.push(n);
6337
+ if (n.stream === sentinel) resolve_sentinel();
6338
+ });
6339
+ const writer = await options.factory();
6340
+ try {
6341
+ const committed = await writer.commit(
6342
+ [
6343
+ { stream: a, msgs: [inc(1), inc(2)] },
6344
+ { stream: b, msgs: [dec(3)] }
6345
+ ],
6346
+ make_meta({ stream: a })
6347
+ );
6348
+ await writer.commit(
6349
+ [{ stream: sentinel, msgs: [inc(1)] }],
6350
+ make_meta({ stream: sentinel })
6351
+ );
6352
+ await sentinel_arrived;
6353
+ for (const stream of [a, b]) {
6354
+ const of_stream = received.filter((n) => n.stream === stream);
6355
+ expect8(of_stream).toHaveLength(1);
6356
+ expect8(of_stream[0].events.map((e) => e.id)).toEqual(
6357
+ committed.filter((e) => e.stream === stream).map((e) => e.id)
6358
+ );
6359
+ }
6360
+ } finally {
6361
+ await writer.dispose();
6362
+ await Promise.resolve(disposer());
6363
+ }
6364
+ });
5849
6365
  });
5850
6366
  }
5851
6367
  });