@shirudo/ddd-kit 3.0.0-rc.4 → 3.0.0-rc.6

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/testing.js CHANGED
@@ -1,5 +1,4 @@
1
- import { r as deepEqual } from "./chunks/deep-equal-except.js";
2
- import { t as isDispatchTrackingOutbox, u as isMintedEvent } from "./chunks/ports.js";
1
+ import { E as deepEqual, t as isDispatchTrackingOutbox, u as isRecordedDomainEvent } from "./chunks/ports.js";
3
2
 
4
3
  //#region src/testing/contract-assertions.ts
5
4
  /**
@@ -80,12 +79,13 @@ function gatedContractTest(gate, test) {
80
79
  }
81
80
  /**
82
81
  * Identities of an in-memory pending batch, with the shared precondition
83
- * that every event is recorded (minted). The `requirement` names the
84
- * suite-specific rule the harness violated when an event is unminted.
82
+ * that every event carries the recorded brand. The `requirement` names
83
+ * the suite-specific rule the harness violated when an event is not
84
+ * recorded.
85
85
  */
86
- function mintedPendingEventIds(events, requirement) {
86
+ function recordedPendingEventIds(events, requirement) {
87
87
  return events.map((event) => {
88
- assert(typeof event === "object" && event !== null && isMintedEvent(event), requirement);
88
+ assert(typeof event === "object" && event !== null && isRecordedDomainEvent(event), requirement);
89
89
  return event.eventId;
90
90
  });
91
91
  }
@@ -276,7 +276,8 @@ function createCommandOutboxContractTests(harness) {
276
276
  }
277
277
  };
278
278
  assert(!deepEqual(conflict.messages, original.messages), "harness contract: createCommand must return distinct content per seed, or the manufactured conflict is an exact retry");
279
- assert(await captureRejection(env.addCommitted([conflict])) !== void 0, "a reused origin event id with different messages must reject");
279
+ const rejection = await captureRejection(env.addCommitted([conflict]));
280
+ assert(rejection !== void 0, "a reused origin event id with different messages must reject");
280
281
  const stored = await env.readAll();
281
282
  assertEqual(stored.length, 1, "a conflicting retry must not append another receipt");
282
283
  assert(deepEqual(stored[0], original), "a conflicting retry must not replace the original receipt");
@@ -313,8 +314,10 @@ function createCommandOutboxContractTests(harness) {
313
314
  }
314
315
  }];
315
316
  for (const { fact, candidate } of conflicts) {
316
- assert(await captureRejection(env.addCommitted([candidate])) !== void 0, `a reused origin event id with a different source.${fact} must reject`);
317
- assert(deepEqual(await env.readAll(), [original]), `a source.${fact} conflict must leave the original receipt unchanged`);
317
+ const rejection = await captureRejection(env.addCommitted([candidate]));
318
+ assert(rejection !== void 0, `a reused origin event id with a different source.${fact} must reject`);
319
+ const stored = await env.readAll();
320
+ assert(deepEqual(stored, [original]), `a source.${fact} conflict must leave the original receipt unchanged`);
318
321
  }
319
322
  })
320
323
  },
@@ -347,8 +350,10 @@ function createCommandOutboxContractTests(harness) {
347
350
  }
348
351
  }
349
352
  };
350
- assert(await captureRejection(env.addCommitted([conflict])) !== void 0, `a reused origin event id with a different position.${fact} must reject`);
351
- assert(deepEqual(await env.readAll(), [original]), `a position.${fact} conflict must leave the original receipt unchanged`);
353
+ const rejection = await captureRejection(env.addCommitted([conflict]));
354
+ assert(rejection !== void 0, `a reused origin event id with a different position.${fact} must reject`);
355
+ const stored = await env.readAll();
356
+ assert(deepEqual(stored, [original]), `a position.${fact} conflict must leave the original receipt unchanged`);
352
357
  }
353
358
  })
354
359
  },
@@ -366,7 +371,8 @@ function createCommandOutboxContractTests(harness) {
366
371
  }
367
372
  };
368
373
  assert(!deepEqual(conflictingOriginal.messages, original.messages), "harness contract: createCommand must return distinct content per seed, or the manufactured conflict is an exact retry");
369
- assert(await captureRejection(env.addCommitted([newCommit, conflictingOriginal])) !== void 0, "a batch containing a conflicting origin must reject");
374
+ const rejection = await captureRejection(env.addCommitted([newCommit, conflictingOriginal]));
375
+ assert(rejection !== void 0, "a batch containing a conflicting origin must reject");
370
376
  const stored = await env.readAll();
371
377
  assertEqual(stored.length, 1, "a rejected batch must not leave its earlier new receipt behind");
372
378
  assert(deepEqual(stored[0], original), "a rejected batch must preserve the pre-existing receipt");
@@ -412,7 +418,8 @@ function createCommandOutboxContractTests(harness) {
412
418
  }
413
419
  }];
414
420
  await env.addCommitted(multiEventCommit);
415
- assert(deepEqual(await env.readAll(), multiEventCommit), "a multi-event commit must retain its shared version, sequence, and size for every receipt");
421
+ const stored = await env.readAll();
422
+ assert(deepEqual(stored, multiEventCommit), "a multi-event commit must retain its shared version, sequence, and size for every receipt");
416
423
  })
417
424
  },
418
425
  {
@@ -558,7 +565,8 @@ function createDeadlineStoreContractTests(harness) {
558
565
  await env.store.cancel("s", "drop");
559
566
  await env.store.cancel("s", "never-scheduled");
560
567
  });
561
- assert(deepEqual((await env.store.due(at(T1), 10)).map((d) => d.key), ["keep"]), "cancel must remove the addressed deadline and nothing else");
568
+ const due = await env.store.due(at(T1), 10);
569
+ assert(deepEqual(due.map((d) => d.key), ["keep"]), "cancel must remove the addressed deadline and nothing else");
562
570
  })
563
571
  },
564
572
  {
@@ -691,7 +699,8 @@ function createDeadlineStoreContractTests(harness) {
691
699
  const [poison] = await env.store.due(at(T1), 1);
692
700
  assert(poison !== void 0, "expected the earliest due deadline");
693
701
  await env.store.markFailed(poison.deliveryId, /* @__PURE__ */ new Error("boom"));
694
- assertEqual((await env.store.due(at(T1), 10)).find((d) => d.deliveryId === poison.deliveryId)?.attempts, 1, "attempts must be surfaced on the record after markFailed");
702
+ const afterOne = await env.store.due(at(T1), 10);
703
+ assertEqual(afterOne.find((d) => d.deliveryId === poison.deliveryId)?.attempts, 1, "attempts must be surfaced on the record after markFailed");
695
704
  for (let i = 1; i < ceiling; i++) await env.store.markFailed(poison.deliveryId, /* @__PURE__ */ new Error("boom"));
696
705
  assert((await env.store.due(at(T1), 10)).some((d) => d.key === "healthy"), "deadlines carry no cross-address ordering; a dead-lettered neighbor must not block delivery");
697
706
  })
