@spine-event-engine/server 2.0.0-snapshot.15 → 2.0.0-snapshot.16

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.
@@ -116,7 +116,7 @@ export class BoundedContext {
116
116
  constructBoundedContext = (snapshot, commandBus, eventBus, systemEventBus, publisher, stand, systemStand, runtime, systemSpec, storageFactory, repositories, deliveryStrategy, token) => new BoundedContext(snapshot, commandBus, eventBus, systemEventBus, publisher, stand, systemStand, runtime, systemSpec, storageFactory, repositories, deliveryStrategy, token);
117
117
  }
118
118
  /**
119
- * Creates a framework-owned bounded context.
119
+ * Creates a Bounded Context from its prepared framework services.
120
120
  *
121
121
  * @param snapshot Contains the immutable context metadata.
122
122
  * @param commandBus Dispatches commands accepted by this context.
@@ -125,7 +125,7 @@ export class BoundedContext {
125
125
  * @param publisher Publishes signals produced by context handlers.
126
126
  * @param stand Stores read-side state for this context.
127
127
  * @param systemStand Stores read-side state for the paired System Context.
128
- * @param subscriptionRuntime Coordinates pair-owned subscription delivery.
128
+ * @param subscriptionRuntime Coordinates subscriptions for the paired contexts.
129
129
  * @param systemSpec Contains paired System Context metadata.
130
130
  * @param storageFactory Creates context storage.
131
131
  * @param repositories Lists repositories to register.
@@ -303,7 +303,7 @@ export class BoundedContext {
303
303
  return ContextParts.cloneContextSnapshot(this.#snapshot);
304
304
  }
305
305
  /**
306
- * Returns the command endpoint owned by this context.
306
+ * Returns this context's command endpoint.
307
307
  *
308
308
  * @returns Returns the context command endpoint.
309
309
  */
@@ -311,7 +311,7 @@ export class BoundedContext {
311
311
  return this.#commandEndpoint;
312
312
  }
313
313
  /**
314
- * Returns the event endpoint owned by this context.
314
+ * Returns this context's event endpoint.
315
315
  *
316
316
  * @returns Returns the context event endpoint.
317
317
  */
@@ -319,7 +319,7 @@ export class BoundedContext {
319
319
  return this.#eventEndpoint;
320
320
  }
321
321
  /**
322
- * Returns the context-owned read-side Stand.
322
+ * Returns the Stand that stores this context's read-side state.
323
323
  *
324
324
  * @returns Returns the read-side state store.
325
325
  */
@@ -391,14 +391,14 @@ export class BoundedContext {
391
391
  });
392
392
  }
393
393
  /**
394
- * Closes context-owned buses, Stand, and repository storage/runtime bindings.
394
+ * Closes this context's buses, Stand, and repository storage and runtime bindings.
395
395
  *
396
396
  * Close is idempotent and returns the same close outcome on repeated calls.
397
- * The context attempts every owned close hook; when any hook fails, the
397
+ * The context attempts every close hook; when any hook fails, the
398
398
  * returned promise rejects with an `AggregateError` after the remaining hooks
399
399
  * have also been attempted.
400
400
  *
401
- * @returns A promise that settles after all owned resources close.
401
+ * @returns A promise that settles after all resources close.
402
402
  */
