@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.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,6 +1095,8 @@ import {
1098
1095
  act,
1099
1096
  ConcurrencyError,
1100
1097
  InMemoryCache,
1098
+ is_literal_source,
1099
+ PreconditionError,
1101
1100
  SNAP_EVENT as SNAP_EVENT2,
1102
1101
  sensitive,
1103
1102
  sleep,
@@ -1111,7 +1110,6 @@ var runStoreTck = (options) => {
1111
1110
  describe8(`TCK / Store / ${options.name}`, () => {
1112
1111
  let store;
1113
1112
  const caps = { ...options.capabilities };
1114
- const is_literal = (source) => !/[\]^$.*+?()[{}|\\]/.test(source);
1115
1113
  const correlate = async (target = store) => {
1116
1114
  const rows = [];
1117
1115
  await target.query_streams(
@@ -1131,7 +1129,10 @@ var runStoreTck = (options) => {
1131
1129
  (e) => {
1132
1130
  mark = Math.max(mark, e.id);
1133
1131
  },
1134
- row.source !== void 0 ? { stream: row.source, stream_exact: is_literal(row.source) } : {}
1132
+ row.source !== void 0 ? {
1133
+ stream: row.source,
1134
+ stream_exact: is_literal_source(row.source, row.stream)
1135
+ } : {}
1135
1136
  );
1136
1137
  if (mark > row.at)
1137
1138
  marks.push({
@@ -1167,14 +1168,12 @@ var runStoreTck = (options) => {
1167
1168
  await zero_config.seed();
1168
1169
  const s = `default-config-${uid()}`;
1169
1170
  const first = await zero_config.commit(
1170
- s,
1171
- [inc(1)],
1171
+ [{ stream: s, msgs: [inc(1)] }],
1172
1172
  make_meta({ stream: s })
1173
1173
  );
1174
1174
  expect8(first[0].version).toBe(0);
1175
1175
  const second = await zero_config.commit(
1176
- s,
1177
- [inc(2)],
1176
+ [{ stream: s, msgs: [inc(2)] }],
1178
1177
  make_meta({ stream: s })
1179
1178
  );
1180
1179
  expect8(second[0].version).toBe(1);
@@ -1190,12 +1189,371 @@ var runStoreTck = (options) => {
1190
1189
  });
1191
1190
  });
1192
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
+ });
1193
1552
  describe8("commit", () => {
1194
1553
  it7("returns committed events with sequenced ids and versions", async () => {
1195
1554
  const s = `commit-seq-${uid()}`;
1196
1555
  const committed = await store.commit(
1197
- s,
1198
- [inc(1), inc(2), dec(3)],
1556
+ [{ stream: s, msgs: [inc(1), inc(2), dec(3)] }],
1199
1557
  make_meta({ stream: s })
1200
1558
  );
1201
1559
  expect8(committed).toHaveLength(3);
@@ -1212,8 +1570,7 @@ var runStoreTck = (options) => {
1212
1570
  const s = `commit-meta-${uid()}`;
1213
1571
  const correlation = `cor-${uid()}`;
1214
1572
  const committed = await store.commit(
1215
- s,
1216
- [inc(1)],
1573
+ [{ stream: s, msgs: [inc(1)] }],
1217
1574
  make_meta({ stream: s, correlation })
1218
1575
  );
1219
1576
  expect8(committed[0].stream).toBe(s);
@@ -1222,64 +1579,50 @@ var runStoreTck = (options) => {
1222
1579
  it7("throws ConcurrencyError when expectedVersion is wrong", async () => {
1223
1580
  const s = `commit-cc-${uid()}`;
1224
1581
  await store.commit(
1225
- s,
1226
- [inc(1)],
1582
+ [{ stream: s, msgs: [inc(1)] }],
1227
1583
  make_meta({ stream: s })
1228
1584
  );
1229
1585
  await store.commit(
1230
- s,
1231
- [inc(1)],
1232
- make_meta({ stream: s }),
1233
- 0
1586
+ [{ stream: s, msgs: [inc(1)], expectedVersion: 0 }],
1587
+ make_meta({ stream: s })
1234
1588
  );
1235
1589
  await expect8(
1236
- 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
+ )
1237
1594
  ).rejects.toBeInstanceOf(ConcurrencyError);
1238
1595
  });
1239
1596
  it7("treats expectedVersion -1 as an empty stream", async () => {
1240
1597
  const s = `commit-new-cc-${uid()}`;
1241
1598
  const [first] = await store.commit(
1242
- s,
1243
- [inc(1)],
1244
- make_meta({ stream: s }),
1245
- -1
1246
- );
1247
- expect8(first.version).toBe(0);
1248
- await expect8(
1249
- store.commit(s, [inc(1)], make_meta({ stream: s }), -1)
1250
- ).rejects.toBeInstanceOf(ConcurrencyError);
1251
- });
1252
- it7("checks expectedVersion even when committing no events", async () => {
1253
- const s = `commit-empty-cc-${uid()}`;
1254
- await store.commit(
1255
- s,
1256
- [inc(1)],
1599
+ [{ stream: s, msgs: [inc(1)], expectedVersion: -1 }],
1257
1600
  make_meta({ stream: s })
1258
1601
  );
1602
+ expect8(first.version).toBe(0);
1259
1603
  await expect8(
1260
- 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
+ )
1261
1608
  ).rejects.toBeInstanceOf(ConcurrencyError);
1262
- expect8(
1263
- await store.commit(s, [], make_meta({ stream: s }), 0)
1264
- ).toEqual([]);
1265
1609
  });
1266
1610
  it7("takes concurrent commits, reads and writes on one instance", async () => {
1267
1611
  const x = `commit-concurrent-x-${uid()}`;
1268
1612
  const y = `commit-concurrent-y-${uid()}`;
1269
1613
  const [first, second, other, read, subscribed] = await Promise.allSettled([
1270
1614
  store.commit(
1271
- x,
1272
- [inc(1)],
1273
- make_meta({ stream: x }),
1274
- -1
1615
+ [{ stream: x, msgs: [inc(1)], expectedVersion: -1 }],
1616
+ make_meta({ stream: x })
1275
1617
  ),
1276
1618
  store.commit(
1277
- x,
1278
- [inc(2)],
1279
- make_meta({ stream: x }),
1280
- -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 })
1281
1625
  ),
1282
- store.commit(y, [inc(1)], make_meta({ stream: y })),
1283
1626
  store.query(() => {
1284
1627
  }, { stream: x, stream_exact: true }),
1285
1628
  // A source nothing writes, so later claims never see it.
@@ -1301,12 +1644,14 @@ var runStoreTck = (options) => {
1301
1644
  it7("preserves prior events when a concurrent commit is rejected", async () => {
1302
1645
  const s = `commit-cc-preserve-${uid()}`;
1303
1646
  await store.commit(
1304
- s,
1305
- [inc(1), inc(2)],
1647
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
1306
1648
  make_meta({ stream: s })
1307
1649
  );
1308
1650
  await expect8(
1309
- 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
+ )
1310
1655
  ).rejects.toBeInstanceOf(ConcurrencyError);
1311
1656
  const found = await collect(store, { stream: s, stream_exact: true });
1312
1657
  expect8(found).toHaveLength(2);
@@ -1318,13 +1663,11 @@ var runStoreTck = (options) => {
1318
1663
  const s2 = `q-s2-${uid()}`;
1319
1664
  const cor = `q-cor-${uid()}`;
1320
1665
  await store.commit(
1321
- s1,
1322
- [inc(1), dec(1)],
1666
+ [{ stream: s1, msgs: [inc(1), dec(1)] }],
1323
1667
  make_meta({ stream: s1, correlation: cor })
1324
1668
  );
1325
1669
  await store.commit(
1326
- s2,
1327
- [inc(2), dec(2), reset()],
1670
+ [{ stream: s2, msgs: [inc(2), dec(2), reset()] }],
1328
1671
  make_meta({ stream: s2, correlation: cor })
1329
1672
  );
1330
1673
  const by_stream = await collect(store, {
@@ -1350,8 +1693,7 @@ var runStoreTck = (options) => {
1350
1693
  it7("excludes snapshots when called with no query object", async () => {
1351
1694
  const s = `q-nofilter-${uid()}`;
1352
1695
  await store.commit(
1353
- s,
1354
- [{ name: SNAP_EVENT2, data: { count: 0 } }],
1696
+ [{ stream: s, msgs: [{ name: SNAP_EVENT2, data: { count: 0 } }] }],
1355
1697
  make_meta({ stream: s })
1356
1698
  );
1357
1699
  const names = [];
@@ -1363,18 +1705,15 @@ var runStoreTck = (options) => {
1363
1705
  it7("with_snaps resumes from the latest snapshot per stream", async () => {
1364
1706
  const s = `q-snap-${uid()}`;
1365
1707
  await store.commit(
1366
- s,
1367
- [inc(1), inc(1)],
1708
+ [{ stream: s, msgs: [inc(1), inc(1)] }],
1368
1709
  make_meta({ stream: s })
1369
1710
  );
1370
1711
  const [snap] = await store.commit(
1371
- s,
1372
- [{ name: SNAP_EVENT2, data: { count: 2 } }],
1712
+ [{ stream: s, msgs: [{ name: SNAP_EVENT2, data: { count: 2 } }] }],
1373
1713
  make_meta({ stream: s })
1374
1714
  );
1375
1715
  await store.commit(
1376
- s,
1377
- [inc(1), inc(1), inc(1)],
1716
+ [{ stream: s, msgs: [inc(1), inc(1), inc(1)] }],
1378
1717
  make_meta({ stream: s })
1379
1718
  );
1380
1719
  const from_snap = await collect(store, {
@@ -1395,8 +1734,7 @@ var runStoreTck = (options) => {
1395
1734
  expect8(after_snap).toHaveLength(3);
1396
1735
  const s2 = `q-nosnap-${uid()}`;
1397
1736
  await store.commit(
1398
- s2,
1399
- [inc(1), inc(1)],
1737
+ [{ stream: s2, msgs: [inc(1), inc(1)] }],
1400
1738
  make_meta({ stream: s2 })
1401
1739
  );
1402
1740
  const full = await collect(store, {
@@ -1409,18 +1747,15 @@ var runStoreTck = (options) => {
1409
1747
  it7("with_snaps applies the resume floor on a backward scan too", async () => {
1410
1748
  const s = `q-snap-back-${uid()}`;
1411
1749
  await store.commit(
1412
- s,
1413
- [inc(1), inc(1)],
1750
+ [{ stream: s, msgs: [inc(1), inc(1)] }],
1414
1751
  make_meta({ stream: s })
1415
1752
  );
1416
1753
  const [snap] = await store.commit(
1417
- s,
1418
- [{ name: SNAP_EVENT2, data: { count: 2 } }],
1754
+ [{ stream: s, msgs: [{ name: SNAP_EVENT2, data: { count: 2 } }] }],
1419
1755
  make_meta({ stream: s })
1420
1756
  );
1421
1757
  await store.commit(
1422
- s,
1423
- [inc(1), inc(1), inc(1)],
1758
+ [{ stream: s, msgs: [inc(1), inc(1), inc(1)] }],
1424
1759
  make_meta({ stream: s })
1425
1760
  );
1426
1761
  const backward = await collect(store, {
@@ -1434,8 +1769,7 @@ var runStoreTck = (options) => {
1434
1769
  expect8(backward.every((e) => e.id >= snap.id)).toBe(true);
1435
1770
  const s2 = `q-nosnap-back-${uid()}`;
1436
1771
  await store.commit(
1437
- s2,
1438
- [inc(1), inc(1)],
1772
+ [{ stream: s2, msgs: [inc(1), inc(1)] }],
1439
1773
  make_meta({ stream: s2 })
1440
1774
  );
1441
1775
  const full = await collect(store, {
@@ -1449,8 +1783,7 @@ var runStoreTck = (options) => {
1449
1783
  it7("supports backward traversal", async () => {
1450
1784
  const s = `q-back-${uid()}`;
1451
1785
  const committed = await store.commit(
1452
- s,
1453
- [inc(1), inc(2), inc(3)],
1786
+ [{ stream: s, msgs: [inc(1), inc(2), inc(3)] }],
1454
1787
  make_meta({ stream: s })
1455
1788
  );
1456
1789
  const forward = await collect(store, { stream: s, stream_exact: true });
@@ -1475,8 +1808,7 @@ var runStoreTck = (options) => {
1475
1808
  it7("after/before bound the id range", async () => {
1476
1809
  const s = `q-bounds-${uid()}`;
1477
1810
  const committed = await store.commit(
1478
- s,
1479
- [inc(1), inc(2), inc(3), inc(4)],
1811
+ [{ stream: s, msgs: [inc(1), inc(2), inc(3), inc(4)] }],
1480
1812
  make_meta({ stream: s })
1481
1813
  );
1482
1814
  const after_first = await collect(store, {
@@ -1499,8 +1831,7 @@ var runStoreTck = (options) => {
1499
1831
  it7("names:[] matches no events", async () => {
1500
1832
  const s = `q-names-empty-${uid()}`;
1501
1833
  await store.commit(
1502
- s,
1503
- [inc(1), dec(1)],
1834
+ [{ stream: s, msgs: [inc(1), dec(1)] }],
1504
1835
  make_meta({ stream: s })
1505
1836
  );
1506
1837
  const none = await collect(store, {
@@ -1515,8 +1846,7 @@ var runStoreTck = (options) => {
1515
1846
  it7("after:0 and before:0 are honored as id bounds", async () => {
1516
1847
  const s = `q-zero-bound-${uid()}`;
1517
1848
  await store.commit(
1518
- s,
1519
- [inc(1), inc(2), inc(3)],
1849
+ [{ stream: s, msgs: [inc(1), inc(2), inc(3)] }],
1520
1850
  make_meta({ stream: s })
1521
1851
  );
1522
1852
  expect8(
@@ -1547,8 +1877,7 @@ var runStoreTck = (options) => {
1547
1877
  it7("created_after/created_before filter by timestamp", async () => {
1548
1878
  const s = `q-ts-${uid()}`;
1549
1879
  const committed = await store.commit(
1550
- s,
1551
- [inc(1)],
1880
+ [{ stream: s, msgs: [inc(1)] }],
1552
1881
  make_meta({ stream: s })
1553
1882
  );
1554
1883
  const ts = committed[0].created.getTime();
@@ -1571,8 +1900,7 @@ var runStoreTck = (options) => {
1571
1900
  it7("created bounds exclude the event they are pinned to", async () => {
1572
1901
  const s = `q-ts-self-${uid()}`;
1573
1902
  const committed = await store.commit(
1574
- s,
1575
- [inc(1)],
1903
+ [{ stream: s, msgs: [inc(1)] }],
1576
1904
  make_meta({ stream: s })
1577
1905
  );
1578
1906
  const own = committed[0].created;
@@ -1592,8 +1920,7 @@ var runStoreTck = (options) => {
1592
1920
  it7("backward traversal short-circuits at `after` id boundary", async () => {
1593
1921
  const s = `q-back-after-${uid()}`;
1594
1922
  const committed = await store.commit(
1595
- s,
1596
- [inc(1), inc(2), inc(3)],
1923
+ [{ stream: s, msgs: [inc(1), inc(2), inc(3)] }],
1597
1924
  make_meta({ stream: s })
1598
1925
  );
1599
1926
  const got = await collect(store, {
@@ -1610,8 +1937,7 @@ var runStoreTck = (options) => {
1610
1937
  it7("backward traversal short-circuits at `created_after` boundary", async () => {
1611
1938
  const s = `q-back-cafter-${uid()}`;
1612
1939
  await store.commit(
1613
- s,
1614
- [inc(1)],
1940
+ [{ stream: s, msgs: [inc(1)] }],
1615
1941
  make_meta({ stream: s })
1616
1942
  );
1617
1943
  const future = new Date(Date.now() + 6e4);
@@ -1626,8 +1952,7 @@ var runStoreTck = (options) => {
1626
1952
  it7("backward traversal honors created_before by skipping newer events", async () => {
1627
1953
  const s = `q-back-ts-${uid()}`;
1628
1954
  const committed = await store.commit(
1629
- s,
1630
- [inc(1)],
1955
+ [{ stream: s, msgs: [inc(1)] }],
1631
1956
  make_meta()
1632
1957
  );
1633
1958
  const past = new Date(committed[0].created.getTime() - 6e4);
@@ -1644,13 +1969,11 @@ var runStoreTck = (options) => {
1644
1969
  const a = `q-exact-${tag}`;
1645
1970
  const b = `q-exact-${tag}-extra`;
1646
1971
  await store.commit(
1647
- a,
1648
- [inc(1)],
1972
+ [{ stream: a, msgs: [inc(1)] }],
1649
1973
  make_meta({ stream: a })
1650
1974
  );
1651
1975
  await store.commit(
1652
- b,
1653
- [inc(2)],
1976
+ [{ stream: b, msgs: [inc(2)] }],
1654
1977
  make_meta({ stream: b })
1655
1978
  );
1656
1979
  const exact = await collect(store, { stream: a, stream_exact: true });
@@ -1662,13 +1985,11 @@ var runStoreTck = (options) => {
1662
1985
  const inner = `qr-${tag}-inner`;
1663
1986
  const longer = `qr-${tag}-inner-extra`;
1664
1987
  await store.commit(
1665
- inner,
1666
- [inc(1)],
1988
+ [{ stream: inner, msgs: [inc(1)] }],
1667
1989
  make_meta({ stream: inner })
1668
1990
  );
1669
1991
  await store.commit(
1670
- longer,
1671
- [inc(2)],
1992
+ [{ stream: longer, msgs: [inc(2)] }],
1672
1993
  make_meta({ stream: longer })
1673
1994
  );
1674
1995
  const got = await collect(store, { stream: `qr-${tag}-inner` });
@@ -1679,13 +2000,11 @@ var runStoreTck = (options) => {
1679
2000
  const inner = `qr-${tag}-anchor`;
1680
2001
  const longer = `qr-${tag}-anchor-extra`;
1681
2002
  await store.commit(
1682
- inner,
1683
- [inc(1)],
2003
+ [{ stream: inner, msgs: [inc(1)] }],
1684
2004
  make_meta({ stream: inner })
1685
2005
  );
1686
2006
  await store.commit(
1687
- longer,
1688
- [inc(2)],
2007
+ [{ stream: longer, msgs: [inc(2)] }],
1689
2008
  make_meta({ stream: longer })
1690
2009
  );
1691
2010
  const got = await collect(store, { stream: `^qr-${tag}-anchor$` });
@@ -1698,18 +2017,15 @@ var runStoreTck = (options) => {
1698
2017
  const b = `qr-${tag}-pfx-b`;
1699
2018
  const other = `zz-${tag}-other`;
1700
2019
  await store.commit(
1701
- a,
1702
- [inc(1)],
2020
+ [{ stream: a, msgs: [inc(1)] }],
1703
2021
  make_meta({ stream: a })
1704
2022
  );
1705
2023
  await store.commit(
1706
- b,
1707
- [inc(2)],
2024
+ [{ stream: b, msgs: [inc(2)] }],
1708
2025
  make_meta({ stream: b })
1709
2026
  );
1710
2027
  await store.commit(
1711
- other,
1712
- [inc(3)],
2028
+ [{ stream: other, msgs: [inc(3)] }],
1713
2029
  make_meta({ stream: other })
1714
2030
  );
1715
2031
  const got = await collect(store, { stream: `^qr-${tag}-pfx-` });
@@ -1720,8 +2036,7 @@ var runStoreTck = (options) => {
1720
2036
  const seed_streams = async (streams) => {
1721
2037
  for (const s of streams)
1722
2038
  await store.commit(
1723
- s,
1724
- [inc(1)],
2039
+ [{ stream: s, msgs: [inc(1)] }],
1725
2040
  make_meta({ stream: s })
1726
2041
  );
1727
2042
  };
@@ -1885,13 +2200,22 @@ var runStoreTck = (options) => {
1885
2200
  const leases = await store.claim(5, 5, `w-${uid()}`, 5e3);
1886
2201
  expect8(leases.filter((l) => l.stream === s)).toHaveLength(0);
1887
2202
  });
2203
+ it7("takes a target reading itself, whatever its name holds", async () => {
2204
+ const name = `self-a+b(${uid()}`;
2205
+ await store.subscribe([{ stream: name, source: name }]);
2206
+ const found = [];
2207
+ await store.query_streams((p) => found.push(p.stream), {
2208
+ stream: name,
2209
+ stream_exact: true
2210
+ });
2211
+ expect8(found).toEqual([name]);
2212
+ });
1888
2213
  it7("claims a fresh subscription as soon as its first event is marked", async () => {
1889
2214
  const s = `fresh-first-${uid()}`;
1890
2215
  const src = `fresh-first-src-${uid()}`;
1891
2216
  await store.subscribe([{ stream: s, source: src }]);
1892
2217
  const [first] = await store.commit(
1893
- src,
1894
- [inc(1)],
2218
+ [{ stream: src, msgs: [inc(1)] }],
1895
2219
  make_meta({ stream: src })
1896
2220
  );
1897
2221
  await store.subscribe([{ stream: s, correlated_at: first.id }]);
@@ -2080,8 +2404,7 @@ var runStoreTck = (options) => {
2080
2404
  const src = `ws-unmarked-src-${uid()}`;
2081
2405
  await store.subscribe([{ stream: s, source: src }]);
2082
2406
  const [event] = await store.commit(
2083
- src,
2084
- [inc(1)],
2407
+ [{ stream: src, msgs: [inc(1)] }],
2085
2408
  make_meta({ stream: src })
2086
2409
  );
2087
2410
  expect8(await claimable(s)).toBe(false);
@@ -2113,8 +2436,7 @@ var runStoreTck = (options) => {
2113
2436
  const s = `claim-${uid()}`;
2114
2437
  await store.subscribe([{ stream: s }]);
2115
2438
  await store.commit(
2116
- s,
2117
- [inc(1)],
2439
+ [{ stream: s, msgs: [inc(1)] }],
2118
2440
  make_meta({ stream: s })
2119
2441
  );
2120
2442
  const by = `worker-${uid()}`;
@@ -2137,8 +2459,7 @@ var runStoreTck = (options) => {
2137
2459
  const other = `claim-other-${uid()}`;
2138
2460
  await store.subscribe([{ stream: s }]);
2139
2461
  await store.commit(
2140
- s,
2141
- [inc(1)],
2462
+ [{ stream: s, msgs: [inc(1)] }],
2142
2463
  make_meta({ stream: s })
2143
2464
  );
2144
2465
  await correlate();
@@ -2147,8 +2468,7 @@ var runStoreTck = (options) => {
2147
2468
  expect8(targetA).toBeDefined();
2148
2469
  await store.subscribe([{ stream: other }]);
2149
2470
  await store.commit(
2150
- other,
2151
- [inc(2)],
2471
+ [{ stream: other, msgs: [inc(2)] }],
2152
2472
  make_meta({ stream: other })
2153
2473
  );
2154
2474
  await correlate();
@@ -2161,8 +2481,7 @@ var runStoreTck = (options) => {
2161
2481
  const s = `claim-stolen-${uid()}`;
2162
2482
  await store.subscribe([{ stream: s }]);
2163
2483
  await store.commit(
2164
- s,
2165
- [inc(1), inc(2)],
2484
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
2166
2485
  make_meta({ stream: s })
2167
2486
  );
2168
2487
  await correlate();
@@ -2188,8 +2507,7 @@ var runStoreTck = (options) => {
2188
2507
  const s = `claim-dual-${uid()}`;
2189
2508
  await store.subscribe([{ stream: s }]);
2190
2509
  await store.commit(
2191
- s,
2192
- [inc(1), inc(2)],
2510
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
2193
2511
  make_meta({ stream: s })
2194
2512
  );
2195
2513
  await correlate();
@@ -2218,13 +2536,11 @@ var runStoreTck = (options) => {
2218
2536
  ]);
2219
2537
  for (const stream of highs)
2220
2538
  await fresh.commit(
2221
- stream,
2222
- [inc(1)],
2539
+ [{ stream, msgs: [inc(1)] }],
2223
2540
  make_meta({ stream })
2224
2541
  );
2225
2542
  await fresh.commit(
2226
- low,
2227
- [inc(1)],
2543
+ [{ stream: low, msgs: [inc(1)] }],
2228
2544
  make_meta({ stream: low })
2229
2545
  );
2230
2546
  const by = `w-${uid()}`;
@@ -2240,8 +2556,7 @@ var runStoreTck = (options) => {
2240
2556
  for (const l of leases) {
2241
2557
  await fresh.ack([{ ...l, at: l.at + 1 }]);
2242
2558
  await fresh.commit(
2243
- l.stream,
2244
- [inc(1)],
2559
+ [{ stream: l.stream, msgs: [inc(1)] }],
2245
2560
  make_meta({ stream: l.stream })
2246
2561
  );
2247
2562
  }
@@ -2255,8 +2570,7 @@ var runStoreTck = (options) => {
2255
2570
  const s = `claim-dedup-${uid()}`;
2256
2571
  await store.subscribe([{ stream: s }]);
2257
2572
  await store.commit(
2258
- s,
2259
- [inc(1)],
2573
+ [{ stream: s, msgs: [inc(1)] }],
2260
2574
  make_meta({ stream: s })
2261
2575
  );
2262
2576
  await correlate();
@@ -2269,13 +2583,11 @@ var runStoreTck = (options) => {
2269
2583
  const sibling = `ack-sibling-${uid()}`;
2270
2584
  await store.subscribe([{ stream: s }, { stream: sibling }]);
2271
2585
  await store.commit(
2272
- s,
2273
- [inc(1)],
2586
+ [{ stream: s, msgs: [inc(1)] }],
2274
2587
  make_meta({ stream: s })
2275
2588
  );
2276
2589
  await store.commit(
2277
- sibling,
2278
- [inc(2)],
2590
+ [{ stream: sibling, msgs: [inc(2)] }],
2279
2591
  make_meta({ stream: sibling })
2280
2592
  );
2281
2593
  await correlate();
@@ -2325,8 +2637,7 @@ var runStoreTck = (options) => {
2325
2637
  const s = `lease-retry-${uid()}`;
2326
2638
  await fresh.subscribe([{ stream: s }]);
2327
2639
  await fresh.commit(
2328
- s,
2329
- [inc(1)],
2640
+ [{ stream: s, msgs: [inc(1)] }],
2330
2641
  make_meta({ stream: s })
2331
2642
  );
2332
2643
  await correlate(fresh);
@@ -2351,8 +2662,7 @@ var runStoreTck = (options) => {
2351
2662
  const s = `lease-zero-${uid()}`;
2352
2663
  await fresh.subscribe([{ stream: s }]);
2353
2664
  const [committed] = await fresh.commit(
2354
- s,
2355
- [inc(1)],
2665
+ [{ stream: s, msgs: [inc(1)] }],
2356
2666
  make_meta({ stream: s })
2357
2667
  );
2358
2668
  await correlate(fresh);
@@ -2381,8 +2691,7 @@ var runStoreTck = (options) => {
2381
2691
  const s = `lease-lag-${uid()}`;
2382
2692
  await fresh.subscribe([{ stream: s }]);
2383
2693
  await fresh.commit(
2384
- s,
2385
- [inc(1)],
2694
+ [{ stream: s, msgs: [inc(1)] }],
2386
2695
  make_meta({ stream: s })
2387
2696
  );
2388
2697
  await correlate(fresh);
@@ -2511,8 +2820,7 @@ var runStoreTck = (options) => {
2511
2820
  await fresh.subscribe(streams.map((stream) => ({ stream })));
2512
2821
  for (const stream of streams) {
2513
2822
  await fresh.commit(
2514
- stream,
2515
- [inc(1)],
2823
+ [{ stream, msgs: [inc(1)] }],
2516
2824
  make_meta({ stream })
2517
2825
  );
2518
2826
  }
@@ -2584,8 +2892,7 @@ var runStoreTck = (options) => {
2584
2892
  const s = `block-${uid()}`;
2585
2893
  await store.subscribe([{ stream: s }]);
2586
2894
  await store.commit(
2587
- s,
2588
- [inc(1)],
2895
+ [{ stream: s, msgs: [inc(1)] }],
2589
2896
  make_meta({ stream: s })
2590
2897
  );
2591
2898
  await correlate();
@@ -2607,8 +2914,7 @@ var runStoreTck = (options) => {
2607
2914
  const s = `block-returns-${uid()}`;
2608
2915
  await store.subscribe([{ stream: s }]);
2609
2916
  await store.commit(
2610
- s,
2611
- [inc(1)],
2917
+ [{ stream: s, msgs: [inc(1)] }],
2612
2918
  make_meta({ stream: s })
2613
2919
  );
2614
2920
  await correlate();
@@ -2633,8 +2939,7 @@ var runStoreTck = (options) => {
2633
2939
  const s = `block-wrong-${uid()}`;
2634
2940
  await store.subscribe([{ stream: s }]);
2635
2941
  await store.commit(
2636
- s,
2637
- [inc(1)],
2942
+ [{ stream: s, msgs: [inc(1)] }],
2638
2943
  make_meta({ stream: s })
2639
2944
  );
2640
2945
  await correlate();
@@ -2652,8 +2957,7 @@ var runStoreTck = (options) => {
2652
2957
  const s = `block-twice-${uid()}`;
2653
2958
  await store.subscribe([{ stream: s }]);
2654
2959
  await store.commit(
2655
- s,
2656
- [inc(1)],
2960
+ [{ stream: s, msgs: [inc(1)] }],
2657
2961
  make_meta({ stream: s })
2658
2962
  );
2659
2963
  await correlate();
@@ -2678,8 +2982,7 @@ var runStoreTck = (options) => {
2678
2982
  await store.subscribe([{ stream: s }, { stream: ctl }]);
2679
2983
  for (const st of [s, ctl])
2680
2984
  await store.commit(
2681
- st,
2682
- [inc(1)],
2985
+ [{ stream: st, msgs: [inc(1)] }],
2683
2986
  make_meta({ stream: st })
2684
2987
  );
2685
2988
  expect8(await store.defer([s], Date.now() + 36e5)).toBe(1);
@@ -2692,8 +2995,7 @@ var runStoreTck = (options) => {
2692
2995
  const s = `defer-clock-${uid()}`;
2693
2996
  await store.subscribe([{ stream: s }]);
2694
2997
  await store.commit(
2695
- s,
2696
- [inc(1)],
2998
+ [{ stream: s, msgs: [inc(1)] }],
2697
2999
  make_meta({ stream: s })
2698
3000
  );
2699
3001
  expect8(await store.defer([s], Date.now() + 6e4)).toBe(1);
@@ -2716,8 +3018,7 @@ var runStoreTck = (options) => {
2716
3018
  const s = `defer-past-${uid()}`;
2717
3019
  await store.subscribe([{ stream: s }]);
2718
3020
  await store.commit(
2719
- s,
2720
- [inc(1)],
3021
+ [{ stream: s, msgs: [inc(1)] }],
2721
3022
  make_meta({ stream: s })
2722
3023
  );
2723
3024
  expect8(await store.defer([s], Date.now() - 1e3)).toBe(1);
@@ -2733,8 +3034,7 @@ var runStoreTck = (options) => {
2733
3034
  const s = `defer-retry-${uid()}`;
2734
3035
  await store.subscribe([{ stream: s }]);
2735
3036
  await store.commit(
2736
- s,
2737
- [inc(1)],
3037
+ [{ stream: s, msgs: [inc(1)] }],
2738
3038
  make_meta({ stream: s })
2739
3039
  );
2740
3040
  await store.defer([s], Date.now() + 36e5);
@@ -2756,8 +3056,7 @@ var runStoreTck = (options) => {
2756
3056
  const s = `defer-reset-${uid()}`;
2757
3057
  await store.subscribe([{ stream: s }]);
2758
3058
  await store.commit(
2759
- s,
2760
- [inc(1)],
3059
+ [{ stream: s, msgs: [inc(1)] }],
2761
3060
  make_meta({ stream: s })
2762
3061
  );
2763
3062
  await store.defer([s], Date.now() + 36e5);
@@ -2772,8 +3071,7 @@ var runStoreTck = (options) => {
2772
3071
  await store.subscribe([{ stream: deferred }, { stream: plain }]);
2773
3072
  for (const s of [deferred, plain])
2774
3073
  await store.commit(
2775
- s,
2776
- [inc(1)],
3074
+ [{ stream: s, msgs: [inc(1)] }],
2777
3075
  make_meta({ stream: s })
2778
3076
  );
2779
3077
  const due = Date.now() + 36e5;
@@ -2795,15 +3093,13 @@ var runStoreTck = (options) => {
2795
3093
  await store.subscribe([{ stream: a }, { stream: b }]);
2796
3094
  for (const s of [a, b])
2797
3095
  await store.commit(
2798
- s,
2799
- [inc(1)],
3096
+ [{ stream: s, msgs: [inc(1)] }],
2800
3097
  make_meta({ stream: s })
2801
3098
  );
2802
3099
  const ctl = `defer-filterctl-${tag}`;
2803
3100
  await store.subscribe([{ stream: ctl }]);
2804
3101
  await store.commit(
2805
- ctl,
2806
- [inc(1)],
3102
+ [{ stream: ctl, msgs: [inc(1)] }],
2807
3103
  make_meta({ stream: ctl })
2808
3104
  );
2809
3105
  const n = await store.defer(
@@ -2825,8 +3121,7 @@ var runStoreTck = (options) => {
2825
3121
  const s = `defer-dup-${uid()}`;
2826
3122
  await store.subscribe([{ stream: s }]);
2827
3123
  await store.commit(
2828
- s,
2829
- [inc(1)],
3124
+ [{ stream: s, msgs: [inc(1)] }],
2830
3125
  make_meta({ stream: s })
2831
3126
  );
2832
3127
  expect8(await store.defer([s, s], Date.now() + 36e5)).toBe(1);
@@ -2838,13 +3133,11 @@ var runStoreTck = (options) => {
2838
3133
  const ctl = `ackdefer-ctl-${uid()}`;
2839
3134
  await store.subscribe([{ stream: s }, { stream: ctl }]);
2840
3135
  await store.commit(
2841
- s,
2842
- [inc(1)],
3136
+ [{ stream: s, msgs: [inc(1)] }],
2843
3137
  make_meta({ stream: s })
2844
3138
  );
2845
3139
  const [ctl_head] = await store.commit(
2846
- ctl,
2847
- [inc(1)],
3140
+ [{ stream: ctl, msgs: [inc(1)] }],
2848
3141
  make_meta({ stream: ctl })
2849
3142
  );
2850
3143
  await correlate();
@@ -2858,8 +3151,7 @@ var runStoreTck = (options) => {
2858
3151
  expect8(acked.find((l) => l.stream === ctl)).toBeDefined();
2859
3152
  expect8(acked.find((l) => l.stream === s)).toBeUndefined();
2860
3153
  await store.commit(
2861
- ctl,
2862
- [inc(2)],
3154
+ [{ stream: ctl, msgs: [inc(2)] }],
2863
3155
  make_meta({ stream: ctl })
2864
3156
  );
2865
3157
  await correlate();
@@ -2871,8 +3163,7 @@ var runStoreTck = (options) => {
2871
3163
  const s = `ackdefer-past-${uid()}`;
2872
3164
  await store.subscribe([{ stream: s }]);
2873
3165
  await store.commit(
2874
- s,
2875
- [inc(1)],
3166
+ [{ stream: s, msgs: [inc(1)] }],
2876
3167
  make_meta({ stream: s })
2877
3168
  );
2878
3169
  await correlate();
@@ -2891,8 +3182,7 @@ var runStoreTck = (options) => {
2891
3182
  const s = `ackdefer-retry-${uid()}`;
2892
3183
  await store.subscribe([{ stream: s }]);
2893
3184
  await store.commit(
2894
- s,
2895
- [inc(1)],
3185
+ [{ stream: s, msgs: [inc(1)] }],
2896
3186
  make_meta({ stream: s })
2897
3187
  );
2898
3188
  await correlate();
@@ -2911,13 +3201,11 @@ var runStoreTck = (options) => {
2911
3201
  const s = `ackadvance-${uid()}`;
2912
3202
  await store.subscribe([{ stream: s }]);
2913
3203
  const [e1] = await store.commit(
2914
- s,
2915
- [inc(1)],
3204
+ [{ stream: s, msgs: [inc(1)] }],
2916
3205
  make_meta({ stream: s })
2917
3206
  );
2918
3207
  await store.commit(
2919
- s,
2920
- [inc(1)],
3208
+ [{ stream: s, msgs: [inc(1)] }],
2921
3209
  make_meta({ stream: s })
2922
3210
  );
2923
3211
  await correlate();
@@ -2939,8 +3227,7 @@ var runStoreTck = (options) => {
2939
3227
  const s = `ackdefer-owner-${uid()}`;
2940
3228
  await store.subscribe([{ stream: s }]);
2941
3229
  await store.commit(
2942
- s,
2943
- [inc(1)],
3230
+ [{ stream: s, msgs: [inc(1)] }],
2944
3231
  make_meta({ stream: s })
2945
3232
  );
2946
3233
  await correlate();
@@ -2958,8 +3245,7 @@ var runStoreTck = (options) => {
2958
3245
  const s = `reset-${uid()}`;
2959
3246
  await store.subscribe([{ stream: s }]);
2960
3247
  await store.commit(
2961
- s,
2962
- [inc(1)],
3248
+ [{ stream: s, msgs: [inc(1)] }],
2963
3249
  make_meta({ stream: s })
2964
3250
  );
2965
3251
  await correlate();
@@ -2978,8 +3264,7 @@ var runStoreTck = (options) => {
2978
3264
  const s = `reset-blk-${uid()}`;
2979
3265
  await store.subscribe([{ stream: s }]);
2980
3266
  await store.commit(
2981
- s,
2982
- [inc(1)],
3267
+ [{ stream: s, msgs: [inc(1)] }],
2983
3268
  make_meta({ stream: s })
2984
3269
  );
2985
3270
  await correlate();
@@ -3001,8 +3286,7 @@ var runStoreTck = (options) => {
3001
3286
  const s = `reset-dup-${uid()}`;
3002
3287
  await store.subscribe([{ stream: s }]);
3003
3288
  await store.commit(
3004
- s,
3005
- [inc(1)],
3289
+ [{ stream: s, msgs: [inc(1)] }],
3006
3290
  make_meta({ stream: s })
3007
3291
  );
3008
3292
  expect8(await store.reset([s, s])).toBe(1);
@@ -3013,13 +3297,11 @@ var runStoreTck = (options) => {
3013
3297
  const s = `unblock-${uid()}`;
3014
3298
  await store.subscribe([{ stream: s }]);
3015
3299
  await store.commit(
3016
- s,
3017
- [inc(1)],
3300
+ [{ stream: s, msgs: [inc(1)] }],
3018
3301
  make_meta({ stream: s })
3019
3302
  );
3020
3303
  await store.commit(
3021
- s,
3022
- [inc(2)],
3304
+ [{ stream: s, msgs: [inc(2)] }],
3023
3305
  make_meta({ stream: s })
3024
3306
  );
3025
3307
  await correlate();
@@ -3052,8 +3334,7 @@ var runStoreTck = (options) => {
3052
3334
  const s = `unblock-noop-${uid()}`;
3053
3335
  await store.subscribe([{ stream: s }]);
3054
3336
  await store.commit(
3055
- s,
3056
- [inc(1)],
3337
+ [{ stream: s, msgs: [inc(1)] }],
3057
3338
  make_meta({ stream: s })
3058
3339
  );
3059
3340
  expect8(await store.unblock([s])).toBe(0);
@@ -3067,13 +3348,11 @@ var runStoreTck = (options) => {
3067
3348
  const s2 = `unblock-mix-b-${uid()}`;
3068
3349
  await store.subscribe([{ stream: s1 }, { stream: s2 }]);
3069
3350
  await store.commit(
3070
- s1,
3071
- [inc(1)],
3351
+ [{ stream: s1, msgs: [inc(1)] }],
3072
3352
  make_meta({ stream: s1 })
3073
3353
  );
3074
3354
  await store.commit(
3075
- s2,
3076
- [inc(1)],
3355
+ [{ stream: s2, msgs: [inc(1)] }],
3077
3356
  make_meta({ stream: s2 })
3078
3357
  );
3079
3358
  await correlate();
@@ -3091,18 +3370,15 @@ var runStoreTck = (options) => {
3091
3370
  const s3 = `other-${tag}`;
3092
3371
  await store.subscribe([{ stream: s1 }, { stream: s2 }, { stream: s3 }]);
3093
3372
  await store.commit(
3094
- s1,
3095
- [inc(1)],
3373
+ [{ stream: s1, msgs: [inc(1)] }],
3096
3374
  make_meta({ stream: s1 })
3097
3375
  );
3098
3376
  await store.commit(
3099
- s2,
3100
- [inc(1)],
3377
+ [{ stream: s2, msgs: [inc(1)] }],
3101
3378
  make_meta({ stream: s2 })
3102
3379
  );
3103
3380
  await store.commit(
3104
- s3,
3105
- [inc(1)],
3381
+ [{ stream: s3, msgs: [inc(1)] }],
3106
3382
  make_meta({ stream: s3 })
3107
3383
  );
3108
3384
  await correlate();
@@ -3130,13 +3406,11 @@ var runStoreTck = (options) => {
3130
3406
  const s2 = `unblock-empty-${tag}-b`;
3131
3407
  await store.subscribe([{ stream: s1 }, { stream: s2 }]);
3132
3408
  await store.commit(
3133
- s1,
3134
- [inc(1)],
3409
+ [{ stream: s1, msgs: [inc(1)] }],
3135
3410
  make_meta({ stream: s1 })
3136
3411
  );
3137
3412
  await store.commit(
3138
- s2,
3139
- [inc(1)],
3413
+ [{ stream: s2, msgs: [inc(1)] }],
3140
3414
  make_meta({ stream: s2 })
3141
3415
  );
3142
3416
  await correlate();
@@ -3156,8 +3430,7 @@ var runStoreTck = (options) => {
3156
3430
  const s = `unblock-blocked-false-${tag}`;
3157
3431
  await store.subscribe([{ stream: s }]);
3158
3432
  await store.commit(
3159
- s,
3160
- [inc(1)],
3433
+ [{ stream: s, msgs: [inc(1)] }],
3161
3434
  make_meta({ stream: s })
3162
3435
  );
3163
3436
  expect8(
@@ -3180,18 +3453,15 @@ var runStoreTck = (options) => {
3180
3453
  { stream: other }
3181
3454
  ]);
3182
3455
  await store.commit(
3183
- s1,
3184
- [inc(1)],
3456
+ [{ stream: s1, msgs: [inc(1)] }],
3185
3457
  make_meta({ stream: s1 })
3186
3458
  );
3187
3459
  await store.commit(
3188
- s2,
3189
- [inc(1)],
3460
+ [{ stream: s2, msgs: [inc(1)] }],
3190
3461
  make_meta({ stream: s2 })
3191
3462
  );
3192
3463
  await store.commit(
3193
- other,
3194
- [inc(1)],
3464
+ [{ stream: other, msgs: [inc(1)] }],
3195
3465
  make_meta({ stream: other })
3196
3466
  );
3197
3467
  await correlate();
@@ -3222,13 +3492,11 @@ var runStoreTck = (options) => {
3222
3492
  const s2 = `reset-blocked-${tag}-fine`;
3223
3493
  await store.subscribe([{ stream: s1 }, { stream: s2 }]);
3224
3494
  await store.commit(
3225
- s1,
3226
- [inc(1)],
3495
+ [{ stream: s1, msgs: [inc(1)] }],
3227
3496
  make_meta({ stream: s1 })
3228
3497
  );
3229
3498
  await store.commit(
3230
- s2,
3231
- [inc(1)],
3499
+ [{ stream: s2, msgs: [inc(1)] }],
3232
3500
  make_meta({ stream: s2 })
3233
3501
  );
3234
3502
  await correlate();
@@ -3307,8 +3575,7 @@ var runStoreTck = (options) => {
3307
3575
  const ok = `pri-${tag}-ok`;
3308
3576
  const bad = `pri-${tag}-bad`;
3309
3577
  await fresh.commit(
3310
- src,
3311
- [inc(1)],
3578
+ [{ stream: src, msgs: [inc(1)] }],
3312
3579
  make_meta({ stream: src })
3313
3580
  );
3314
3581
  await fresh.subscribe([
@@ -3354,8 +3621,7 @@ var runStoreTck = (options) => {
3354
3621
  const tag = uid();
3355
3622
  const src = `pri-src-${tag}`;
3356
3623
  await fresh.commit(
3357
- src,
3358
- [inc(1), inc(2)],
3624
+ [{ stream: src, msgs: [inc(1), inc(2)] }],
3359
3625
  make_meta({ stream: src })
3360
3626
  );
3361
3627
  await fresh.subscribe([
@@ -3453,13 +3719,11 @@ var runStoreTck = (options) => {
3453
3719
  const sub_default = `lane-claim-def-${tag}`;
3454
3720
  const sub_slow = `lane-claim-slow-${tag}`;
3455
3721
  await store.commit(
3456
- src1,
3457
- [inc(1)],
3722
+ [{ stream: src1, msgs: [inc(1)] }],
3458
3723
  make_meta({ stream: src1 })
3459
3724
  );
3460
3725
  await store.commit(
3461
- src2,
3462
- [inc(1)],
3726
+ [{ stream: src2, msgs: [inc(1)] }],
3463
3727
  make_meta({ stream: src2 })
3464
3728
  );
3465
3729
  await store.subscribe([
@@ -3526,8 +3790,7 @@ var runStoreTck = (options) => {
3526
3790
  const a = `lane-reset-a-${tag}`;
3527
3791
  const b = `lane-reset-b-${tag}`;
3528
3792
  await store.commit(
3529
- src,
3530
- [inc(1)],
3793
+ [{ stream: src, msgs: [inc(1)] }],
3531
3794
  make_meta({ stream: src })
3532
3795
  );
3533
3796
  await store.subscribe([
@@ -3556,8 +3819,7 @@ var runStoreTck = (options) => {
3556
3819
  const a = `lane-ub-a-${tag}`;
3557
3820
  const b = `lane-ub-b-${tag}`;
3558
3821
  await store.commit(
3559
- src,
3560
- [inc(1)],
3822
+ [{ stream: src, msgs: [inc(1)] }],
3561
3823
  make_meta({ stream: src })
3562
3824
  );
3563
3825
  await store.subscribe([
@@ -3586,8 +3848,7 @@ var runStoreTck = (options) => {
3586
3848
  const tag = uid();
3587
3849
  const long = `${tag}-${"x".repeat(140)}`;
3588
3850
  await store.commit(
3589
- long,
3590
- [inc(1)],
3851
+ [{ stream: long, msgs: [inc(1)] }],
3591
3852
  make_meta({ stream: long })
3592
3853
  );
3593
3854
  await store.subscribe([
@@ -3612,8 +3873,7 @@ var runStoreTck = (options) => {
3612
3873
  for (const s of [restarted, retired]) {
3613
3874
  await store.subscribe([{ stream: s }]);
3614
3875
  await store.commit(
3615
- s,
3616
- [inc(1)],
3876
+ [{ stream: s, msgs: [inc(1)] }],
3617
3877
  make_meta({ stream: s })
3618
3878
  );
3619
3879
  }
@@ -3637,8 +3897,7 @@ var runStoreTck = (options) => {
3637
3897
  const lane = `inert-lane-${uid()}`;
3638
3898
  const s = `trunc-inert-${uid()}`;
3639
3899
  const [e] = await store.commit(
3640
- s,
3641
- [inc(1)],
3900
+ [{ stream: s, msgs: [inc(1)] }],
3642
3901
  make_meta({ stream: s })
3643
3902
  );
3644
3903
  await store.subscribe([{ stream: s, correlated_at: e.id, lane }]);
@@ -3653,8 +3912,7 @@ var runStoreTck = (options) => {
3653
3912
  it7("seeds a tombstone when no snapshot is provided", async () => {
3654
3913
  const s = `trunc-tomb-${uid()}`;
3655
3914
  await store.commit(
3656
- s,
3657
- [inc(1), inc(2)],
3915
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
3658
3916
  make_meta({ stream: s })
3659
3917
  );
3660
3918
  const result = await store.truncate([{ stream: s }]);
@@ -3674,8 +3932,7 @@ var runStoreTck = (options) => {
3674
3932
  it7("seeds a snapshot when one is provided", async () => {
3675
3933
  const s = `trunc-snap-${uid()}`;
3676
3934
  await store.commit(
3677
- s,
3678
- [inc(1)],
3935
+ [{ stream: s, msgs: [inc(1)] }],
3679
3936
  make_meta({ stream: s })
3680
3937
  );
3681
3938
  const result = await store.truncate([
@@ -3698,8 +3955,7 @@ var runStoreTck = (options) => {
3698
3955
  it7("leaves exactly one seed when a stream is listed twice", async () => {
3699
3956
  const s = `trunc-dup-${uid()}`;
3700
3957
  await store.commit(
3701
- s,
3702
- [inc(1), inc(2)],
3958
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
3703
3959
  make_meta({ stream: s })
3704
3960
  );
3705
3961
  const result = await store.truncate([
@@ -3720,13 +3976,11 @@ var runStoreTck = (options) => {
3720
3976
  it7("applies windowed targets before full ones", async () => {
3721
3977
  const s = `trunc-mix-${uid()}`;
3722
3978
  await store.commit(
3723
- s,
3724
- [inc(1)],
3979
+ [{ stream: s, msgs: [inc(1)] }],
3725
3980
  make_meta({ stream: s })
3726
3981
  );
3727
3982
  await store.commit(
3728
- s,
3729
- [inc(2)],
3983
+ [{ stream: s, msgs: [inc(2)] }],
3730
3984
  make_meta({ stream: s })
3731
3985
  );
3732
3986
  await sleep(5);
@@ -3763,28 +4017,23 @@ var runStoreTck = (options) => {
3763
4017
  describe8("windowed (before boundary)", () => {
3764
4018
  async function seed_windowed(s) {
3765
4019
  await store.commit(
3766
- s,
3767
- [inc(1), inc(1)],
4020
+ [{ stream: s, msgs: [inc(1), inc(1)] }],
3768
4021
  make_meta({ stream: s })
3769
4022
  );
3770
4023
  const [snap_a] = await store.commit(
3771
- s,
3772
- [{ name: SNAP_EVENT2, data: { count: 2 } }],
4024
+ [{ stream: s, msgs: [{ name: SNAP_EVENT2, data: { count: 2 } }] }],
3773
4025
  make_meta({ stream: s })
3774
4026
  );
3775
4027
  await store.commit(
3776
- s,
3777
- [inc(1)],
4028
+ [{ stream: s, msgs: [inc(1)] }],
3778
4029
  make_meta({ stream: s })
3779
4030
  );
3780
4031
  const [snap_b] = await store.commit(
3781
- s,
3782
- [{ name: SNAP_EVENT2, data: { count: 3 } }],
4032
+ [{ stream: s, msgs: [{ name: SNAP_EVENT2, data: { count: 3 } }] }],
3783
4033
  make_meta({ stream: s })
3784
4034
  );
3785
4035
  await store.commit(
3786
- s,
3787
- [inc(1)],
4036
+ [{ stream: s, msgs: [inc(1)] }],
3788
4037
  make_meta({ stream: s })
3789
4038
  );
3790
4039
  return { snap_a, snap_b };
@@ -3854,8 +4103,7 @@ var runStoreTck = (options) => {
3854
4103
  it7("no-ops when no snapshot qualifies \u2014 stream absent from the result, events untouched", async () => {
3855
4104
  const s = `trunc-win-noop-${uid()}`;
3856
4105
  await store.commit(
3857
- s,
3858
- [inc(1), inc(1)],
4106
+ [{ stream: s, msgs: [inc(1), inc(1)] }],
3859
4107
  make_meta({ stream: s })
3860
4108
  );
3861
4109
  const none = await store.truncate([
@@ -3863,8 +4111,7 @@ var runStoreTck = (options) => {
3863
4111
  ]);
3864
4112
  expect8(none.has(s)).toBe(false);
3865
4113
  await store.commit(
3866
- s,
3867
- [{ name: SNAP_EVENT2, data: { count: 2 } }],
4114
+ [{ stream: s, msgs: [{ name: SNAP_EVENT2, data: { count: 2 } }] }],
3868
4115
  make_meta({ stream: s })
3869
4116
  );
3870
4117
  const too_early = await store.truncate([
@@ -3902,8 +4149,7 @@ var runStoreTck = (options) => {
3902
4149
  const f = `trunc-win-mix-f-${uid()}`;
3903
4150
  const { snap_b } = await seed_windowed(w);
3904
4151
  await store.commit(
3905
- f,
3906
- [inc(1)],
4152
+ [{ stream: f, msgs: [inc(1)] }],
3907
4153
  make_meta({ stream: f })
3908
4154
  );
3909
4155
  const result = await store.truncate([
@@ -3923,8 +4169,7 @@ var runStoreTck = (options) => {
3923
4169
  { stream: s, before: new Date(Date.now() + 6e4) }
3924
4170
  ]);
3925
4171
  const [next] = await store.commit(
3926
- s,
3927
- [inc(1)],
4172
+ [{ stream: s, msgs: [inc(1)] }],
3928
4173
  make_meta({ stream: s })
3929
4174
  );
3930
4175
  expect8(next.version).toBe(6);
@@ -4062,8 +4307,7 @@ var runStoreTck = (options) => {
4062
4307
  const sibling = `qb-${tag}-other`;
4063
4308
  await store.subscribe([{ stream: s }, { stream: sibling }]);
4064
4309
  await store.commit(
4065
- s,
4066
- [inc(1)],
4310
+ [{ stream: s, msgs: [inc(1)] }],
4067
4311
  make_meta({ stream: s })
4068
4312
  );
4069
4313
  await correlate();
@@ -4094,18 +4338,15 @@ var runStoreTck = (options) => {
4094
4338
  const sB = `qst-${tag}-b`;
4095
4339
  const sUnasked = `qst-${tag}-unasked`;
4096
4340
  await store.commit(
4097
- sA,
4098
- [inc(1), inc(2)],
4341
+ [{ stream: sA, msgs: [inc(1), inc(2)] }],
4099
4342
  make_meta({ stream: sA })
4100
4343
  );
4101
4344
  await store.commit(
4102
- sB,
4103
- [dec(5)],
4345
+ [{ stream: sB, msgs: [dec(5)] }],
4104
4346
  make_meta({ stream: sB })
4105
4347
  );
4106
4348
  await store.commit(
4107
- sUnasked,
4108
- [inc(99)],
4349
+ [{ stream: sUnasked, msgs: [inc(99)] }],
4109
4350
  make_meta({ stream: sUnasked })
4110
4351
  );
4111
4352
  const stats = await store.query_stats([sA, sB]);
@@ -4123,8 +4364,7 @@ var runStoreTck = (options) => {
4123
4364
  it7("limit: 0 returns no rows", async () => {
4124
4365
  const s = `qst-zero-${uid()}`;
4125
4366
  await store.commit(
4126
- s,
4127
- [inc(1)],
4367
+ [{ stream: s, msgs: [inc(1)] }],
4128
4368
  make_meta({ stream: s })
4129
4369
  );
4130
4370
  const filter = { stream: s, stream_exact: true };
@@ -4145,8 +4385,7 @@ var runStoreTck = (options) => {
4145
4385
  ];
4146
4386
  for (const s of names)
4147
4387
  await store.commit(
4148
- s,
4149
- [inc(1)],
4388
+ [{ stream: s, msgs: [inc(1)] }],
4150
4389
  make_meta({ stream: s })
4151
4390
  );
4152
4391
  const visited = [];
@@ -4169,18 +4408,15 @@ var runStoreTck = (options) => {
4169
4408
  const tag = uid();
4170
4409
  const s = `qst-tail-${tag}`;
4171
4410
  await store.commit(
4172
- s,
4173
- [inc(1)],
4411
+ [{ stream: s, msgs: [inc(1)] }],
4174
4412
  make_meta({ stream: s })
4175
4413
  );
4176
4414
  await store.commit(
4177
- s,
4178
- [inc(2)],
4415
+ [{ stream: s, msgs: [inc(2)] }],
4179
4416
  make_meta({ stream: s })
4180
4417
  );
4181
4418
  await store.commit(
4182
- s,
4183
- [inc(3)],
4419
+ [{ stream: s, msgs: [inc(3)] }],
4184
4420
  make_meta({ stream: s })
4185
4421
  );
4186
4422
  const stats = await store.query_stats([s], {
@@ -4196,14 +4432,12 @@ var runStoreTck = (options) => {
4196
4432
  const tag = uid();
4197
4433
  const s = `qst-cn-${tag}`;
4198
4434
  await store.commit(
4199
- s,
4200
- [inc(1)],
4435
+ [{ stream: s, msgs: [inc(1)] }],
4201
4436
  make_meta({ stream: s })
4202
4437
  );
4203
4438
  await store.truncate([{ stream: s, snapshot: { count: 99 } }]);
4204
4439
  await store.commit(
4205
- s,
4206
- [inc(2), inc(3), dec(1)],
4440
+ [{ stream: s, msgs: [inc(2), inc(3), dec(1)] }],
4207
4441
  make_meta({ stream: s })
4208
4442
  );
4209
4443
  const stats = await store.query_stats([s], {
@@ -4222,13 +4456,11 @@ var runStoreTck = (options) => {
4222
4456
  const s = `qst-excl-${tag}`;
4223
4457
  const sAllOut = `qst-allout-${tag}`;
4224
4458
  await store.commit(
4225
- s,
4226
- [inc(1), dec(2), inc(3)],
4459
+ [{ stream: s, msgs: [inc(1), dec(2), inc(3)] }],
4227
4460
  make_meta({ stream: s })
4228
4461
  );
4229
4462
  await store.commit(
4230
- sAllOut,
4231
- [inc(7)],
4463
+ [{ stream: sAllOut, msgs: [inc(7)] }],
4232
4464
  make_meta({ stream: sAllOut })
4233
4465
  );
4234
4466
  const all = await store.query_stats([s]);
@@ -4252,18 +4484,15 @@ var runStoreTck = (options) => {
4252
4484
  const tag = uid();
4253
4485
  const s = `qst-tt-${tag}`;
4254
4486
  const c1 = await store.commit(
4255
- s,
4256
- [inc(1)],
4487
+ [{ stream: s, msgs: [inc(1)] }],
4257
4488
  make_meta({ stream: s })
4258
4489
  );
4259
4490
  const c2 = await store.commit(
4260
- s,
4261
- [inc(2)],
4491
+ [{ stream: s, msgs: [inc(2)] }],
4262
4492
  make_meta({ stream: s })
4263
4493
  );
4264
4494
  await store.commit(
4265
- s,
4266
- [inc(3)],
4495
+ [{ stream: s, msgs: [inc(3)] }],
4267
4496
  make_meta({ stream: s })
4268
4497
  );
4269
4498
  const before = c2[0].id;
@@ -4287,18 +4516,15 @@ var runStoreTck = (options) => {
4287
4516
  const sB = `qsf-${tag}-orders-2`;
4288
4517
  const sOther = `qsf-${tag}-users-1`;
4289
4518
  await store.commit(
4290
- sA,
4291
- [inc(1)],
4519
+ [{ stream: sA, msgs: [inc(1)] }],
4292
4520
  make_meta({ stream: sA })
4293
4521
  );
4294
4522
  await store.commit(
4295
- sB,
4296
- [inc(2)],
4523
+ [{ stream: sB, msgs: [inc(2)] }],
4297
4524
  make_meta({ stream: sB })
4298
4525
  );
4299
4526
  await store.commit(
4300
- sOther,
4301
- [inc(3)],
4527
+ [{ stream: sOther, msgs: [inc(3)] }],
4302
4528
  make_meta({ stream: sOther })
4303
4529
  );
4304
4530
  const orders = await store.query_stats({
@@ -4321,13 +4547,11 @@ var runStoreTck = (options) => {
4321
4547
  const b = `qsc-${tag}-b`;
4322
4548
  await store.subscribe([{ stream: a }, { stream: b }]);
4323
4549
  await store.commit(
4324
- a,
4325
- [inc(1)],
4550
+ [{ stream: a, msgs: [inc(1)] }],
4326
4551
  make_meta({ stream: a })
4327
4552
  );
4328
4553
  await store.commit(
4329
- b,
4330
- [inc(2)],
4554
+ [{ stream: b, msgs: [inc(2)] }],
4331
4555
  make_meta({ stream: b })
4332
4556
  );
4333
4557
  await correlate();
@@ -4352,13 +4576,11 @@ var runStoreTck = (options) => {
4352
4576
  const a = `qse-${tag}-a`;
4353
4577
  const b = `qse-${tag}-b`;
4354
4578
  await store.commit(
4355
- a,
4356
- [inc(1)],
4579
+ [{ stream: a, msgs: [inc(1)] }],
4357
4580
  make_meta({ stream: a })
4358
4581
  );
4359
4582
  await store.commit(
4360
- b,
4361
- [dec(2)],
4583
+ [{ stream: b, msgs: [dec(2)] }],
4362
4584
  make_meta({ stream: b })
4363
4585
  );
4364
4586
  const all = await store.query_stats({});
@@ -4369,8 +4591,7 @@ var runStoreTck = (options) => {
4369
4591
  const tag = uid();
4370
4592
  const s = `qsfl-${tag}`;
4371
4593
  await store.commit(
4372
- s,
4373
- [inc(1), inc(2), dec(3)],
4594
+ [{ stream: s, msgs: [inc(1), inc(2), dec(3)] }],
4374
4595
  make_meta({ stream: s })
4375
4596
  );
4376
4597
  const c = await store.query_stats([s], {
@@ -4401,8 +4622,7 @@ var runStoreTck = (options) => {
4401
4622
  ];
4402
4623
  for (const s of streams) {
4403
4624
  await store.commit(
4404
- s,
4405
- [inc(1)],
4625
+ [{ stream: s, msgs: [inc(1)] }],
4406
4626
  make_meta({ stream: s })
4407
4627
  );
4408
4628
  }
@@ -4432,8 +4652,7 @@ var runStoreTck = (options) => {
4432
4652
  const tag = uid();
4433
4653
  for (const s of [`qsc-${tag}-c`, `qsc-${tag}-a`, `qsc-${tag}-b`])
4434
4654
  await store.commit(
4435
- s,
4436
- [inc(1), inc(2)],
4655
+ [{ stream: s, msgs: [inc(1), inc(2)] }],
4437
4656
  make_meta({ stream: s })
4438
4657
  );
4439
4658
  const pattern = { stream: `qsc-${tag}-.*` };
@@ -4582,8 +4801,7 @@ var runStoreTck = (options) => {
4582
4801
  const s = `head-${uid()}`;
4583
4802
  await store.subscribe([{ stream: s }]);
4584
4803
  await store.commit(
4585
- s,
4586
- [inc(1)],
4804
+ [{ stream: s, msgs: [inc(1)] }],
4587
4805
  make_meta({ stream: s })
4588
4806
  );
4589
4807
  const positions = [];
@@ -4599,7 +4817,7 @@ var runStoreTck = (options) => {
4599
4817
  const actor2 = { id: "reader", name: "Reader" };
4600
4818
  const reader = (shape) => {
4601
4819
  const Happened = z2.object(shape);
4602
- 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();
4603
4821
  };
4604
4822
  const over = async (S) => {
4605
4823
  const fresh = await options.factory();
@@ -4692,10 +4910,16 @@ var runStoreTck = (options) => {
4692
4910
  const t = await over(S);
4693
4911
  try {
4694
4912
  await t.fresh.commit(
4695
- t.stream,
4696
- [{ name: "Happened", data: { at: /* @__PURE__ */ new Date("2020-01-01") } }],
4697
- raw,
4698
- -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
4699
4923
  );
4700
4924
  const d = await t.read();
4701
4925
  expect8(d.label).toBeUndefined();
@@ -4709,10 +4933,14 @@ var runStoreTck = (options) => {
4709
4933
  const t = await over(S);
4710
4934
  try {
4711
4935
  await t.fresh.commit(
4712
- t.stream,
4713
- [{ name: "Happened", data: { at: "not a date" } }],
4714
- raw,
4715
- -1
4936
+ [
4937
+ {
4938
+ stream: t.stream,
4939
+ msgs: [{ name: "Happened", data: { at: "not a date" } }],
4940
+ expectedVersion: -1
4941
+ }
4942
+ ],
4943
+ raw
4716
4944
  );
4717
4945
  expect8((await t.read()).at).toBe("not a date");
4718
4946
  } finally {
@@ -4852,6 +5080,19 @@ var runStoreTck = (options) => {
4852
5080
  expect8((await take(k, who)).correlating).toBe(true);
4853
5081
  await release(k, who);
4854
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
+ });
4855
5096
  it7("answers undefined when no correlator is supplied", async () => {
4856
5097
  expect8(await store.subscribe([])).not.toHaveProperty("correlating");
4857
5098
  });
@@ -4933,7 +5174,234 @@ var runStoreTck = (options) => {
4933
5174
  });
4934
5175
  }
4935
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
+ });
4936
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
+ });
4937
5405
  beforeEach(async () => {
4938
5406
  await store.drop();
4939
5407
  await store.seed();
@@ -5119,8 +5587,7 @@ var runStoreTck = (options) => {
5119
5587
  it7("wipes pre-existing events before inserting", async () => {
5120
5588
  const old = `restore-old-${uid()}`;
5121
5589
  await store.commit(
5122
- old,
5123
- [inc(1), inc(2)],
5590
+ [{ stream: old, msgs: [inc(1), inc(2)] }],
5124
5591
  make_meta({ stream: old })
5125
5592
  );
5126
5593
  const fresh = `restore-fresh-${uid()}`;
@@ -5267,8 +5734,7 @@ var runStoreTck = (options) => {
5267
5734
  it7("rolls back atomically when the source throws mid-iteration", async () => {
5268
5735
  const original = `restore-pre-${uid()}`;
5269
5736
  const committed = await store.commit(
5270
- original,
5271
- [inc(1), inc(2), inc(3)],
5737
+ [{ stream: original, msgs: [inc(1), inc(2), inc(3)] }],
5272
5738
  make_meta({ stream: original })
5273
5739
  );
5274
5740
  const explosive = {
@@ -5441,12 +5907,16 @@ var runStoreTck = (options) => {
5441
5907
  it7("commits and loads pii alongside data", async () => {
5442
5908
  const s = `pii-roundtrip-${uid()}`;
5443
5909
  const committed = await store.commit(
5444
- s,
5445
5910
  [
5446
5911
  {
5447
- name: "Incremented",
5448
- data: { amount: 1 },
5449
- 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
+ ]
5450
5920
  }
5451
5921
  ],
5452
5922
  make_meta({ stream: s })
@@ -5469,12 +5939,16 @@ var runStoreTck = (options) => {
5469
5939
  });
5470
5940
  describe8("with_pii", () => {
5471
5941
  const seed_pii = async (s) => await store.commit(
5472
- s,
5473
5942
  [
5474
5943
  {
5475
- name: "Incremented",
5476
- data: { amount: 1 },
5477
- 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
+ ]
5478
5952
  }
5479
5953
  ],
5480
5954
  make_meta({ stream: s })
@@ -5535,12 +6009,16 @@ var runStoreTck = (options) => {
5535
6009
  const s = `pii-date-${uid()}`;
5536
6010
  const when = /* @__PURE__ */ new Date("2024-03-01T10:20:30.000Z");
5537
6011
  await store.commit(
5538
- s,
5539
6012
  [
5540
6013
  {
5541
- name: "Incremented",
5542
- data: { amount: 1, at: when },
5543
- pii: { born: when }
6014
+ stream: s,
6015
+ msgs: [
6016
+ {
6017
+ name: "Incremented",
6018
+ data: { amount: 1, at: when },
6019
+ pii: { born: when }
6020
+ }
6021
+ ]
5544
6022
  }
5545
6023
  ],
5546
6024
  make_meta({ stream: s })
@@ -5563,17 +6041,21 @@ var runStoreTck = (options) => {
5563
6041
  it7("query_stats head/tail never carry pii \u2014 introspection surface is pii-safe", async () => {
5564
6042
  const s = `pii-stats-${uid()}`;
5565
6043
  await store.commit(
5566
- s,
5567
6044
  [
5568
6045
  {
5569
- name: "Incremented",
5570
- data: { amount: 1 },
5571
- pii: { email: "head@example.com" }
5572
- },
5573
- {
5574
- name: "Incremented",
5575
- data: { amount: 2 },
5576
- 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
+ ]
5577
6059
  }
5578
6060
  ],
5579
6061
  make_meta({ stream: s })
@@ -5597,8 +6079,7 @@ var runStoreTck = (options) => {
5597
6079
  it7("passes through events without pii (pii is null or undefined on load)", async () => {
5598
6080
  const s = `pii-none-${uid()}`;
5599
6081
  await store.commit(
5600
- s,
5601
- [inc(1)],
6082
+ [{ stream: s, msgs: [inc(1)] }],
5602
6083
  make_meta({ stream: s })
5603
6084
  );
5604
6085
  const seen = [];
@@ -5614,17 +6095,21 @@ var runStoreTck = (options) => {
5614
6095
  it7("wipes pii for every event on the stream via forget_pii", async () => {
5615
6096
  const s = `pii-forget-${uid()}`;
5616
6097
  await store.commit(
5617
- s,
5618
6098
  [
5619
6099
  {
5620
- name: "Incremented",
5621
- data: { amount: 1 },
5622
- pii: { email: "a@example.com" }
5623
- },
5624
- {
5625
- name: "Incremented",
5626
- data: { amount: 2 },
5627
- 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
+ ]
5628
6113
  }
5629
6114
  ],
5630
6115
  make_meta({ stream: s })
@@ -5649,12 +6134,16 @@ var runStoreTck = (options) => {
5649
6134
  it7("is idempotent \u2014 second forget_pii returns 0, no error", async () => {
5650
6135
  const s = `pii-forget-idem-${uid()}`;
5651
6136
  await store.commit(
5652
- s,
5653
6137
  [
5654
6138
  {
5655
- name: "Incremented",
5656
- data: { amount: 1 },
5657
- 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
+ ]
5658
6147
  }
5659
6148
  ],
5660
6149
  make_meta({ stream: s })
@@ -5669,23 +6158,31 @@ var runStoreTck = (options) => {
5669
6158
  const sA = `pii-iso-a-${uid()}`;
5670
6159
  const sB = `pii-iso-b-${uid()}`;
5671
6160
  await store.commit(
5672
- sA,
5673
6161
  [
5674
6162
  {
5675
- name: "Incremented",
5676
- data: { amount: 1 },
5677
- 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
+ ]
5678
6171
  }
5679
6172
  ],
5680
6173
  make_meta({ stream: sA })
5681
6174
  );
5682
6175
  await store.commit(
5683
- sB,
5684
6176
  [
5685
6177
  {
5686
- name: "Incremented",
5687
- data: { amount: 1 },
5688
- 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
+ ]
5689
6186
  }
5690
6187
  ],
5691
6188
  make_meta({ stream: sB })
@@ -5711,8 +6208,7 @@ var runStoreTck = (options) => {
5711
6208
  it7("forget_pii on a stream with no pii events returns 0", async () => {
5712
6209
  const s = `pii-forget-empty-${uid()}`;
5713
6210
  await store.commit(
5714
- s,
5715
- [inc(1)],
6211
+ [{ stream: s, msgs: [inc(1)] }],
5716
6212
  make_meta({ stream: s })
5717
6213
  );
5718
6214
  const wiped = await store.forget_pii.call(store, s);
@@ -5737,8 +6233,7 @@ var runStoreTck = (options) => {
5737
6233
  try {
5738
6234
  const stream = `notify-${uid()}`;
5739
6235
  await writer.commit(
5740
- stream,
5741
- [inc(1)],
6236
+ [{ stream, msgs: [inc(1)] }],
5742
6237
  make_meta({ stream })
5743
6238
  );
5744
6239
  await arrived;
@@ -5766,18 +6261,15 @@ var runStoreTck = (options) => {
5766
6261
  const writer = await options.factory();
5767
6262
  try {
5768
6263
  await store.commit(
5769
- own,
5770
- [inc(1)],
6264
+ [{ stream: own, msgs: [inc(1)] }],
5771
6265
  make_meta({ stream: own })
5772
6266
  );
5773
6267
  await writer.commit(
5774
- remote,
5775
- [inc(1)],
6268
+ [{ stream: remote, msgs: [inc(1)] }],
5776
6269
  make_meta({ stream: remote })
5777
6270
  );
5778
6271
  await writer.commit(
5779
- sentinel,
5780
- [inc(1)],
6272
+ [{ stream: sentinel, msgs: [inc(1)] }],
5781
6273
  make_meta({ stream: sentinel })
5782
6274
  );
5783
6275
  await sentinel_arrived;
@@ -5789,7 +6281,7 @@ var runStoreTck = (options) => {
5789
6281
  await Promise.resolve(disposer());
5790
6282
  }
5791
6283
  });
5792
- 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 () => {
5793
6285
  const received = [];
5794
6286
  let resolve_sentinel;
5795
6287
  const sentinel_arrived = new Promise((res) => {
@@ -5804,13 +6296,11 @@ var runStoreTck = (options) => {
5804
6296
  const writer = await options.factory();
5805
6297
  try {
5806
6298
  await writer.commit(
5807
- batch,
5808
- [inc(1), inc(2), inc(3)],
6299
+ [{ stream: batch, msgs: [inc(1), inc(2), inc(3)] }],
5809
6300
  make_meta({ stream: batch })
5810
6301
  );
5811
6302
  await writer.commit(
5812
- sentinel,
5813
- [inc(1)],
6303
+ [{ stream: sentinel, msgs: [inc(1)] }],
5814
6304
  make_meta({ stream: sentinel })
5815
6305
  );
5816
6306
  await sentinel_arrived;
@@ -5833,6 +6323,45 @@ var runStoreTck = (options) => {
5833
6323
  await Promise.resolve(disposer());
5834
6324
  }
5835
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
+ });
5836
6365
  });
5837
6366
  }
5838
6367
  });