@@ -794,9 +803,10 @@ function createEsRepositoryContractTests(harness) {
794
803
  const readAll = { limit: 100 };
795
804
  const createAggregateWithId = harness.createAggregateWithId;
796
805
  const snapshotState = harness.snapshotState;
806
+ const captureSnapshot = harness.captureSnapshot;
797
807
  const load = (repository, id) => loadAggregateOrFail(repository, id, "the stream was not appended or replayed correctly");
798
808
  const streamFor = (id) => harness.streamKeyFor(id);
799
- const recordedIds = (events) => mintedPendingEventIds(events, "pending events must be recorded before flush");
809
+ const recordedIds = (events) => recordedPendingEventIds(events, "pending events must be recorded before flush");
800
810
  const ids = (events) => events.map((event) => {
801
811
  assert(typeof event.eventId === "string" && event.eventId.length > 0, "committed events must carry their persisted eventId");
802
812
  return event.eventId;
@@ -891,7 +901,8 @@ function createEsRepositoryContractTests(harness) {
891
901
  repository.add(aggregate);
892
902
  throw new Error("rollback probe");
893
903
  }));
894
- assert(!(await environment.committedStreamEvents(streamFor(aggregate.id), readAll)).exists, "rollback must leave the stream absent");
904
+ const stream = await environment.committedStreamEvents(streamFor(aggregate.id), readAll);
905
+ assert(!stream.exists, "rollback must leave the stream absent");
895
906
  assertEqual((await environment.committedOutboxEvents()).length, 0, "rollback must leave the outbox empty");
896
907
  assert(deepEqual(aggregate.pendingEvents, pending), "rollback must retain the exact pending batch");
897
908
  })
@@ -920,10 +931,12 @@ function createEsRepositoryContractTests(harness) {
920
931
  const aggregate = harness.createAggregate();
921
932
  const pending = [...aggregate.pendingEvents];
922
933
  environment.failNextOutboxWrite(/* @__PURE__ */ new Error("outbox failure probe"));
923
- assert(await captureRejection(environment.run(async ({ repository }) => {
934
+ const rejection = await captureRejection(environment.run(async ({ repository }) => {
924
935
  repository.add(aggregate);
925
- })) !== void 0, "the outbox failure must reject");
926
- assert(!(await environment.committedStreamEvents(streamFor(aggregate.id), readAll)).exists, "stream append must roll back with the outbox");
936
+ }));
937
+ assert(rejection !== void 0, "the outbox failure must reject");
938
+ const stream = await environment.committedStreamEvents(streamFor(aggregate.id), readAll);
939
+ assert(!stream.exists, "stream append must roll back with the outbox");
927
940
  assertEqual((await environment.committedOutboxEvents()).length, 0, "failed outbox write must commit no envelope");
928
941
  assert(deepEqual(aggregate.pendingEvents, pending), "failed commit must acknowledge none of the event batch");
929
942
  })
@@ -982,9 +995,433 @@ function createEsRepositoryContractTests(harness) {
982
995
  assert(deepEqual(ids(after.events), ids(before.events)), "duplicate add must not modify the existing stream");
983
996
  })
984
997
  }));
998
+ tests.push(gatedContractTest({
999
+ capability: "captureSnapshot",
1000
+ satisfiedBy: Boolean(captureSnapshot)
1001
+ }, {
1002
+ name: "snapshot catch-up ends at the stream head and folds only the tail",
1003
+ run: inEnvironment(async (environment) => {
1004
+ assert(captureSnapshot !== void 0, "capability gate");
1005
+ const seeded = await seed(environment);
1006
+ await environment.run(async ({ repository }) => {
1007
+ const loaded = await load(repository, seeded.id);
1008
+ harness.mutate(loaded);
1009
+ repository.update(loaded);
1010
+ });
1011
+ const snapshotted = await environment.run(({ repository }) => load(repository, seeded.id));
1012
+ await captureSnapshot.call(harness, snapshotted, environment);
1013
+ const atHead = await environment.run(({ repository }) => load(repository, seeded.id));
1014
+ assert(atHead.version === snapshotted.version, `a snapshot at the head must load at the head ${snapshotted.version}, not beyond it; got ${atHead.version}`);
1015
+ const winner = await environment.run(async ({ repository }) => {
1016
+ const loaded = await load(repository, seeded.id);
1017
+ harness.mutate(loaded);
1018
+ harness.mutate(loaded);
1019
+ repository.update(loaded);
1020
+ return loaded;
1021
+ });
1022
+ const caughtUp = await environment.run(({ repository }) => load(repository, seeded.id));
1023
+ assert(caughtUp.version === winner.version, `snapshot catch-up must end at the stream head ${winner.version}; got ${caughtUp.version}`);
1024
+ if (snapshotState) assert(deepEqual(snapshotState.call(harness, caughtUp), snapshotState.call(harness, winner)), "snapshot catch-up must fold only the tail after the snapshot");
1025
+ })
1026
+ }));
985
1027
  return tests;
986
1028
  }
987
1029
 