403
403
  close() {
404
404
  closingContexts.add(this);
@@ -570,7 +570,7 @@ export class BoundedContextBuilder {
570
570
  constructBoundedContextBuilder = (snapshot, token) => new BoundedContextBuilder(snapshot, token);
571
571
  }
572
572
  /**
573
- * Creates a framework-owned context builder.
573
+ * Creates a builder from a prepared context specification.
574
574
  *
575
575
  * @param specSnapshot Contains the initial context specification.
576
576
  * @param token Proves framework-controlled construction.
@@ -616,6 +616,7 @@ export class BoundedContextBuilder {
616
616
  /**
617
617
  * Adds an explicitly assembled repository or an Entity class.
618
618
  *
619
+ * @typeParam EntityType Concrete Entity class represented by this entry.
619
620
  * @param entry The repository or Entity class to register.
620
621
  * @param options Optional settings used only when an Entity class is supplied.
621
622
  * @returns This builder for further configuration.
@@ -638,6 +639,7 @@ export class BoundedContextBuilder {
638
639
  /**
639
640
  * Removes a repository from the context registration list.
640
641
  *
642
+ * @typeParam EntityType Concrete Entity class represented by the repository.
641
643
  * @param repository Identifies the repository to remove.
642
644
  * @returns Returns this builder for further configuration.
643
645
  */
@@ -748,7 +750,7 @@ export class BoundedContextBuilder {
748
750
  return this;
749
751
  }
750
752
  /**
751
- * Sets the target-to-shard strategy for the context-owned Entity Inbox.
753
+ * Sets how Entity Inbox targets are assigned to shards.
752
754
  *
753
755
  * @param strategy Selects the durable shard for Aggregate and Process Manager targets.
754
756
  * @returns Returns this builder for further configuration.
@@ -972,6 +974,12 @@ export class BoundedContextBuilder {
972
974
  #standaloneInstances() {
973
975
  return Object.freeze([...this.#assignees, ...this.#commanders, ...this.#eventReceivers]);
974
976
  }
977
+ /**
978
+ * Creates the domain EventStore using this context's storage mode.
979
+ *
980
+ * @param storageFactory Storage provider for event records.
981
+ * @returns Domain EventStore.
982
+ */
975
983
  createEventStore(storageFactory) {
976
984
  return new EventStore(ContextParts.createStorageMode(this.#specSnapshot), storageFactory);
977
985
  }
@@ -988,7 +996,7 @@ export class ContextSpec {
988
996
  constructContextSpec = (snapshot, token) => new ContextSpec(snapshot, token);
989
997
  }
990
998
  /**
991
- * Creates a framework-owned context specification.
999
+ * Creates a context specification from prepared values.
992
1000
  *
993
1001
  * @param snapshot Contains immutable specification values.
994
1002
  * @param token Proves framework-controlled construction.
@@ -1040,10 +1048,19 @@ export class ContextSpec {
1040
1048
  }
1041
1049
  }
1042
1050
  const registeredRepositories = new WeakMap();
1051
+ /**
1052
+ * Reports failure while redispatching a stored event during read-side replay.
1053
+ */
1043
1054
  class CatchUpReplayError extends Error {
1044
1055
  code = "READ_SIDE_CATCH_UP_REPLAY_FAILED";
1045
1056
  eventId;
1046
1057
  detail;
1058
+ /**
1059
+ * Creates a replay error tied to the stored event that failed.
1060
+ *
1061
+ * @param eventId Identifies the stored event.
1062
+ * @param detail Contains the bounded cause name and message.
1063
+ */
1047
1064
  constructor(eventId, detail) {
1048
1065
  super(`Read-side catch-up failed for stored event "${eventId}".`);
1049
1066
  this.name = "ReadCatchUpReplayError";
@@ -1056,6 +1073,11 @@ class CatchUpReplayError extends Error {
1056
1073
  * Assembles private bounded-context lifecycle and replay details.
1057
1074
  */
1058
1075
  const ContextParts = Object.freeze({
1076
+ /**
1077
+ * Rejects duplicate command receptors in generated standalone groups.
1078
+ *
1079
+ * @param receivers Generated groups to validate.
1080
+ */
1059
1081
  assertUniqueCommandReceptors(receivers) {
1060
1082
  const receptorByType = new Map();
1061
1083
  for (const receiver of receivers) {
@@ -1070,6 +1092,14 @@ const ContextParts = Object.freeze({
1070
1092
  }
1071
1093
  }
1072
1094
  },
1095
+ /**
1096
+ * Matches generated standalone groups to their registered instances.
1097
+ *
1098
+ * @param generated Generated handler groups.
1099
+ * @param instances Registered standalone instances.
1100
+ * @param publisher Publisher used by bindings.
1101
+ * @returns Frozen bindings in generated order.
1102
+ */
1073
1103
  matchStandaloneHandlers(generated, instances, publisher) {
1074
1104
  const byConstructor = new Map();
1075
1105
  for (const instance of instances) {
@@ -1094,6 +1124,12 @@ const ContextParts = Object.freeze({
1094
1124
  }
1095
1125
  return Object.freeze(bindings);
1096
1126
  },
1127
+ /**
1128
+ * Calls cleanup and records any thrown error.
1129
+ *
1130
+ * @param onCleanup Cleanup action.
1131
+ * @param errors Collection receiving failures.
1132
+ */
1097
1133
  attemptCleanup(onCleanup, errors) {
1098
1134
  try {
1099
1135
  onCleanup();
@@ -1102,6 +1138,12 @@ const ContextParts = Object.freeze({
1102
1138
  errors.push(error);
1103
1139
  }
1104
1140
  },
1141
+ /**
1142
+ * Closes buses and stores after context assembly fails.
1143
+ *
1144
+ * @param resources Partially created resources.
1145
+ * @param errors Collection receiving failures.
1146
+ */
1105
1147
  cleanupBuildBuses(resources, errors) {
1106
1148
  ContextParts.attemptCleanup(() => {
1107
1149
  if (resources.commandBus !== undefined)
@@ -1120,20 +1162,61 @@ const ContextParts = Object.freeze({
1120
1162
  resources.eventStore?.close();
1121
1163
  }, errors);
1122
1164
  },
1165
+ /**
1166
+ * Returns constituent failures from nested aggregate errors.
1167
+ *
1168
+ * @param errors Errors to expand.
1169
+ * @returns Flattened error list.
1170
+ */
1123
1171
  flattenErrors(errors) {
1124
1172
  return errors.flatMap((error) => error instanceof AggregateError ? ContextParts.flattenErrors(error.errors) : [error]);
1125
1173
  },
1174
+ /**
1175
+ * Rejects construction without the framework token.
1176
+ *
1177
+ * @param token Supplied token.
1178
+ * @param message Error message for an invalid token.
1179
+ */
1126
1180
  requireFrameworkConstructionToken(token, message) {
1127
1181
  if (token !== frameworkConstructionToken) {
1128
1182
  throw new TypeError(message);
1129
1183
  }
1130
1184
  },
1185
+ /**
1186
+ * Creates a context specification from its snapshot.
1187
+ *
1188
+ * @param specSnapshot Snapshot used for construction.
1189
+ * @returns Constructed context specification.
1190
+ */
1131
1191
  createContextSpec(specSnapshot) {
1132
1192
  return constructContextSpec(specSnapshot, frameworkConstructionToken);
1133
1193
  },
1194
+ /**
1195
+ * Creates a builder for a context snapshot.
1196
+ *
1197
+ * @param specSnapshot Context configuration snapshot.
1198
+ * @returns Constructed context builder.
1199
+ */
1134
1200
  createBoundedContextBuilder(specSnapshot) {
1135
1201
  return constructBoundedContextBuilder(specSnapshot, frameworkConstructionToken);
1136
1202
  },
1203
+ /**
1204
+ * Creates a bounded context from assembled runtime components.
1205
+ *
1206
+ * @param specSnapshot Context configuration.
1207
+ * @param commandBus Command dispatch bus.
1208
+ * @param eventBus Domain event bus.
1209
+ * @param systemEventBus System event bus.
1210
+ * @param publisher Signal publisher.
1211
+ * @param stand Domain read-side Stand.
1212
+ * @param systemStand System read-side Stand.
1213
+ * @param runtime Subscription runtime.
1214
+ * @param systemSpec Paired System specification.
1215
+ * @param storageFactory Storage provider.
1216
+ * @param repositories Registered repository views.
1217
+ * @param deliveryStrategy Delivery sharding strategy.
1218
+ * @returns Constructed bounded context.
1219
+ */
1137
1220
  createBoundedContext(specSnapshot, commandBus, eventBus, systemEventBus, publisher, stand, systemStand, runtime, systemSpec, storageFactory, repositories, deliveryStrategy) {
1138
1221
  return constructBoundedContext({
1139
1222
  name: specSnapshot.name,
@@ -1141,18 +1224,36 @@ const ContextParts = Object.freeze({
1141
1224
  spec: specSnapshot,
1142
1225
  }, commandBus, eventBus, systemEventBus, publisher, stand, systemStand, runtime, systemSpec, storageFactory, repositories, deliveryStrategy, frameworkConstructionToken);
1143
1226
  },
1227
+ /**
1228
+ * Creates the storage mode represented by a context snapshot.
1229
+ *
1230
+ * @param specSnapshot Context configuration snapshot.
1231
+ * @returns Storage mode for the context.
1232
+ */
1144
1233
  createStorageMode(specSnapshot) {
1145
1234
  return Object.freeze({
1146
1235
  name: specSnapshot.name.value,
1147
1236
  multitenant: specSnapshot.multitenant,
1148
1237
  });
1149
1238
  },
1239
+ /**
1240
+ * Creates a non-tenant-scoped subscription storage context.
1241
+ *
1242
+ * @param specSnapshot Context configuration snapshot.
1243
+ * @returns Subscription storage context.
1244
+ */
1150
1245
  createSubscriptionStorageContext(specSnapshot) {
1151
1246
  return Object.freeze({
1152
1247
  name: `${specSnapshot.name.value}:subscriptions`,
1153
1248
  multitenant: false,
1154
1249
  });
1155
1250
  },
1251
+ /**
1252
+ * Captures the shard count and validates later shard results.
1253
+ *
1254
+ * @param strategy Configured delivery strategy.
1255
+ * @returns Immutable delivery strategy snapshot.
1256
+ */
1156
1257
  snapshotDeliveryStrategy(strategy) {
1157
1258
  if (!Number.isSafeInteger(strategy.shardCount) || strategy.shardCount <= 0) {
1158
1259
  throw new Error("Delivery strategy shard count must be a positive safe integer.");
@@ -1160,6 +1261,13 @@ const ContextParts = Object.freeze({
1160
1261
  const shardCount = strategy.shardCount;
1161
1262
  return Object.freeze({
1162
1263
  shardCount,
1264
+ /**
1265
+ * Returns a target shard after verifying the captured shard total.
1266
+ *
1267
+ * @param targetId Inbox target identifier.
1268
+ * @param targetType Inbox target type.
1269
+ * @returns Validated shard index.
1270
+ */
1163
1271
  shardFor(targetId, targetType) {
1164
1272
  const shard = strategy.shardFor(InboxTargets.clone(targetId), targetType);
1165
1273
  if (shard.ofTotal !== shardCount) {
@@ -1169,6 +1277,13 @@ const ContextParts = Object.freeze({
1169
1277
  },
1170
1278
  });
1171
1279
  },
1280
+ /**
1281
+ * Creates the storage context for read-side catch-up.
1282
+ *
1283
+ * @param specSnapshot Context configuration.
1284
+ * @param options Catch-up tenant options.
1285
+ * @returns Storage context for the selected scope.
1286
+ */
1172
1287
  catchUpStorageContext(specSnapshot, options) {
1173
1288
  if (!specSnapshot.multitenant) {
1174
1289
  if (options.tenantId !== undefined) {
@@ -1186,6 +1301,12 @@ const ContextParts = Object.freeze({
1186
1301
  tenantId: TenantBoundary.from(tenantId).tenantId,
1187
1302
  });
1188
1303
  },
1304
+ /**
1305
+ * Creates a bounded-context name after checking reserved names.
1306
+ *
1307
+ * @param value Candidate context name.
1308
+ * @returns Immutable validated name.
1309
+ */
1189
1310
  createBoundedContextName(value) {
1190
1311
  if (typeof value !== "string" ||
1191
1312
  value.trim().length === 0 ||
@@ -1194,6 +1315,13 @@ const ContextParts = Object.freeze({
1194
1315
  }
1195
1316
  return Object.freeze({ value });
1196
1317
  },
1318
+ /**
1319
+ * Creates a context snapshot with event storage enabled.
1320
+ *
1321
+ * @param name Context name.
1322
+ * @param multitenant Whether tenant isolation is enabled.
1323
+ * @returns Immutable specification snapshot.
1324
+ */
1197
1325
  createSpecSnapshot(name, multitenant) {
1198
1326
  return Object.freeze({
1199
1327
  name: ContextParts.createBoundedContextName(name),
@@ -1201,9 +1329,21 @@ const ContextParts = Object.freeze({
1201
1329
  storesEvents: true,
1202
1330
  });
1203
1331
  },
1332
+ /**
1333
+ * Copies a bounded-context name into an immutable value.
1334
+ *
1335
+ * @param name Source context name.
1336
+ * @returns Immutable name copy.
1337
+ */
1204
1338
  cloneName(name) {
1205
1339
  return ContextParts.createBoundedContextName(name.value);
1206
1340
  },
1341
+ /**
1342
+ * Copies a context specification snapshot and its name.
1343
+ *
1344
+ * @param spec Source specification.
1345
+ * @returns Immutable specification copy.
1346
+ */
1207
1347
  cloneSpecSnapshot(spec) {
1208
1348
  return Object.freeze({
1209
1349
  name: ContextParts.cloneName(spec.name),
@@ -1211,6 +1351,12 @@ const ContextParts = Object.freeze({
1211
1351
  storesEvents: spec.storesEvents,
1212
1352
  });
1213
1353
  },
1354
+ /**
1355
+ * Copies a bounded-context snapshot and its specification.
1356
+ *
1357
+ * @param snapshot Source context snapshot.
1358
+ * @returns Immutable context snapshot copy.
1359
+ */
1214
1360
  cloneContextSnapshot(snapshot) {
1215
1361
  return Object.freeze({
1216
1362
  name: ContextParts.cloneName(snapshot.name),
@@ -1218,6 +1364,13 @@ const ContextParts = Object.freeze({
1218
1364
  spec: ContextParts.cloneSpecSnapshot(snapshot.spec),
1219
1365
  });
1220
1366
  },
1367
+ /**
1368
+ * Creates the System specification paired with a domain context.
1369
+ *
1370
+ * @param domainSpec Domain specification.
1371
+ * @param storesEvents Whether System events are stored.
1372
+ * @returns System context snapshot.
1373
+ */
1221
1374
  createSystemSpec(domainSpec, storesEvents) {
1222
1375
  return Object.freeze({
1223
1376
  name: ContextParts.createBoundedContextName(`${domainSpec.name.value}_System`),
@@ -1225,24 +1378,49 @@ const ContextParts = Object.freeze({
1225
1378
  storesEvents,
1226
1379
  });
1227
1380
  },
1381
+ /**
1382
+ * Creates a pairing snapshot for domain and System specifications.
1383
+ *
1384
+ * @param snapshot Domain snapshot.
1385
+ * @param systemSpec Paired System specification.
1386
+ * @returns Immutable context pairing.
1387
+ */
1228
1388
  createSystemPairing(snapshot, systemSpec) {
1229
1389
  return Object.freeze({
1230
1390
  domain: ContextParts.cloneContextSnapshot(snapshot),
1231
1391
  system: ContextParts.cloneSpecSnapshot(systemSpec),
1232
1392
  });
1233
1393
  },
1394
+ /**
1395
+ * Copies both specifications in a System pairing.
1396
+ *
1397
+ * @param pairing Pairing to copy.
1398
+ * @returns Immutable pairing copy.
1399
+ */
1234
1400
  cloneSystemPairing(pairing) {
1235
1401
  return Object.freeze({
1236
1402
  domain: ContextParts.cloneContextSnapshot(pairing.domain),
1237
1403
  system: ContextParts.cloneSpecSnapshot(pairing.system),
1238
1404
  });
1239
1405
  },
1406
+ /**
1407
+ * Lists non-internal event type URLs exposed by an event bus.
1408
+ *
1409
+ * @param eventBus Bus whose schemas are inspected.
1410
+ * @returns Frozen exposed type URLs.
1411
+ */
1240
1412
  exposedEventTypeUrls(eventBus) {
1241
1413
  return Object.freeze(eventBusAccess
1242
1414
  .eventSchemas(eventBus)
1243
1415
  .filter((schema) => !ContextParts.isInternalEventSchema(schema))
1244
1416
  .map((schema) => TypeUrls.derive(schema)));
1245
1417
  },
1418
+ /**
1419
+ * Collects external event schemas by type URL.
1420
+ *
1421
+ * @param dispatchers Dispatchers to inspect.
1422
+ * @returns Unique frozen schemas.
1423
+ */
1246
1424
  externalEventSchemas(dispatchers) {
1247
1425
  const schemas = new Map();
1248
1426
  for (const dispatcher of dispatchers) {
@@ -1252,6 +1430,14 @@ const ContextParts = Object.freeze({
1252
1430
  }
1253
1431
  return Object.freeze([...schemas.values()]);
1254
1432
  },
1433
+ /**
1434
+ * Opens the integration broker and associates context readiness.
1435
+ *
1436
+ * @param context Context receiving integration.
1437
+ * @param eventBus Event bus supplied to broker.
1438
+ * @param systemSpec Paired System specification.
1439
+ * @param externalEventSchemas Schemas accepted from outside.
1440
+ */
1255
1441
  attachIntegration(context, eventBus, systemSpec, externalEventSchemas) {
1256
1442
  const broker = new IntegrationBroker({
1257
1443
  contextName: create(BoundedContextNameSchema, { value: context.name.value }),
@@ -1274,6 +1460,13 @@ const ContextParts = Object.freeze({
1274
1460
  void ready.catch(() => undefined);
1275
1461
  contextIntegrations.set(context, { broker, ready });
1276
1462
  },
1463
+ /**
1464
+ * Posts an event after integration readiness and tenant validation.
1465
+ *
1466
+ * @param context Target context.
1467
+ * @param event Event to post.
1468
+ * @returns Promise fulfilled after posting.
1469
+ */
1277
1470
  postContextEvent(context, event) {
1278
1471
  const buses = contextEventBuses.get(context);
1279
1472
  if (buses === undefined)
@@ -1285,15 +1478,34 @@ const ContextParts = Object.freeze({
1285
1478
  return buses[0].post(event);
1286
1479
  });
1287
1480
  },
1481
+ /**
1482
+ * Closes the context integration broker.
1483
+ *
1484
+ * @param context Context whose broker is closed.
1485
+ * @returns Promise fulfilled after closing.
1486
+ */
1288
1487
  closeIntegration(context) {
1289
1488
  return contextIntegrations.get(context)?.broker.close() ?? Promise.resolve();
1290
1489
  },
1490
+ /**
1491
+ * Publishes an imported event through the integration broker.
1492
+ *
1493
+ * @param context Context integration.
1494
+ * @param event Imported event.
1495
+ * @returns Promise fulfilled after publishing.
1496
+ */
1291
1497
  publishImported(context, event) {
1292
1498
  const integration = contextIntegrations.get(context);
1293
1499
  if (integration === undefined)
1294
1500
  return Promise.reject(new Error("Context integration is unavailable."));
1295
1501
  return integration.ready.then(() => integration.broker.publishImported(event));
1296
1502
  },
1503
+ /**
1504
+ * Checks imported event tenant against the context tenant mode.
1505
+ *
1506
+ * @param context Target context.
1507
+ * @param event Imported event to validate.
1508
+ */
1297
1509
  validateImportedTenant(context, event) {
1298
1510
  const tenantId = ContextParts.readReplayTenant(event);
1299
1511
  if (!context.isMultitenant) {
@@ -1307,17 +1519,41 @@ const ContextParts = Object.freeze({
1307
1519
  }
1308
1520
  TenantBoundary.from(tenantId);
1309
1521
  },
1522
+ /**
1523
+ * Waits for the integration broker to become ready.
1524
+ *
1525
+ * @param context Context whose readiness is observed.
1526
+ * @returns Readiness promise.
1527
+ */
1310
1528
  integrationReady(context) {
1311
1529
  return contextIntegrations.get(context)?.ready ?? Promise.resolve();
1312
1530
  },
1531
+ /**
1532
+ * Checks whether a schema carries an internal or SPI marker.
1533
+ *
1534
+ * @param schema Event schema to inspect.
1535
+ * @returns True when an internal marker is set.
1536
+ */
1313
1537
  isInternalEventSchema(schema) {
1314
1538
  return ((hasOption(schema, internal_type) && getOption(schema, internal_type)) ||
1315
1539
  (hasOption(schema, SPI_type) && getOption(schema, SPI_type)) ||
1316
1540
  (hasOption(schema.file, internal_all) && getOption(schema.file, internal_all)));
1317
1541
  },
1542
+ /**
1543
+ * Converts the multitenant flag to a tenant mode.
1544
+ *
1545
+ * @param multitenant Context isolation flag.
1546
+ * @returns Corresponding tenant mode.
1547
+ */
1318
1548
  toTenantMode(multitenant) {
1319
1549
  return multitenant ? "multitenant" : "single-tenant";
1320
1550
  },
1551
+ /**
1552
+ * Reads the System pairing recorded for a built context.
1553
+ *
1554
+ * @param context Context to inspect.
1555
+ * @returns Pairing snapshot; throws if unpaired.
1556
+ */
1321
1557
  requireSystemPairing(context) {
1322
1558
  const pairing = contextSystemPairings.get(context);
1323
1559
  if (pairing === undefined) {
@@ -1325,6 +1561,12 @@ const ContextParts = Object.freeze({
1325
1561
  }
1326
1562
  return pairing;
1327
1563
  },
1564
+ /**
1565
+ * Reads the tenant index recorded for a built context.
1566
+ *
1567
+ * @param context Context to inspect.
1568
+ * @returns Tenant index; throws if unavailable.
1569
+ */
1328
1570
  requireTenantIndex(context) {
1329
1571
  const tenantIndex = contextTenantIndexes.get(context);
1330
1572
  if (tenantIndex === undefined) {
@@ -1332,16 +1574,33 @@ const ContextParts = Object.freeze({
1332
1574
  }
1333
1575
  return tenantIndex;
1334
1576
  },
1577
+ /**
1578
+ * Rejects values that are not Repository instances.
1579
+ *
1580
+ * @param repository Candidate value.
1581
+ * @param operation Operation name included in the error.
1582
+ */
1335
1583
  requireRepositoryInstance(repository, operation) {
1336
1584
  if (!repositoryAccess.hasInstance(repository)) {
1337
1585
  throw new TypeError(`${operation} requires a Repository instance.`);
1338
1586
  }
1339
1587
  },
1588
+ /**
1589
+ * Rejects values that are not Entity classes.
1590
+ *
1591
+ * @param entityType Candidate Entity constructor.
1592
+ * @param operation Operation name included in the error.
1593
+ */
1340
1594
  requireEntityClass(entityType, operation) {
1341
1595
  if (typeof entityType !== "function") {
1342
1596
  throw new TypeError(`${operation} requires a Repository instance. Use an entity class only with buildAsync().`);
1343
1597
  }
1344
1598
  },
1599
+ /**
1600
+ * Rejects synchronous assembly when classes need generated metadata.
1601
+ *
1602
+ * @param entityTypes Entity classes to assemble.
1603
+ */
1345
1604
  rejectSyncEntityAssembly(entityTypes) {
1346
1605
  if (entityTypes.size === 0) {
1347
1606
  return;
@@ -1349,6 +1608,12 @@ const ContextParts = Object.freeze({
1349
1608
  throw new Error("BoundedContextBuilder.build() cannot assemble entity classes from generated metadata. " +
1350
1609
  "Use buildAsync().");
1351
1610
  },
1611
+ /**
1612
+ * Validates that a root exists when generated metadata must be loaded.
1613
+ *
1614
+ * @param root Configured registry location.
1615
+ * @returns Configured root; throws when absent.
1616
+ */
1352
1617
  requireGeneratedRegistryRoot(root) {
1353
1618
  if (root !== undefined) {
1354
1619
  return root;
@@ -1356,6 +1621,12 @@ const ContextParts = Object.freeze({
1356
1621
  throw new Error("BoundedContextBuilder.buildAsync() requires withGeneratedRegistryRoot(root) " +
1357
1622
  "when assembling entity classes from generated metadata.");
1358
1623
  },
1624
+ /**
1625
+ * Resolves the generated registry module and verifies it stays beneath the configured root.
1626
+ *
1627
+ * @param root Configured filesystem path or file URL.
1628
+ * @returns Registry module file URL.
1629
+ */
1359
1630
  async trustedGeneratedRegistryModule(root) {
1360
1631
  const trustedRoot = await ContextParts.canonicalGeneratedRegistryRoot(root);
1361
1632
  const registryPath = resolve(trustedRoot, generatedRegistryFile);
@@ -1366,6 +1637,12 @@ const ContextParts = Object.freeze({
1366
1637
  }
1367
1638
  return pathToFileURL(canonicalRegistryPath);
1368
1639
  },
1640
+ /**
1641
+ * Resolves a generated registry root to its canonical filesystem path.
1642
+ *
1643
+ * @param root Configured path or file URL.
1644
+ * @returns Canonical directory path.
1645
+ */
1369
1646
  async canonicalGeneratedRegistryRoot(root) {
1370
1647
  const rootPath = ContextParts.generatedRegistryRootPath(root);
1371
1648
  try {
@@ -1375,6 +1652,12 @@ const ContextParts = Object.freeze({
1375
1652
  throw new Error(`Generated registry root "${rootPath}" must be an existing readable directory.`, { cause: error });
1376
1653
  }
1377
1654
  },
1655
+ /**
1656
+ * Converts a registry root path or file URL to an absolute filesystem path.
1657
+ *
1658
+ * @param root Configured path or file URL.
1659
+ * @returns Absolute filesystem path.
1660
+ */
1378
1661
  generatedRegistryRootPath(root) {
1379
1662
  if (root instanceof URL) {
1380
1663
  return ContextParts.fileUrlPath(root, "Generated registry root");
@@ -1384,6 +1667,13 @@ const ContextParts = Object.freeze({
1384
1667
  }
1385
1668
  return resolve(root);
1386
1669
  },
1670
+ /**
1671
+ * Validates a file URL and converts it to an absolute filesystem path.
1672
+ *
1673
+ * @param url File URL to convert.
1674
+ * @param label Label used in validation errors.
1675
+ * @returns Filesystem path.
1676
+ */
1387
1677
  fileUrlPath(url, label) {
1388
1678
  if (url.protocol !== "file:") {
1389
1679
  throw new Error(`${label} "${url.href}" must use the file: URL scheme.`);
@@ -1393,6 +1683,12 @@ const ContextParts = Object.freeze({
1393
1683
  }
1394
1684
  return resolve(fileURLToPath(url));
1395
1685
  },
1686
+ /**
1687
+ * Parses a generated registry root string as a URL.
1688
+ *
1689
+ * @param root Registry root string.
1690
+ * @returns Parsed URL.
1691
+ */
1396
1692
  parseRootUrl(root) {
1397
1693
  try {
1398
1694
  return new URL(root);
@@ -1401,9 +1697,21 @@ const ContextParts = Object.freeze({
1401
1697
  throw new Error(`Generated registry root "${root}" is not a valid URL.`, { cause: error });
1402
1698
  }
1403
1699
  },
1700
+ /**
1701
+ * Checks strings for a URL scheme, excluding Windows drive paths.
1702
+ *
1703
+ * @param value String to inspect.
1704
+ * @returns True when the string has a URL scheme.
1705
+ */
1404
1706
  isUrlLike(value) {
1405
1707
  return moduleSchemeRe.test(value) && !/^[A-Za-z]:[\\/]/.test(value);
1406
1708
  },
1709
+ /**
1710
+ * Verifies registry-module readability and resolves its canonical path.
1711
+ *
1712
+ * @param registryPath Registry module filesystem path.
1713
+ * @returns Canonical readable path.
1714
+ */
1407
1715
  async canonicalReadableRegistryPath(registryPath) {
1408
1716
  try {
1409
1717
  await access(registryPath, fsConstants.R_OK);
@@ -1413,6 +1721,13 @@ const ContextParts = Object.freeze({
1413
1721
  throw new Error(`Generated handler registry module "${registryPath}" must exist and be readable.`, { cause: error });
1414
1722
  }
1415
1723
  },
1724
+ /**
1725
+ * Checks whether a canonical path escapes a canonical root.
1726
+ *
1727
+ * @param canonicalRoot Root directory.
1728
+ * @param canonicalPath Candidate path.
1729
+ * @returns True when the candidate escapes the root.
1730
+ */
1416
1731
  resolvesOutsideRoot(canonicalRoot, canonicalPath) {
1417
1732
  const relativePath = relative(canonicalRoot, canonicalPath);
1418
1733
  return (relativePath.startsWith("..") ||
@@ -1420,13 +1735,30 @@ const ContextParts = Object.freeze({
1420
1735
  relativePath.split(sep).includes("..") ||
1421
1736
  isAbsolute(relativePath));
1422
1737
  },
1738
+ /**
1739
+ * Creates a cache-bust query after a prior registry load failure.
1740
+ *
1741
+ * @param registryKey Canonical registry identifier.
1742
+ * @returns Retry query, or an empty object on the first attempt.
1743
+ */
1423
1744
  generatedRegistryCacheBust(registryKey) {
1424
1745
  const attempt = generatedRegistryLoadAttempts.get(registryKey) ?? 0;
1425
1746
  return attempt === 0 ? {} : { cacheBust: `retry-${attempt.toString()}` };
1426
1747
  },
1748
+ /**
1749
+ * Records another failed load for a registry.
1750
+ *
1751
+ * @param registryKey Canonical registry identifier.
1752
+ */
1427
1753
  recordGeneratedRegistryFailure(registryKey) {
1428
1754
  generatedRegistryLoadAttempts.set(registryKey, (generatedRegistryLoadAttempts.get(registryKey) ?? 0) + 1);
1429
1755
  },
1756
+ /**
1757
+ * Loads generated handler registries into a metadata registry.
1758
+ *
1759
+ * @param registries Generated registries to ingest.
1760
+ * @returns Populated handler metadata registry.
1761
+ */
1430
1762
  ingestGeneratedRegistries(registries) {
1431
1763
  const registry = new HandlerMetadataRegistry();
1432
1764
  const ingestor = new HandlerRegistryIngestor();
@@ -1435,6 +1767,15 @@ const ContextParts = Object.freeze({
1435
1767
  }
1436
1768
  return registry;
1437
1769
  },
1770
+ /**
1771
+ * Creates a repository from generated Entity handler metadata.
1772
+ *
1773
+ * @param entityType Entity class.
1774
+ * @param registries Generated registries to search.
1775
+ * @param metadata Ingested handler metadata.
1776
+ * @param options Repository options overriding defaults.
1777
+ * @returns Configured repository view.
1778
+ */
1438
1779
  createGeneratedRepository(entityType, registries, metadata, options) {
1439
1780
  const generated = ContextParts.findGeneratedEntity(entityType, registries);
1440
1781
  const handlers = ContextParts.findGeneratedHandlers(entityType, generated, metadata);
@@ -1446,6 +1787,13 @@ const ContextParts = Object.freeze({
1446
1787
  }, options);
1447
1788
  return new Repository(repositoryOptions);
1448
1789
  },
1790
+ /**
1791
+ * Finds the generated Entity handler group for an Entity class.
1792
+ *
1793
+ * @param entityType Entity class to find.
1794
+ * @param registries Generated registries to search.
1795
+ * @returns Matching handler group; throws when absent.
1796
+ */
1449
1797
  findGeneratedEntity(entityType, registries) {
1450
1798
  for (const registry of registries) {
1451
1799
  const generated = registry.receivers.find((receiver) => receiver.receiverKind === "entity" && receiver.receiverType === entityType);
@@ -1455,6 +1803,14 @@ const ContextParts = Object.freeze({
1455
1803
  }
1456
1804
  throw new Error(`Generated handler registry is missing metadata for ${entityType.name}.`);
1457
1805
  },
1806
+ /**
1807
+ * Finds handler metadata for an Entity class and state schema.
1808
+ *
1809
+ * @param entityType Entity class.
1810
+ * @param generated Matching generated Entity group.
1811
+ * @param metadata Handler metadata registry.
1812
+ * @returns Matching handler metadata; throws when absent.
1813
+ */
1458
1814
  findGeneratedHandlers(entityType, generated, metadata) {
1459
1815
  const matches = metadata.findByState(generated.stateSchema.typeName);
1460
1816
  const handlers = matches.find((candidate) => candidate.entityType === entityType);
@@ -1463,6 +1819,12 @@ const ContextParts = Object.freeze({
1463
1819
  }
1464
1820
  return handlers;
1465
1821
  },
1822
+ /**
1823
+ * Collects event schemas declared as returned or thrown handler outcomes.
1824
+ *
1825
+ * @param generated Generated Entity handler group.
1826
+ * @returns Frozen unique event schemas.
1827
+ */
1466
1828
  aggregateAssignedEvents(generated) {
1467
1829
  return ContextParts.uniqueSchemas(generated.handlers.flatMap((handler) => [
1468
1830
  ...(handler.kind === "command-assignment" || handler.kind === "event-reaction"
@@ -1473,6 +1835,12 @@ const ContextParts = Object.freeze({
1473
1835
  : []),
1474
1836
  ]));
1475
1837
  },
1838
+ /**
1839
+ * Collects event schemas produced by standalone handler outcomes.
1840
+ *
1841
+ * @param receivers Generated standalone groups.
1842
+ * @returns Frozen unique event schemas.
1843
+ */
1476
1844
  standaloneProducedEventSchemas(receivers) {
1477
1845
  return ContextParts.uniqueSchemas(receivers.flatMap((receiver) => receiver.handlers.flatMap((handler) => [
1478
1846
  ...(handler.kind === "command-assignment" || handler.kind === "event-reaction"
@@ -1483,6 +1851,13 @@ const ContextParts = Object.freeze({
1483
1851
  : []),
1484
1852
  ])));
1485
1853
  },
1854
+ /**
1855
+ * Returns unique schemas by Protobuf type name, preserving the last schema.
1856
+ *
1857
+ * @typeParam Schema Descriptor schema type retained in the result.
1858
+ * @param schemas Schemas to deduplicate.
1859
+ * @returns Frozen deduplicated schemas.
1860
+ */
1486
1861
  uniqueSchemas(schemas) {
1487
1862
  const byTypeName = new Map();
1488
1863
  for (const schema of schemas) {
@@ -1490,6 +1865,11 @@ const ContextParts = Object.freeze({
1490
1865
  }
1491
1866
  return Object.freeze([...byTypeName.values()]);
1492
1867
  },
1868
+ /**
1869
+ * Validates repository uniqueness, registration state, and projection cycles.
1870
+ *
1871
+ * @param repositories Repository views to validate.
1872
+ */
1493
1873
  preflightRepositories(repositories) {
1494
1874
  const entityTypes = new Set();
1495
1875
  const stateTypeNames = new Set();
@@ -1512,6 +1892,11 @@ const ContextParts = Object.freeze({
1512
1892
  }
1513
1893
  ContextParts.rejectStateCycles(repositories);
1514
1894
  },
1895
+ /**
1896
+ * Rejects cycles among repository state-subscription dependencies.
1897
+ *
1898
+ * @param repositories Repository views whose dependencies are checked.
1899
+ */
1515
1900
  rejectStateCycles(repositories) {
1516
1901
  const dependencies = new Map();
1517
1902
  for (const repository of repositories) {
@@ -1542,30 +1927,66 @@ const ContextParts = Object.freeze({
1542
1927
  for (const stateType of dependencies.keys())
1543
1928
  visit(stateType);
1544
1929
  },
1930
+ /**
1931
+ * Collects command dispatchers configured by repositories.
1932
+ *
1933
+ * @param repositories Repository views to inspect.
1934
+ * @returns Present command dispatchers.
1935
+ */
1545
1936
  repositoryCommandDispatchers(repositories) {
1546
1937
  return repositories.flatMap((repository) => {
1547
1938
  const dispatcher = repositoryAccess.commandDispatcher(repository);
1548
1939
  return dispatcher === undefined ? [] : [dispatcher];
1549
1940
  });
1550
1941
  },
1942
+ /**
1943
+ * Collects event dispatchers configured by repositories.
1944
+ *
1945
+ * @param repositories Repository views to inspect.
1946
+ * @returns Present event dispatchers.
1947
+ */
1551
1948
  repositoryEventDispatchers(repositories) {
1552
1949
  return repositories.flatMap((repository) => {
1553
1950
  const dispatcher = repositoryAccess.eventDispatcher(repository);
1554
1951
  return dispatcher === undefined ? [] : [dispatcher];
1555
1952
  });
1556
1953
  },
1954
+ /**
1955
+ * Collects System event dispatchers configured by repositories.
1956
+ *
1957
+ * @param repositories Repository views to inspect.
1958
+ * @returns Present System event dispatchers.
1959
+ */
1557
1960
  repositorySystemEventDispatchers(repositories) {
1558
1961
  return repositories.flatMap((repository) => {
1559
1962
  const dispatcher = repositoryAccess.systemEventDispatcher(repository);
1560
1963
  return dispatcher === undefined ? [] : [dispatcher];
1561
1964
  });
1562
1965
  },
1966
+ /**
1967
+ * Returns event dispatchers excluding System routes.
1968
+ *
1969
+ * @param dispatchers Event dispatchers to filter.
1970
+ * @returns Frozen domain event dispatchers.
1971
+ */
1563
1972
  domainEventDispatchers(dispatchers) {
1564
1973
  return Object.freeze(dispatchers.filter((dispatcher) => !ContextParts.isSystemEventDispatcher(dispatcher)));
1565
1974
  },
1975
+ /**
1976
+ * Returns System event dispatchers.
1977
+ *
1978
+ * @param dispatchers Event dispatchers to filter.
1979
+ * @returns Frozen System event dispatchers.
1980
+ */
1566
1981
  systemEventDispatchers(dispatchers) {
1567
1982
  return Object.freeze(dispatchers.filter((dispatcher) => ContextParts.isSystemEventDispatcher(dispatcher)));
1568
1983
  },
1984
+ /**
1985
+ * Checks a dispatcher's event family and rejects mixed families.
1986
+ *
1987
+ * @param dispatcher Dispatcher to inspect.
1988
+ * @returns True when its schemas are System event schemas.
1989
+ */
1569
1990
  isSystemEventDispatcher(dispatcher) {
1570
1991
  const schemas = [...dispatcher.messageSchemas()];
1571
1992
  const systemSchemas = schemas.filter((schema) => schema.typeName.startsWith("spine.system."));
@@ -1574,6 +1995,12 @@ const ContextParts = Object.freeze({
1574
1995
  }
1575
1996
  return systemSchemas.length > 0;
1576
1997
  },
1998
+ /**
1999
+ * Collects repository-produced event schemas by type URL.
2000
+ *
2001
+ * @param repositories Repository views to inspect.
2002
+ * @returns Frozen unique event schemas.
2003
+ */
1577
2004
  repositoryProducedEventSchemas(repositories) {
1578
2005
  const schemas = new Map();
1579
2006
  for (const repository of repositories) {
@@ -1583,10 +2010,21 @@ const ContextParts = Object.freeze({
1583
2010
  }
1584
2011
  return Object.freeze([...schemas.values()]);
1585
2012
  },
2013
+ /**
2014
+ * Clears metadata for a failed context build and closes its tenant index.
2015
+ *
2016
+ * @param context Context whose build failed.
2017
+ * @param tenantIndex Tenant index to close.
2018
+ */
1586
2019
  cleanupFailedContext(context, tenantIndex) {
1587
2020
  ContextParts.clearContextMetadata(context);
1588
2021
  tenantIndex.close();
1589
2022
  },
2023
+ /**
2024
+ * Removes runtime metadata associated with a context.
2025
+ *
2026
+ * @param context Context whose WeakMap metadata is removed.
2027
+ */
1590
2028
  clearContextMetadata(context) {
1591
2029
  const buses = contextEventBuses.get(context);
1592
2030
  if (buses !== undefined) {
@@ -1608,48 +2046,72 @@ const ContextParts = Object.freeze({
1608
2046
  eventSubscribers.delete(context);
1609
2047
  systemEventPosters.delete(context);
1610
2048
  },
2049
+ /**
2050
+ * Creates delivery operations from the context inboxes and readiness state.
2051
+ *
2052
+ * @param context Built context snapshot.
2053
+ * @param storageFactory Context storage provider.
2054
+ * @param tenantIndex Tenant index for startup scopes.
2055
+ * @param entityInbox Registered Entity inbox.
2056
+ * @param projections Projection inbox.
2057
+ * @param readiness Delivery readiness coordinator.
2058
+ * @returns Delivery descriptor.
2059
+ */
1611
2060
  createDeliveryDescriptor(context, storageFactory, tenantIndex, entityInbox, projections, readiness) {
1612
2061
  return Object.freeze({
1613
2062
  storageFactory,
1614
- async startupScopes() {
1615
- if (tenantIndex.tenantMode === "single-tenant") {
1616
- return Object.freeze([Object.freeze({})]);
1617
- }
1618
- return Object.freeze((await tenantIndex.all()).map((tenantId) => Object.freeze({ tenantId })));
1619
- },
1620
- storageContext(scope) {
1621
- if (context.tenantMode === "single-tenant") {
1622
- if (scope.tenantId !== undefined) {
1623
- throw new Error(`Single-tenant context "${context.name.value}" does not accept tenantId.`);
1624
- }
1625
- return Object.freeze({ name: context.name.value, multitenant: false });
1626
- }
1627
- const tenantId = scope.tenantId;
1628
- if (tenantId === undefined) {
1629
- throw new Error(`Multitenant context "${context.name.value}" requires tenantId.`);
1630
- }
1631
- return Object.freeze({
1632
- name: context.name.value,
1633
- multitenant: true,
1634
- tenantId: TenantBoundary.from(tenantId).tenantId,
1635
- });
1636
- },
1637
- endpoints() {
1638
- return Object.freeze([...entityInbox.endpoints(), ...projections.endpoints()]);
1639
- },
1640
- replay(message, tenantId) {
1641
- return message.label === "UPDATE_SUBSCRIBER"
1642
- ? projections.replay(message, tenantId)
1643
- : entityInbox.replay(message, tenantId);
1644
- },
1645
- onReady(onReady) {
1646
- return readiness.onReady(onReady);
1647
- },
1648
- transition(scopes, onReady, options) {
1649
- return readiness.transition(scopes, onReady, options);
1650
- },
2063
+ startupScopes: () => ContextParts.deliveryStartupScopes(tenantIndex),
2064
+ storageContext: (scope) => ContextParts.deliveryStorageContext(context, scope),
2065
+ endpoints: () => Object.freeze([...entityInbox.endpoints(), ...projections.endpoints()]),
2066
+ replay: (message, tenantId) => message.label === "UPDATE_SUBSCRIBER"
2067
+ ? projections.replay(message, tenantId)
2068
+ : entityInbox.replay(message, tenantId),
2069
+ onReady: (onReady) => readiness.onReady(onReady),
2070
+ transition: (scopes, onReady, options) => readiness.transition(scopes, onReady, options),
2071
+ });
2072
+ },
2073
+ /**
2074
+ * Lists delivery startup scopes, one per tenant in multitenant contexts.
2075
+ *
2076
+ * @param tenantIndex Tenant index supplying configured scopes.
2077
+ * @returns Frozen startup scope list.
2078
+ */
2079
+ async deliveryStartupScopes(tenantIndex) {
2080
+ if (tenantIndex.tenantMode === "single-tenant") {
2081
+ return Object.freeze([Object.freeze({})]);
2082
+ }
2083
+ return Object.freeze((await tenantIndex.all()).map((tenantId) => Object.freeze({ tenantId })));
2084
+ },
2085
+ /**
2086
+ * Creates tenant-validated storage context for a delivery scope.
2087
+ *
2088
+ * @param context Built context snapshot.
2089
+ * @param scope Selected startup scope.
2090
+ * @returns Storage context for the selected tenant.
2091
+ */
2092
+ deliveryStorageContext(context, scope) {
2093
+ if (context.tenantMode === "single-tenant") {
2094
+ if (scope.tenantId !== undefined) {
2095
+ throw new Error(`Single-tenant context "${context.name.value}" does not accept tenantId.`);
2096
+ }
2097
+ return Object.freeze({ name: context.name.value, multitenant: false });
2098
+ }
2099
+ const tenantId = scope.tenantId;
2100
+ if (tenantId === undefined) {
2101
+ throw new Error(`Multitenant context "${context.name.value}" requires tenantId.`);
2102
+ }
2103
+ return Object.freeze({
2104
+ name: context.name.value,
2105
+ multitenant: true,
2106
+ tenantId: TenantBoundary.from(tenantId).tenantId,
1651
2107
  });
1652
2108
  },
2109
+ /**
2110
+ * Closes prepared repositories and collects close failures.
2111
+ *
2112
+ * @param preparedRepositories Prepared repository resources to close.
2113
+ * @returns Collected close errors.
2114
+ */
1653
2115
  closePreparedRepositories(preparedRepositories) {
1654
2116
  const errors = [];
1655
2117
  for (const preparedRepository of preparedRepositories) {
@@ -1662,6 +2124,13 @@ const ContextParts = Object.freeze({
1662
2124
  }
1663
2125
  return errors;
1664
2126
  },
2127
+ /**
2128
+ * Calls one asynchronous context close action and records failures.
2129
+ *
2130
+ * @param close Close operation.
2131
+ * @param errors Failure collection.
2132
+ * @returns Promise fulfilled after the close attempt is recorded.
2133
+ */
1665
2134
  async closeContextPart(close, errors) {
1666
2135
  try {
1667
2136
  await close();
@@ -1670,6 +2139,15 @@ const ContextParts = Object.freeze({
1670
2139
  ContextParts.collectCloseError(error, errors);
1671
2140
  }
1672
2141
  },
2142
+ /**
2143
+ * Waits until accepted command, event, System event, and signal work drains.
2144
+ *
2145
+ * @param commandBus Command bus.
2146
+ * @param eventBus Domain event bus.
2147
+ * @param systemEventBus System event bus.
2148
+ * @param publisher Signal publisher.
2149
+ * @returns Promise fulfilled when accepted work stabilizes.
2150
+ */
1673
2151
  async drainContextWork(commandBus, eventBus, systemEventBus, publisher) {
1674
2152
  let observedCommandWork = -1;
1675
2153
  let observedEventWork = -1;
@@ -1686,6 +2164,12 @@ const ContextParts = Object.freeze({
1686
2164
  eventBusAccess.acceptedWorkCount(eventBus) !== observedEventWork ||
1687
2165
  eventBusAccess.acceptedWorkCount(systemEventBus) !== observedSystemEventWork);
1688
2166
  },
2167
+ /**
2168
+ * Adds an error or aggregate causes to the close error collection.
2169
+ *
2170
+ * @param error Close failure.
2171
+ * @param errors Failure collection.
2172
+ */
1689
2173
  collectCloseError(error, errors) {
1690
2174
  if (error instanceof AggregateError) {
1691
2175
  const causes = error.errors;
@@ -1696,6 +2180,13 @@ const ContextParts = Object.freeze({
1696
2180
  }
1697
2181
  errors.push(error);
1698
2182
  },
2183
+ /**
2184
+ * Binds a repository to context runtime services and supplies commit and close operations.
2185
+ *
2186
+ * @param repository Repository to prepare.
2187
+ * @param registration Context services and schemas.
2188
+ * @returns Prepared repository.
2189
+ */
1699
2190
  prepareRepositoryForContext(repository, registration) {
1700
2191
  ContextParts.requireRepositoryInstance(repository, "BoundedContext repository registration");
1701
2192
  const snapshot = ContextParts.repositorySnapshot(repository);
@@ -1726,6 +2217,12 @@ const ContextParts = Object.freeze({
1726
2217
  },
1727
2218
  };
1728
2219
  },
2220
+ /**
2221
+ * Captures repository metadata needed for context registration.
2222
+ *
2223
+ * @param repository Repository to inspect.
2224
+ * @returns Frozen registration snapshot.
2225
+ */
1729
2226
  repositorySnapshot(repository) {
1730
2227
  const snapshot = repositoryAccess.snapshot(repository);
1731
2228
  return Object.freeze({
@@ -1738,6 +2235,11 @@ const ContextParts = Object.freeze({
1738
2235
  snapshot,
1739
2236
  });
1740
2237
  },
2238
+ /**
2239
+ * Rejects a repository already registered with a bounded context.
2240
+ *
2241
+ * @param repository Repository to check.
2242
+ */
1741
2243
  rejectRegisteredRepository(repository) {
1742
2244
  const snapshot = ContextParts.repositorySnapshot(repository);
1743
2245
  const registration = registeredRepositories.get(repository);
@@ -1746,9 +2248,21 @@ const ContextParts = Object.freeze({
1746
2248
  `"${registration.name.value}".`);
1747
2249
  }
1748
2250
  },
2251
+ /**
2252
+ * Creates storage record columns from Entity metadata.
2253
+ *
2254
+ * @param snapshot Registration snapshot supplying fields.
2255
+ * @returns Record columns.
2256
+ */
1749
2257
  repositoryColumns(snapshot) {
1750
2258
  return snapshot.metadata.columns.map((field) => new RecordColumn(field.name, ColumnTypes.fromField(field.descriptor), (record) => ContextParts.readRecordField(record, field.localName)));
1751
2259
  },
2260
+ /**
2261
+ * Creates an immutable repository view from a registration snapshot.
2262
+ *
2263
+ * @param snapshot Registration snapshot.
2264
+ * @returns Immutable repository view.
2265
+ */
1752
2266
  createRepositoryView(snapshot) {
1753
2267
  return Object.freeze({
1754
2268
  entityType: snapshot.entityType,
@@ -1760,6 +2274,12 @@ const ContextParts = Object.freeze({
1760
2274
  snapshot: snapshot.snapshot,
1761
2275
  });
1762
2276
  },
2277
+ /**
2278
+ * Builds dispatch descriptors for Projection repositories with event handlers.
2279
+ *
2280
+ * @param repositories Repository views to inspect.
2281
+ * @returns Frozen Projection dispatch descriptors.
2282
+ */
1763
2283
  projectionDispatchers(repositories) {
1764
2284
  const projections = [];
1765
2285
  for (const repository of repositories) {
@@ -1778,6 +2298,12 @@ const ContextParts = Object.freeze({
1778
2298
  }
1779
2299
  return Object.freeze(projections);
1780
2300
  },
2301
+ /**
2302
+ * Collects unique Projection state schemas for clearing.
2303
+ *
2304
+ * @param projections Projection dispatch descriptors.
2305
+ * @returns Frozen unique clear targets.
2306
+ */
1781
2307
  projectionStateClearTargets(projections) {
1782
2308
  const unique = new Map();
1783
2309
  for (const projection of projections) {
@@ -1790,6 +2316,13 @@ const ContextParts = Object.freeze({
1790
2316
  }
1791
2317
  return Object.freeze([...unique.values()]);
1792
2318
  },
2319
+ /**
2320
+ * Reads stored events in deterministic timestamp, producer, version, and ID order.
2321
+ *
2322
+ * @param context Event-store storage context.
2323
+ * @param storageFactory Event storage provider.
2324
+ * @returns Stored events in deterministic order.
2325
+ */
1793
2326
  async readStoredEvents(context, storageFactory) {
1794
2327
  const eventStore = new EventStore(context, storageFactory);
1795
2328
  try {
@@ -1806,12 +2339,24 @@ const ContextParts = Object.freeze({
1806
2339
  eventStore.close();
1807
2340
  }
1808
2341
  },
2342
+ /**
2343
+ * Builds Stand options for the catch-up tenant scope.
2344
+ *
2345
+ * @param context Catch-up storage context.
2346
+ * @returns Options with a cloned tenant ID when multitenant.
2347
+ */
1809
2348
  catchUpStandOptions(context) {
1810
2349
  if (!context.multitenant) {
1811
2350
  return {};
1812
2351
  }
1813
2352
  return Object.freeze({ tenantId: clone(TenantIdSchema, context.tenantId) });
1814
2353
  },
2354
+ /**
2355
+ * Verifies a stored event envelope belongs to the catch-up tenant.
2356
+ *
2357
+ * @param context Catch-up storage context.
2358
+ * @param event Stored event to validate.
2359
+ */
1815
2360
  validateReplayTenant(context, event) {
1816
2361
  if (!context.multitenant) {
1817
2362
  return;
@@ -1825,6 +2370,12 @@ const ContextParts = Object.freeze({
1825
2370
  throw new Error("Read-side catch-up stored event envelope tenant does not match.");
1826
2371
  }
1827
2372
  },
2373
+ /**
2374
+ * Reads the tenant ID from an event import or past-message origin.
2375
+ *
2376
+ * @param event Event whose origin is inspected.
2377
+ * @returns Cloned tenant ID when present.
2378
+ */
1828
2379
  readReplayTenant(event) {
1829
2380
  switch (event.context?.origin.case) {
1830
2381
  case "importContext":
@@ -1835,9 +2386,23 @@ const ContextParts = Object.freeze({
1835
2386
  return undefined;
1836
2387
  }
1837
2388
  },
2389
+ /**
2390
+ * Copies a tenant ID when present.
2391
+ *
2392
+ * @param tenantId Optional tenant ID.
2393
+ * @returns Cloned tenant ID or undefined.
2394
+ */
1838
2395
  tenantIdValue(tenantId) {
1839
2396
  return tenantId === undefined ? undefined : clone(TenantIdSchema, tenantId);
1840
2397
  },
2398
+ /**
2399
+ * Dispatches a stored event to matching Projection handlers and direct updates.
2400
+ *
2401
+ * @param projections Projection dispatch descriptors.
2402
+ * @param event Stored event.
2403
+ * @param rebuild Whether direct dispatch is a rebuild.
2404
+ * @returns One when a Projection matched, otherwise zero.
2405
+ */
1841
2406
  async dispatchStoredProjectionEvent(projections, event, rebuild) {
1842
2407
  const typeUrl = event.message?.typeUrl;
1843
2408
  if (typeUrl === undefined || typeUrl === "") {
@@ -1852,12 +2417,32 @@ const ContextParts = Object.freeze({
1852
2417
  }
1853
2418
  return matching.length > 0 ? 1 : 0;
1854
2419
  },
2420
+ /**
2421
+ * Creates a replay error with the event ID and bounded cause details.
2422
+ *
2423
+ * @param event Event that failed.
2424
+ * @param cause Thrown replay cause.
2425
+ * @returns Replay error.
2426
+ */
1855
2427
  catchUpReplayError(event, cause) {
1856
2428
  return new CatchUpReplayError(event.id?.value ?? "(missing)", ContextParts.catchUpReplayDetail(cause));
1857
2429
  },
2430
+ /**
2431
+ * Returns an error string truncated to a maximum length.
2432
+ *
2433
+ * @param value String to bound.
2434
+ * @param limit Maximum character count.
2435
+ * @returns Bounded string.
2436
+ */
1858
2437
  boundedErrorString(value, limit) {
1859
2438
  return value.length <= limit ? value : `${value.slice(0, limit - 3)}...`;
1860
2439
  },
2440
+ /**
2441
+ * Converts a thrown value to bounded error name and message details.
2442
+ *
2443
+ * @param error Thrown value.
2444
+ * @returns Immutable bounded details.
2445
+ */
1861
2446
  catchUpReplayDetail(error) {
1862
2447
  if (error instanceof Error) {
1863
2448
  return Object.freeze({
@@ -1870,6 +2455,13 @@ const ContextParts = Object.freeze({
1870
2455
  message: ContextParts.boundedErrorString(String(error), errorDetailLimit),
1871
2456
  });
1872
2457
  },
2458
+ /**
2459
+ * Reads and requires the Entity ID field from a stored record.
2460
+ *
2461
+ * @param record Protobuf record.
2462
+ * @param snapshot Repository metadata identifying the ID field.
2463
+ * @returns ID field value; throws when missing.
2464
+ */
1873
2465
  readRecordId(record, snapshot) {
1874
2466
  const value = ContextParts.readRecordField(record, snapshot.idField.localName);
1875
2467
  if (value === undefined || value === null) {
@@ -1877,6 +2469,13 @@ const ContextParts = Object.freeze({
1877
2469
  }
1878
2470
  return value;
1879
2471
  },
2472
+ /**
2473
+ * Reads a field from a Protobuf record by descriptor local name.
2474
+ *
2475
+ * @param record Record message.
2476
+ * @param localName Descriptor local field name.
2477
+ * @returns Field value.
2478
+ */
1880
2479
  readRecordField(record, localName) {
1881
2480
  return record[localName];
1882
2481
  },