1030
+ //#endregion
1031
+ //#region src/testing/event-bus-contract.ts
1032
+ /** Resolves after enough turns for a parallel batch to have started. */
1033
+ function turn() {
1034
+ return new Promise((resolve) => setTimeout(resolve, 0));
1035
+ }
1036
+ /**
1037
+ * The event-bus contract test suite: the proof that an implementation
1038
+ * delivers the guarantees the `EventBus` port documents. Ordering,
1039
+ * parallelism within one event, and error collection after the batch are
1040
+ * a **port contract, not a kit guarantee**; this suite is how an
1041
+ * implementation demonstrates them.
1042
+ *
1043
+ * The suite covers the port and nothing else. Construction options of the
1044
+ * kit's own adapter, such as a publish-depth bound or an observer bundle,
1045
+ * are not port behavior and are not pinned here.
1046
+ *
1047
+ * Framework-agnostic: bind with
1048
+ * `(test.skipped ? it.skip : it)(test.name, test.run)`.
1049
+ */
1050
+ function createEventBusContractTests(harness) {
1051
+ const inEnv = bindContractEnvironment(() => harness.createEnvironment());
1052
+ const first = () => harness.createFirstEvent();
1053
+ const second = () => harness.createSecondEvent();
1054
+ const types = () => {
1055
+ const firstType = first().type;
1056
+ const secondType = second().type;
1057
+ assert(firstType !== secondType, "the harness must supply two event factories with different types");
1058
+ return {
1059
+ firstType,
1060
+ secondType
1061
+ };
1062
+ };
1063
+ return [
1064
+ {
1065
+ name: "dispatches the events of one batch in input order",
1066
+ run: inEnv(async ({ bus }) => {
1067
+ const { firstType, secondType } = types();
1068
+ const seen = [];
1069
+ bus.subscribe(firstType, async (event) => {
1070
+ await new Promise((resolve) => setTimeout(resolve, 20));
1071
+ seen.push(event.type);
1072
+ });
1073
+ bus.subscribe(secondType, async (event) => {
1074
+ seen.push(event.type);
1075
+ });
1076
+ await bus.publish([
1077
+ first(),
1078
+ second(),
1079
+ first()
1080
+ ]);
1081
+ assertEqual(seen.join(","), [
1082
+ firstType,
1083
+ secondType,
1084
+ firstType
1085
+ ].join(","), "a batch dispatches its events in input order");
1086
+ })
1087
+ },
1088
+ {
1089
+ name: "starts the handlers of an event only after the previous event finished",
1090
+ run: inEnv(async ({ bus }) => {
1091
+ const { firstType, secondType } = types();
1092
+ const trace = [];
1093
+ bus.subscribe(firstType, async () => {
1094
+ trace.push("first:start");
1095
+ await turn();
1096
+ trace.push("first:end");
1097
+ });
1098
+ bus.subscribe(secondType, async () => {
1099
+ trace.push("second:start");
1100
+ });
1101
+ await bus.publish([first(), second()]);
1102
+ assertEqual(trace.join(","), "first:start,first:end,second:start", "an event dispatches only after the previous one finished");
1103
+ })
1104
+ },
1105
+ {
1106
+ name: "runs the handlers of one event in parallel",
1107
+ run: inEnv(async ({ bus }) => {
1108
+ const { firstType } = types();
1109
+ const trace = [];
1110
+ bus.subscribe(firstType, async () => {
1111
+ trace.push("a:start");
1112
+ await turn();
1113
+ trace.push("a:end");
1114
+ });
1115
+ bus.subscribe(firstType, async () => {
1116
+ trace.push("b:start");
1117
+ await turn();
1118
+ trace.push("b:end");
1119
+ });
1120
+ await bus.publish([first()]);
1121
+ assertEqual(trace.join(","), "a:start,b:start,a:end,b:end", "the handlers of one event run in parallel");
1122
+ })
1123
+ },
1124
+ {
1125
+ name: "runs every handler of an event when a peer fails",
1126
+ run: inEnv(async ({ bus }) => {
1127
+ const { firstType } = types();
1128
+ const seen = [];
1129
+ bus.subscribe(firstType, async () => {
1130
+ throw new Error("first handler failed");
1131
+ });
1132
+ bus.subscribe(firstType, async () => {
1133
+ seen.push("peer");
1134
+ });
1135
+ await captureRejection(bus.publish([first()]));
1136
+ assertEqual(seen.join(","), "peer", "a peer of a failing handler still runs");
1137
+ })
1138
+ },
1139
+ {
1140
+ name: "reaches the caller with a single failure directly",
1141
+ run: inEnv(async ({ bus }) => {
1142
+ const { firstType } = types();
1143
+ const failure = /* @__PURE__ */ new Error("the only handler failed");
1144
+ bus.subscribe(firstType, async () => {
1145
+ throw failure;
1146
+ });
1147
+ const thrown = await captureRejection(bus.publish([first()]));
1148
+ assertEqual(thrown, failure, "a single failure reaches the caller unchanged");
1149
+ })
1150
+ },
1151
+ {
1152
+ name: "collects two or more failures into an AggregateError",
1153
+ run: inEnv(async ({ bus }) => {
1154
+ const { firstType } = types();
1155
+ const one = /* @__PURE__ */ new Error("handler one failed");
1156
+ const two = /* @__PURE__ */ new Error("handler two failed");
1157
+ bus.subscribe(firstType, async () => {
1158
+ throw one;
1159
+ });
1160
+ bus.subscribe(firstType, async () => {
1161
+ throw two;
1162
+ });
1163
+ const thrown = await captureRejection(bus.publish([first()]));
1164
+ assert(thrown instanceof AggregateError, "two failures must reach the caller as an AggregateError");
1165
+ assertEqual(thrown.errors.length, 2, "the AggregateError carries every failure");
1166
+ assert(thrown.errors.includes(one) && thrown.errors.includes(two), "the AggregateError carries both failures");
1167
+ })
1168
+ },
1169
+ {
1170
+ name: "publishes the remaining events of a batch after a failure",
1171
+ run: inEnv(async ({ bus }) => {
1172
+ const { firstType, secondType } = types();
1173
+ const seen = [];
1174
+ bus.subscribe(firstType, async () => {
1175
+ throw new Error("first handler failed");
1176
+ });
1177
+ bus.subscribe(secondType, async (event) => {
1178
+ seen.push(event.type);
1179
+ });
1180
+ await captureRejection(bus.publish([first(), second()]));
1181
+ assertEqual(seen.join(","), secondType, "a failure does not stop the remaining events of the batch");
1182
+ })
1183
+ },
1184
+ {
1185
+ name: "treats a handler that throws synchronously as a rejection",
1186
+ run: inEnv(async ({ bus }) => {
1187
+ const { firstType } = types();
1188
+ const seen = [];
1189
+ bus.subscribe(firstType, () => {
1190
+ throw new Error("thrown synchronously");
1191
+ });
1192
+ bus.subscribe(firstType, async () => {
1193
+ seen.push("peer");
1194
+ });
1195
+ const thrown = await captureRejection(bus.publish([first()]));
1196
+ assert(thrown instanceof Error, "the throw must reach the caller");
1197
+ assertEqual(seen.join(","), "peer", "a synchronous throw does not skip the peers");
1198
+ })
1199
+ },
1200
+ {
1201
+ name: "hands every subscriber the same event object",
1202
+ run: inEnv(async ({ bus }) => {
1203
+ const { firstType } = types();
1204
+ const received = [];
1205
+ bus.subscribe(firstType, (event) => {
1206
+ received.push(event);
1207
+ });
1208
+ bus.subscribeAll((event) => {
1209
+ received.push(event);
1210
+ });
1211
+ const published = first();
1212
+ await bus.publish([published]);
1213
+ assertEqual(received.length, 2, "both subscriptions must receive the event");
1214
+ for (const one of received) assert(one === published, "every subscriber receives the published event itself, so its metadata cannot differ between them");
1215
+ })
1216
+ },
1217
+ {
1218
+ name: "delivers every type of a subscribed set to one handler",
1219
+ run: inEnv(async ({ bus }) => {
1220
+ const { firstType, secondType } = types();
1221
+ const seen = [];
1222
+ bus.subscribeMany([firstType, secondType], (event) => {
1223
+ seen.push(event.type);
1224
+ });
1225
+ await bus.publish([first(), second()]);
1226
+ assertEqual(seen.join(","), [firstType, secondType].join(","), "a set subscription receives every type in the set");
1227
+ })
1228
+ },
1229
+ {
1230
+ name: "releases every subscription of a set with one call",
1231
+ run: inEnv(async ({ bus }) => {
1232
+ const { firstType, secondType } = types();
1233
+ const seen = [];
1234
+ bus.subscribeMany([firstType, secondType], (event) => {
1235
+ seen.push(event.type);
1236
+ })();
1237
+ await bus.publish([first(), second()]);
1238
+ assertEqual(seen.length, 0, "one release must remove every subscription the set made");
1239
+ })
1240
+ },
1241
+ {
1242
+ name: "subscribes a repeated type of a set once",
1243
+ run: inEnv(async ({ bus }) => {
1244
+ const { firstType } = types();
1245
+ let calls = 0;
1246
+ bus.subscribeMany([firstType, firstType], () => {
1247
+ calls++;
1248
+ });
1249
+ await bus.publish([first()]);
1250
+ assertEqual(calls, 1, "the argument is a set, so a repeated type subscribes once");
1251
+ })
1252
+ },
1253
+ {
1254
+ name: "delivers every event type to a catch-all subscription",
1255
+ run: inEnv(async ({ bus }) => {
1256
+ const { firstType, secondType } = types();
1257
+ const seen = [];
1258
+ bus.subscribeAll(async (event) => {
1259
+ seen.push(event.type);
1260
+ });
1261
+ await bus.publish([first(), second()]);
1262
+ assertEqual(seen.join(","), [firstType, secondType].join(","), "a catch-all subscription receives every event type");
1263
+ })
1264
+ },
1265
+ {
1266
+ name: "runs a catch-all handler in the same batch as the typed handlers",
1267
+ run: inEnv(async ({ bus }) => {
1268
+ const { firstType } = types();
1269
+ const seen = [];
1270
+ bus.subscribe(firstType, async () => {
1271
+ throw new Error("typed handler failed");
1272
+ });
1273
+ bus.subscribeAll(async () => {
1274
+ seen.push("catch-all");
1275
+ });
1276
+ await captureRejection(bus.publish([first()]));
1277
+ assertEqual(seen.join(","), "catch-all", "a catch-all handler runs in the same batch as the typed ones");
1278
+ })
1279
+ },
1280
+ {
1281
+ name: "removes exactly one subscription when the same handler subscribed twice",
1282
+ run: inEnv(async ({ bus }) => {
1283
+ const { firstType } = types();
1284
+ let calls = 0;
1285
+ const handler = async () => {
1286
+ calls++;
1287
+ };
1288
+ const release = bus.subscribe(firstType, handler);
1289
+ bus.subscribe(firstType, handler);
1290
+ release();
1291
+ await bus.publish([first()]);
1292
+ assertEqual(calls, 1, "unsubscribe removes exactly one of two identical subscriptions");
1293
+ })
1294
+ },
1295
+ {
1296
+ name: "ignores a second call of the unsubscribe function",
1297
+ run: inEnv(async ({ bus }) => {
1298
+ const { firstType } = types();
1299
+ let calls = 0;
1300
+ const release = bus.subscribe(firstType, async () => {
1301
+ calls++;
1302
+ });
1303
+ bus.subscribe(firstType, async () => {
1304
+ calls++;
1305
+ });
1306
+ release();
1307
+ release();
1308
+ await bus.publish([first()]);
1309
+ assertEqual(calls, 1, "a second unsubscribe removes no further subscription");
1310
+ })
1311
+ },
1312
+ {
1313
+ name: "resolves once() with the next event of its type and stops after it",
1314
+ run: inEnv(async ({ bus }) => {
1315
+ const { firstType } = types();
1316
+ let deliveries = 0;
1317
+ bus.subscribeAll(async () => {
1318
+ deliveries++;
1319
+ });
1320
+ const waiting = bus.once(firstType);
1321
+ const announced = first();
1322
+ await bus.publish([announced]);
1323
+ const received = await waiting;
1324
+ await bus.publish([first()]);
1325
+ assertEqual(received.eventId, announced.eventId, "once() resolves with the first event of that type");
1326
+ assertEqual(deliveries, 2, "the second publication must still reach the bus");
1327
+ })
1328
+ },
1329
+ {
1330
+ name: "rejects once() when its timeout expires",
1331
+ run: inEnv(async ({ bus }) => {
1332
+ const { firstType } = types();
1333
+ const thrown = await captureRejection(bus.once(firstType, { timeoutMs: 5 }));
1334
+ assert(thrown !== void 0, "once() must reject after its timeout");
1335
+ })
1336
+ },
1337
+ {
1338
+ name: "rejects once() with the reason of an aborted signal",
1339
+ run: inEnv(async ({ bus }) => {
1340
+ const { firstType } = types();
1341
+ const controller = new AbortController();
1342
+ const reason = /* @__PURE__ */ new Error("the caller stopped waiting");
1343
+ const waiting = captureRejection(bus.once(firstType, { signal: controller.signal }));
1344
+ controller.abort(reason);
1345
+ assertEqual(await waiting, reason, "once() rejects with the reason of the signal");
1346
+ })
1347
+ },
1348
+ {
1349
+ name: "rejects a publication whose signal is already aborted",
1350
+ run: inEnv(async ({ bus }) => {
1351
+ const { firstType } = types();
1352
+ const controller = new AbortController();
1353
+ controller.abort(/* @__PURE__ */ new Error("stopped before publish"));
1354
+ let called = false;
1355
+ bus.subscribe(firstType, async () => {
1356
+ called = true;
1357
+ });
1358
+ const thrown = await captureRejection(bus.publish([first()], { signal: controller.signal }));
1359
+ assert(thrown !== void 0, "an aborted publication must reject");
1360
+ assertEqual(called, false, "an aborted publication dispatches no handler");
1361
+ })
1362
+ },
1363
+ {
1364
+ name: "refuses every operation after close",
1365
+ run: inEnv(async ({ bus }) => {
1366
+ const { firstType } = types();
1367
+ bus.close();
1368
+ let subscribeThrew = false;
1369
+ try {
1370
+ bus.subscribe(firstType, () => {});
1371
+ } catch {
1372
+ subscribeThrew = true;
1373
+ }
1374
+ let subscribeAllThrew = false;
1375
+ try {
1376
+ bus.subscribeAll(() => {});
1377
+ } catch {
1378
+ subscribeAllThrew = true;
1379
+ }
1380
+ assert(subscribeThrew, "subscribe must refuse a closed bus");
1381
+ assert(subscribeAllThrew, "subscribeAll must refuse a closed bus");
1382
+ assert(await captureRejection(bus.publish([first()])) !== void 0, "publish must refuse a closed bus");
1383
+ assert(await captureRejection(bus.once(firstType)) !== void 0, "once must refuse a closed bus");
1384
+ })
1385
+ },
1386
+ {
1387
+ name: "settles a pending once() when the bus closes",
1388
+ run: inEnv(async ({ bus }) => {
1389
+ const { firstType } = types();
1390
+ const waiting = captureRejection(bus.once(firstType));
1391
+ bus.close();
1392
+ assert(await waiting !== void 0, "closing must settle a pending once()");
1393
+ })
1394
+ },
1395
+ {
1396
+ name: "does nothing when close is called again",
1397
+ run: inEnv(async ({ bus }) => {
1398
+ bus.close();
1399
+ let threw = false;
1400
+ try {
1401
+ bus.close();
1402
+ } catch {
1403
+ threw = true;
1404
+ }
1405
+ assert(threw === false, "a second close must do nothing");
1406
+ })
1407
+ },
1408
+ {
1409
+ name: "bounds the wait, not the handler, when the timeout expires",
1410
+ run: inEnv(async ({ bus }) => {
1411
+ const { firstType } = types();
1412
+ let running = true;
1413
+ bus.subscribe(firstType, async () => {
1414
+ await new Promise((resolve) => setTimeout(resolve, 200));
1415
+ running = false;
1416
+ });
1417
+ const thrown = await captureRejection(bus.publish([first()], { timeoutMs: 10 }));
1418
+ assert(thrown !== void 0, "the publication must reject");
1419
+ assertEqual(running, true, "the timeout bounds the wait, so the handler keeps running");
1420
+ })
1421
+ }
1422
+ ];
1423
+ }
1424
+
988
1425
  //#endregion
989
1426
  //#region src/testing/event-store-contract.ts
990
1427
  /**
@@ -1126,7 +1563,10 @@ function createEventStoreContractTests(harness) {
1126
1563
  toVersion: Number.MAX_SAFE_INTEGER + 1
1127
1564
  }
1128
1565
  ];
1129
- for (const options of invalidOptions) assert(await captureRejection(store.readStream(firstKey, options)) instanceof RangeError, "invalid read bounds must reject with RangeError before querying the stream");
1566
+ for (const options of invalidOptions) {
1567
+ const rejection = await captureRejection(store.readStream(firstKey, options));
1568
+ assert(rejection instanceof RangeError, "invalid read bounds must reject with RangeError before querying the stream");
1569
+ }
1130
1570
  })
1131
1571
  },
1132
1572
  {
@@ -1227,7 +1667,8 @@ function createEventStoreContractTests(harness) {
1227
1667
  const [firstKey] = harness.createCollidingStreamKeys();
1228
1668
  const seeded = [harness.createEvent(firstKey, 30), harness.createEvent(firstKey, 31)];
1229
1669
  await store.append(firstKey, seeded, { expectedVersion: 0 });
1230
- assertChainContainsKitError(await captureRejection(store.append({ ...firstKey }, [harness.createEvent(firstKey, 32), harness.createEvent(firstKey, 33)], { expectedVersion: 1 })), ["CONCURRENCY_CONFLICT"], "a stale append must map the adapter conflict to ConcurrencyConflictError");
1670
+ const rejection = await captureRejection(store.append({ ...firstKey }, [harness.createEvent(firstKey, 32), harness.createEvent(firstKey, 33)], { expectedVersion: 1 }));
1671
+ assertChainContainsKitError(rejection, ["CONCURRENCY_CONFLICT"], "a stale append must map the adapter conflict to ConcurrencyConflictError");
1231
1672
  const stored = await store.readStream(firstKey, fixtureRead);
1232
1673
  assert(stored.exists && stored.lastVersion === 2 && stored.events.length === 2 && stored.events[0]?.eventId === seeded[0]?.eventId && stored.events[1]?.eventId === seeded[1]?.eventId, "a rejected multi-event append must leave the stream untouched");
1233
1674
  })
@@ -1238,7 +1679,8 @@ function createEventStoreContractTests(harness) {
1238
1679
  const [firstKey] = harness.createCollidingStreamKeys();
1239
1680
  const seeded = harness.createEvent(firstKey, 50);
1240
1681
  await store.append(firstKey, [seeded], { expectedVersion: 0 });
1241
- assertChainContainsKitError(await captureRejection(store.append({ ...firstKey }, [harness.createEvent(firstKey, 51), harness.createEvent(firstKey, 52)], { expectedVersion: 0 })), ["CONCURRENCY_CONFLICT", "DUPLICATE_AGGREGATE"], "a duplicate create must map to ConcurrencyConflictError or the sanctioned DuplicateAggregateError");
1682
+ const rejection = await captureRejection(store.append({ ...firstKey }, [harness.createEvent(firstKey, 51), harness.createEvent(firstKey, 52)], { expectedVersion: 0 }));
1683
+ assertChainContainsKitError(rejection, ["CONCURRENCY_CONFLICT", "DUPLICATE_AGGREGATE"], "a duplicate create must map to ConcurrencyConflictError or the sanctioned DuplicateAggregateError");
1242
1684
  const stored = await store.readStream(firstKey, fixtureRead);
1243
1685
  assert(stored.exists && stored.lastVersion === 1 && stored.events.length === 1 && stored.events[0]?.eventId === seeded.eventId, "a rejected duplicate-create batch must leave the existing stream untouched");
1244
1686
  })
@@ -1247,7 +1689,8 @@ function createEventStoreContractTests(harness) {
1247
1689
  name: "OCC: an expectedVersion ahead of an unknown stream conflicts without creating it",
1248
1690
  run: inEnv(async ({ store }) => {
1249
1691
  const [firstKey] = harness.createCollidingStreamKeys();
1250
- assertChainContainsKitError(await captureRejection(store.append(firstKey, [harness.createEvent(firstKey, 60)], { expectedVersion: 3 })), ["CONCURRENCY_CONFLICT"], "an expectedVersion ahead of the stream must map to ConcurrencyConflictError");
1692
+ const rejection = await captureRejection(store.append(firstKey, [harness.createEvent(firstKey, 60)], { expectedVersion: 3 }));
1693
+ assertChainContainsKitError(rejection, ["CONCURRENCY_CONFLICT"], "an expectedVersion ahead of the stream must map to ConcurrencyConflictError");
1251
1694
  const first = harness.createEvent(firstKey, 61);
1252
1695
  await store.append({ ...firstKey }, [first], { expectedVersion: 0 });
1253
1696
  const stored = await store.readStream(firstKey, fixtureRead);
@@ -1318,7 +1761,8 @@ function createIdempotencyStoreContractTests(harness) {
1318
1761
  return claim;
1319
1762
  });
1320
1763
  await env.store.confirm(first);
1321
- assertChainContainsKitError(await captureRejection(env.run((ctx) => env.store.claim(ctx, "key-1", "fp-OTHER"))), ["IDEMPOTENCY_KEY_REUSE"], "a different fingerprint must throw IdempotencyKeyReuseError, never replay another command's outcome");
1764
+ const rejection = await captureRejection(env.run((ctx) => env.store.claim(ctx, "key-1", "fp-OTHER")));
1765
+ assertChainContainsKitError(rejection, ["IDEMPOTENCY_KEY_REUSE"], "a different fingerprint must throw IdempotencyKeyReuseError, never replay another command's outcome");
1322
1766
  })
1323
1767
  },
1324
1768
  {
@@ -1357,10 +1801,11 @@ function createIdempotencyStoreContractTests(harness) {
1357
1801
  {
1358
1802
  name: "complete without a pending claim throws the wiring error",
1359
1803
  run: inEnv(async (env) => {
1360
- assertChainContainsKitError(await captureRejection(env.run((ctx) => env.store.complete(ctx, {
1804
+ const rejection = await captureRejection(env.run((ctx) => env.store.complete(ctx, {
1361
1805
  key: "key-1",
1362
1806
  token: "missing"
1363
- }, "x"))), ["IDEMPOTENCY_COMPLETED_WITHOUT_CLAIM"], "complete() without claim() must throw IdempotencyCompletionWithoutClaimError");
1807
+ }, "x")));
1808
+ assertChainContainsKitError(rejection, ["IDEMPOTENCY_COMPLETED_WITHOUT_CLAIM"], "complete() without claim() must throw IdempotencyCompletionWithoutClaimError");
1364
1809
  })
1365
1810
  }
1366
1811
  ];
@@ -1401,7 +1846,8 @@ function createIdempotencyStoreContractTests(harness) {
1401
1846
  const renewed = await env.store.renew(claim);
1402
1847
  assert(renewed !== void 0 && new Date(renewed.expiresAt).getTime() > originalExpiry, "renew must extend the lease beyond its previous expiry");
1403
1848
  await advanceTimeTo(env, new Date(originalExpiry + 1));
1404
- assertChainContainsKitError(await captureRejection(env.run((ctx) => env.store.claim(ctx, "key-renew", "fp"))), ["IDEMPOTENCY_IN_FLIGHT"], "the renewed owner must still hold the key after the original expiry");
1849
+ const rejection = await captureRejection(env.run((ctx) => env.store.claim(ctx, "key-renew", "fp")));
1850
+ assertChainContainsKitError(rejection, ["IDEMPOTENCY_IN_FLIGHT"], "the renewed owner must still hold the key after the original expiry");
1405
1851
  })
1406
1852
  }), gatedContractTest({
1407
1853
  capability: "family: non-transactional",
@@ -1412,8 +1858,10 @@ function createIdempotencyStoreContractTests(harness) {
1412
1858
  const first = claimedHandle(await env.run((ctx) => env.store.claim(ctx, "key-1", "fp-1")), "a fresh key must be claimed");
1413
1859
  assert(first.lease !== void 0, "a non-transactional claim must carry lease timing");
1414
1860
  await expireLease(env, first);
1415
- assert(claimedHandle(await env.run((ctx) => env.store.claim(ctx, "key-1", "fp-1")), "an expired pending claim must be reclaimed").token !== first.token, "each ownership generation must have a different token");
1416
- assertChainContainsKitError(await captureRejection(env.run((ctx) => env.store.complete(ctx, first, "stale"))), ["IDEMPOTENCY_CLAIM_LOST"], "a stale owner must fail before it can complete after takeover");
1861
+ const successor = claimedHandle(await env.run((ctx) => env.store.claim(ctx, "key-1", "fp-1")), "an expired pending claim must be reclaimed");
1862
+ assert(successor.token !== first.token, "each ownership generation must have a different token");
1863
+ const rejection = await captureRejection(env.run((ctx) => env.store.complete(ctx, first, "stale")));
1864
+ assertChainContainsKitError(rejection, ["IDEMPOTENCY_CLAIM_LOST"], "a stale owner must fail before it can complete after takeover");
1417
1865
  })
1418
1866
  }), gatedContractTest({
1419
1867
  capability: "family: non-transactional",
@@ -1455,7 +1903,8 @@ function createIdempotencyStoreContractTests(harness) {
1455
1903
  const rolledBack = await env.run((ctx) => env.store.claim(ctx, "rolled-back", "fp"));
1456
1904
  assert(rolledBack.status === "reconciliation-required", "the staged outcome must expose its reconciliation receipt");
1457
1905
  await env.store.reconcile(rolledBack.reconciliation, "not-committed");
1458
- assertEqual((await env.run((ctx) => env.store.claim(ctx, "rolled-back", "fp"))).status, "claimed", "not-committed evidence must release the staged outcome");
1906
+ const fresh = await env.run((ctx) => env.store.claim(ctx, "rolled-back", "fp"));
1907
+ assertEqual(fresh.status, "claimed", "not-committed evidence must release the staged outcome");
1459
1908
  })
1460
1909
  }), gatedContractTest({
1461
1910
  capability: "family: non-transactional",
@@ -1467,7 +1916,8 @@ function createIdempotencyStoreContractTests(harness) {
1467
1916
  const claim = claimedHandle(await env.store.claim(ctx, "key-1", "fp-1"), "a fresh key must be claimed");
1468
1917
  await env.store.complete(ctx, claim, "uncommitted");
1469
1918
  });
1470
- assertChainContainsKitError(await captureRejection(env.run((ctx) => env.store.claim(ctx, "key-1", "fp-1"))), ["IDEMPOTENCY_IN_FLIGHT"], "a staged outcome must never replay; it is in-flight until confirmed");
1919
+ const rejection = await captureRejection(env.run((ctx) => env.store.claim(ctx, "key-1", "fp-1")));
1920
+ assertChainContainsKitError(rejection, ["IDEMPOTENCY_IN_FLIGHT"], "a staged outcome must never replay; it is in-flight until confirmed");
1471
1921
  })
1472
1922
  }), gatedContractTest({
1473
1923
  capability: "family: non-transactional",
@@ -1477,7 +1927,8 @@ function createIdempotencyStoreContractTests(harness) {
1477
1927
  run: inEnv(async (env) => {
1478
1928
  const first = claimedHandle(await env.run((ctx) => env.store.claim(ctx, "key-1", "fp-1")), "a fresh key must be claimed");
1479
1929
  await env.store.abandon(first);
1480
- assertEqual((await env.run((ctx) => env.store.claim(ctx, "key-1", "fp-1"))).status, "claimed", "an abandoned pending claim must be claimable again");
1930
+ const claim = await env.run((ctx) => env.store.claim(ctx, "key-1", "fp-1"));
1931
+ assertEqual(claim.status, "claimed", "an abandoned pending claim must be claimable again");
1481
1932
  })
1482
1933
  }), gatedContractTest({
1483
1934
  capability: "family: non-transactional",
@@ -1491,7 +1942,8 @@ function createIdempotencyStoreContractTests(harness) {
1491
1942
  return claim;
1492
1943
  });
1493
1944
  await env.store.abandon(first);
1494
- assertEqual((await env.run((ctx) => env.store.claim(ctx, "key-1", "fp-1"))).status, "claimed", "an abandoned staged outcome must be claimable again");
1945
+ const claim = await env.run((ctx) => env.store.claim(ctx, "key-1", "fp-1"));
1946
+ assertEqual(claim.status, "claimed", "an abandoned staged outcome must be claimable again");
1495
1947
  })
1496
1948
  }), gatedContractTest({
1497
1949
  capability: "family: transactional",
@@ -1504,7 +1956,8 @@ function createIdempotencyStoreContractTests(harness) {
1504
1956
  const claim = claimedHandle(await env.store.claim(ctx, "key-1", "fp-1"), "a fresh key must be claimed");
1505
1957
  await env.store.complete(ctx, claim, "rolled back");
1506
1958
  }).catch(() => {});
1507
- assertEqual((await env.run((ctx) => env.store.claim(ctx, "key-1", "fp-1"))).status, "claimed", "a rolled-back claim/complete must leave the key claimable");
1959
+ const claim = await env.run((ctx) => env.store.claim(ctx, "key-1", "fp-1"));
1960
+ assertEqual(claim.status, "claimed", "a rolled-back claim/complete must leave the key claimable");
1508
1961
  })
1509
1962
  }));
1510
1963
  return tests;
@@ -1555,7 +2008,8 @@ function createOutboxContractTests(harness) {
1555
2008
  run: inEnv(async (env) => {
1556
2009
  await env.addCommitted(commit([harness.createEvent(1), harness.createEvent(2)], 1));
1557
2010
  await env.addCommitted(commit([harness.createEvent(3)], 3));
1558
- assert(deepEqual((await takeAndAck(env, 3)).map(({ position }) => position), [
2011
+ const records = await takeAndAck(env, 3);
2012
+ assert(deepEqual(records.map(({ position }) => position), [
1559
2013
  {
1560
2014
  aggregateVersion: 1,
1561
2015
  commitSequence: 0,
@@ -1583,7 +2037,8 @@ function createOutboxContractTests(harness) {
1583
2037
  const original = commit([harness.createEvent(1)], 1);
1584
2038
  const collision = commit([harness.createEvent(2)], 1);
1585
2039
  await env.addCommitted(original);
1586
- assert(await captureRejection(env.addCommitted(collision)) !== void 0, "a different eventId at one qualified source position must reject");
2040
+ const rejection = await captureRejection(env.addCommitted(collision));
2041
+ assert(rejection !== void 0, "a different eventId at one qualified source position must reject");
1587
2042
  const [record] = await takeAndAck(env, 1);
1588
2043
  assertEqual(record?.event.eventId, original[0]?.event.eventId, "the rejected collision must not replace the original record");
1589
2044
  await env.addCommitted(commit([harness.createEvent(3)], 2));
@@ -1609,7 +2064,8 @@ function createOutboxContractTests(harness) {
1609
2064
  }
1610
2065
  ];
1611
2066
  for (const [index, source] of sources.entries()) await env.addCommitted(commit([harness.createEvent(index + 1)], 1, source));
1612
- assert((await takeAndAck(env, sources.length)).every((record, index) => record.source.aggregateType === sources[index]?.aggregateType && record.source.aggregateId === sources[index]?.aggregateId && record.position.previousEventfulAggregateVersion === null), "colliding raw ids or aggregate types must each retain an independent genesis head");
2067
+ const records = await takeAndAck(env, sources.length);
2068
+ assert(records.every((record, index) => record.source.aggregateType === sources[index]?.aggregateType && record.source.aggregateId === sources[index]?.aggregateId && record.position.previousEventfulAggregateVersion === null), "colliding raw ids or aggregate types must each retain an independent genesis head");
1613
2069
  })
1614
2070
  },
1615
2071
  {
@@ -1668,7 +2124,8 @@ function createOutboxContractTests(harness) {
1668
2124
  await env.outbox.markDispatched([first.dispatchId]);
1669
2125
  await env.outbox.markDispatched([first.dispatchId]);
1670
2126
  await env.outbox.markDispatched(["no-such-dispatch-id"]);
1671
- assert(!(await env.outbox.getPending(10)).some((r) => r.dispatchId === first.dispatchId), "a dispatched record must never come back");
2127
+ const remaining = await env.outbox.getPending(10);
2128
+ assert(!remaining.some((r) => r.dispatchId === first.dispatchId), "a dispatched record must never come back");
1672
2129
  })
1673
2130
  },
1674
2131
  gatedContractTest({
@@ -1699,7 +2156,8 @@ function createOutboxContractTests(harness) {
1699
2156
  } catch (error) {
1700
2157
  throw new Error(`Contract violated: add() must swallow a duplicate eventId, not throw. Dedupe means an idempotent add (INSERT ... ON CONFLICT DO NOTHING or equivalent), not a raised unique violation. Got: ${describeError(error)}`);
1701
2158
  }
1702
- assertEqual((await env.outbox.getPending(10)).length, 1, "the same eventId must yield one record (unique-key dedupe)");
2159
+ const pending = await env.outbox.getPending(10);
2160
+ assertEqual(pending.length, 1, "the same eventId must yield one record (unique-key dedupe)");
1703
2161
  })
1704
2162
  })
1705
2163
  ];
@@ -1835,7 +2293,8 @@ function createProjectionCheckpointStoreContractTests(harness) {
1835
2293
  {
1836
2294
  name: "a never-seen (projection, aggregate) pair loads undefined",
1837
2295
  run: inEnv(async (env) => {
1838
- assertEqual(await env.run((ctx) => env.store.load(ctx, "order-list", order("o-1"))), void 0, "a fresh store must report no watermark");
2296
+ const loaded = await env.run((ctx) => env.store.load(ctx, "order-list", order("o-1")));
2297
+ assertEqual(loaded, void 0, "a fresh store must report no watermark");
1839
2298
  })
1840
2299
  },
1841
2300
  gatedContractTest({
@@ -1859,16 +2318,18 @@ function createProjectionCheckpointStoreContractTests(harness) {
1859
2318
  };
1860
2319
  assertEqual(await advanceOnce(void 0, 1), 1, "exactly one competing callback may advance a missing checkpoint key; genesis has no row that SELECT FOR UPDATE could lock");
1861
2320
  assertEqual(await advanceOnce(1, 2), 1, "exactly one competing callback may advance an existing checkpoint key from the observed watermark");
1862
- assert((await env.run((ctx) => env.store.load(ctx, "order-list", address)))?.position.aggregateVersion === 2, "serialized genesis and existing-row advances must leave the final watermark visible");
2321
+ const stored = await env.run((ctx) => env.store.load(ctx, "order-list", address));
2322
+ assert(stored?.position.aggregateVersion === 2, "serialized genesis and existing-row advances must leave the final watermark visible");
1863
2323
  })
1864
2324
  }),
1865
2325
  {
1866
2326
  name: "checkpoint locks release after a rejected critical section",
1867
2327
  run: inEnv(async (env) => {
1868
2328
  const address = order("o-rejected-lock");
1869
- assert(await captureRejection(env.run((ctx) => env.store.withCheckpointLocks(ctx, "order-list", [address], async () => {
2329
+ const rejection = await captureRejection(env.run((ctx) => env.store.withCheckpointLocks(ctx, "order-list", [address], async () => {
1870
2330
  throw new Error("projection failed");
1871
- }))) !== void 0, "the store must propagate a rejected critical section");
2331
+ })));
2332
+ assert(rejection !== void 0, "the store must propagate a rejected critical section");
1872
2333
  let retried = false;
1873
2334
  await env.run((ctx) => env.store.withCheckpointLocks(ctx, "order-list", [address], async () => {
1874
2335
  retried = true;
@@ -1902,7 +2363,8 @@ function createProjectionCheckpointStoreContractTests(harness) {
1902
2363
  const loaded = await env.run((ctx) => env.store.load(ctx, "order-list", order("o-1")));
1903
2364
  assert(loaded !== void 0, "expected a stored watermark");
1904
2365
  loaded.position.aggregateVersion = 99;
1905
- assert((await env.run((ctx) => env.store.load(ctx, "order-list", order("o-1"))))?.position.aggregateVersion === 2, "the stored watermark must be immune to mutation of a previously loaded copy");
2366
+ const reloaded = await env.run((ctx) => env.store.load(ctx, "order-list", order("o-1")));
2367
+ assert(reloaded?.position.aggregateVersion === 2, "the stored watermark must be immune to mutation of a previously loaded copy");
1906
2368
  })
1907
2369
  },
1908
2370
  {
@@ -1996,7 +2458,8 @@ function createProjectionCheckpointStoreContractTests(harness) {
1996
2458
  run: inEnv(async (env) => {
1997
2459
  if (!env.runRolledBack) throw new Error("Contract violated: harness declared providesRolledBackRuns but the environment lacks runRolledBack");
1998
2460
  await env.runRolledBack((ctx) => env.store.save(ctx, "order-list", order("o-1"), checkpoint(pos(1, 0)))).catch(() => {});
1999
- assertEqual(await env.run((ctx) => env.store.load(ctx, "order-list", order("o-1"))), void 0, "a checkpoint from a rolled-back transaction must not exist; otherwise events are lost while marked processed");
2461
+ const loaded = await env.run((ctx) => env.store.load(ctx, "order-list", order("o-1")));
2462
+ assertEqual(loaded, void 0, "a checkpoint from a rolled-back transaction must not exist; otherwise events are lost while marked processed");
2000
2463
  })
2001
2464
  })
2002
2465
  ];
@@ -2037,7 +2500,7 @@ function createRepositoryContractTests(harness) {
2037
2500
  }
2038
2501
  const reload = (environment, id) => environment.run(({ repository }) => load(repository, id));
2039
2502
  const eventIds = (events) => sortedCommittedEventIds(events);
2040
- const pendingEventIds = (events) => mintedPendingEventIds(events, "the harness must record pending events before persistence");
2503
+ const pendingEventIds = (events) => recordedPendingEventIds(events, "the harness must record pending events before persistence");
2041
2504
  const tests = [
2042
2505
  {
2043
2506
  name: "add flushes a new aggregate and its exact event batch atomically",
@@ -2122,7 +2585,8 @@ function createRepositoryContractTests(harness) {
2122
2585
  repository.add(aggregate);
2123
2586
  throw new Error("rollback probe");
2124
2587
  }));
2125
- assert(await environment.run(({ repository }) => repository.findById(aggregate.id)) === void 0, "a rolled-back add must leave no row");
2588
+ const absent = await environment.run(({ repository }) => repository.findById(aggregate.id));
2589
+ assert(absent === void 0, "a rolled-back add must leave no row");
2126
2590
  assertEqual((await environment.committedOutboxEvents()).length, 0, "a rolled-back add must leave no outbox record");
2127
2591
  assert(deepEqual(aggregate.pendingEvents, pending), "rollback must acknowledge none of the registered event batch");
2128
2592
  })
@@ -2134,10 +2598,12 @@ function createRepositoryContractTests(harness) {
2134
2598
  harness.mutate(aggregate);
2135
2599
  const pending = [...aggregate.pendingEvents];
2136
2600
  environment.failNextOutboxWrite(/* @__PURE__ */ new Error("outbox failure probe"));
2137
- assert(await captureRejection(environment.run(async ({ repository }) => {
2601
+ const rejection = await captureRejection(environment.run(async ({ repository }) => {
2138
2602
  repository.add(aggregate);
2139
- })) !== void 0, "the outbox failure must reject");
2140
- assert(await environment.run(({ repository }) => repository.findById(aggregate.id)) === void 0, "state flush must roll back when the outbox write fails");
2603
+ }));
2604
+ assert(rejection !== void 0, "the outbox failure must reject");
2605
+ const absent = await environment.run(({ repository }) => repository.findById(aggregate.id));
2606
+ assert(absent === void 0, "state flush must roll back when the outbox write fails");
2141
2607
  assertEqual((await environment.committedOutboxEvents()).length, 0, "failed outbox write must commit no envelope");
2142
2608
  assert(deepEqual(aggregate.pendingEvents, pending), "failed commit must acknowledge none of the event batch");
2143
2609
  })
@@ -2200,7 +2666,8 @@ function createRepositoryContractTests(harness) {
2200
2666
  mutateVersionOnly.call(harness, aggregate);
2201
2667
  repository.update(aggregate);
2202
2668
  });
2203
- assertEqual((await reload(environment, seeded.id)).version, seeded.version + 1, "a version-only change (empty change set, bumped version) must still be persisted; skipping it desyncs the persisted version and produces false concurrency conflicts later");
2669
+ const reloaded = await reload(environment, seeded.id);
2670
+ assertEqual(reloaded.version, seeded.version + 1, "a version-only change (empty change set, bumped version) must still be persisted; skipping it desyncs the persisted version and produces false concurrency conflicts later");
2204
2671
  assert(deepEqual(eventIds(await environment.committedOutboxEvents()), eventIds(outboxBefore)), "state-only update must not create an outbox event");
2205
2672
  })
2206
2673
  }));
@@ -2217,7 +2684,8 @@ function createRepositoryContractTests(harness) {
2217
2684
  mutateChildCollection.call(harness, aggregate);
2218
2685
  repository.update(aggregate);
2219
2686
  });
2220
- assertEqual((await reload(environment, seeded.id)).version, seeded.version + 1, "nested collection update must advance the persisted root version");
2687
+ const reloaded = await reload(environment, seeded.id);
2688
+ assertEqual(reloaded.version, seeded.version + 1, "nested collection update must advance the persisted root version");
2221
2689
  })
2222
2690
  }));
2223
2691
  tests.push(gatedContractTest({
@@ -2336,7 +2804,8 @@ function createSnapshotStoreContractTests(harness) {
2336
2804
  total: 0,
2337
2805
  items: []
2338
2806
  }));
2339
- assertEqual((await env.store.load(address("Order", "o-1")))?.schemaVersion, void 0, "a snapshot stored without schemaVersion must not come back with a fabricated one; restore treats absence as schema 1");
2807
+ const loaded = await env.store.load(address("Order", "o-1"));
2808
+ assertEqual(loaded?.schemaVersion, void 0, "a snapshot stored without schemaVersion must not come back with a fabricated one; restore treats absence as schema 1");
2340
2809
  })
2341
2810
  },
2342
2811
  {
@@ -2413,12 +2882,13 @@ function createSnapshotStoreContractTests(harness) {
2413
2882
  assert(loaded !== void 0, "expected the saved snapshot");
2414
2883
  assertEqual(loaded.state.items.length, 1, "save must capture the snapshot by value, not hold the caller's reference");
2415
2884
  loaded.state.total = 999;
2416
- assertEqual((await env.store.load(address("Order", "o-1")))?.state.total, 5, "load must hand out a detached copy, never live internal state");
2885
+ const reloaded = await env.store.load(address("Order", "o-1"));
2886
+ assertEqual(reloaded?.state.total, 5, "load must hand out a detached copy, never live internal state");
2417
2887
  })
2418
2888
  }
2419
2889
  ];
2420
2890
  }
2421
2891
 
2422
2892
  //#endregion
2423
- export { createCommandOutboxContractTests, createDeadlineStoreContractTests, createEsRepositoryContractTests, createEventStoreContractTests, createIdempotencyStoreContractTests, createOutboxContractTests, createProjectionCheckpointStoreContractTests, createRepositoryContractTests, createSnapshotStoreContractTests };
2893
+ export { createCommandOutboxContractTests, createDeadlineStoreContractTests, createEsRepositoryContractTests, createEventBusContractTests, createEventStoreContractTests, createIdempotencyStoreContractTests, createOutboxContractTests, createProjectionCheckpointStoreContractTests, createRepositoryContractTests, createSnapshotStoreContractTests };
2424
2894
  //# sourceMappingURL=testing.js.map