@spine-event-engine/server 2.0.0-snapshot.25 → 2.0.0-snapshot.28

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.
Files changed (207) hide show
  1. package/README.md +104 -16
  2. package/REFERENCE.md +486 -57
  3. package/dist/agent/agent-history.d.ts +19 -3
  4. package/dist/agent/agent-history.d.ts.map +1 -1
  5. package/dist/agent/agent-history.js +41 -10
  6. package/dist/agent/agent-history.js.map +1 -1
  7. package/dist/agent/agent-live-ai-runtime.d.ts +1 -1
  8. package/dist/agent/agent-live-ai-runtime.js +1 -1
  9. package/dist/agent/agent-live-budget.d.ts +1 -1
  10. package/dist/agent/agent-live-budget.js +1 -1
  11. package/dist/agent/agent-live-read-runtime.d.ts +13 -2
  12. package/dist/agent/agent-live-read-runtime.d.ts.map +1 -1
  13. package/dist/agent/agent-live-read-runtime.js +6 -5
  14. package/dist/agent/agent-live-read-runtime.js.map +1 -1
  15. package/dist/browser/browser-admission.d.ts +47 -0
  16. package/dist/browser/browser-admission.d.ts.map +1 -0
  17. package/dist/browser/browser-admission.js +200 -0
  18. package/dist/browser/browser-admission.js.map +1 -0
  19. package/dist/browser/browser-server.d.ts +171 -1
  20. package/dist/browser/browser-server.d.ts.map +1 -1
  21. package/dist/browser/browser-server.js +1027 -303
  22. package/dist/browser/browser-server.js.map +1 -1
  23. package/dist/browser/durable-subscription-bindings.d.ts +5 -0
  24. package/dist/browser/durable-subscription-bindings.d.ts.map +1 -1
  25. package/dist/browser/durable-subscription-bindings.js +226 -38
  26. package/dist/browser/durable-subscription-bindings.js.map +1 -1
  27. package/dist/browser/index.d.ts +1 -1
  28. package/dist/browser/index.d.ts.map +1 -1
  29. package/dist/browser/options.d.ts +53 -2
  30. package/dist/browser/options.d.ts.map +1 -1
  31. package/dist/bus/command-bus.d.ts +9 -1
  32. package/dist/bus/command-bus.d.ts.map +1 -1
  33. package/dist/bus/command-bus.js +75 -10
  34. package/dist/bus/command-bus.js.map +1 -1
  35. package/dist/bus/command-errors.d.ts +20 -4
  36. package/dist/bus/command-errors.d.ts.map +1 -1
  37. package/dist/bus/command-errors.js +47 -7
  38. package/dist/bus/command-errors.js.map +1 -1
  39. package/dist/bus/event-bus.d.ts +9 -1
  40. package/dist/bus/event-bus.d.ts.map +1 -1
  41. package/dist/bus/event-bus.js +136 -9
  42. package/dist/bus/event-bus.js.map +1 -1
  43. package/dist/context/bounded-context.d.ts +59 -39
  44. package/dist/context/bounded-context.d.ts.map +1 -1
  45. package/dist/context/bounded-context.js +277 -262
  46. package/dist/context/bounded-context.js.map +1 -1
  47. package/dist/context/entity-inbox.d.ts +12 -17
  48. package/dist/context/entity-inbox.d.ts.map +1 -1
  49. package/dist/context/entity-inbox.js +106 -119
  50. package/dist/context/entity-inbox.js.map +1 -1
  51. package/dist/context/local-inbox-handoff.d.ts +13 -13
  52. package/dist/context/local-inbox-handoff.d.ts.map +1 -1
  53. package/dist/context/local-inbox-handoff.js +60 -32
  54. package/dist/context/local-inbox-handoff.js.map +1 -1
  55. package/dist/context/persistent-tenant-index.d.ts +81 -0
  56. package/dist/context/persistent-tenant-index.d.ts.map +1 -0
  57. package/dist/context/persistent-tenant-index.js +270 -0
  58. package/dist/context/persistent-tenant-index.js.map +1 -0
  59. package/dist/context/projection-handoff.d.ts +9 -9
  60. package/dist/context/projection-handoff.d.ts.map +1 -1
  61. package/dist/context/projection-handoff.js +31 -23
  62. package/dist/context/projection-handoff.js.map +1 -1
  63. package/dist/context/tenant-index.d.ts +7 -6
  64. package/dist/context/tenant-index.d.ts.map +1 -1
  65. package/dist/context/tenant-index.js +2 -188
  66. package/dist/context/tenant-index.js.map +1 -1
  67. package/dist/context/tenant-unknown-error.d.ts +2 -2
  68. package/dist/context/tenant-unknown-error.d.ts.map +1 -1
  69. package/dist/context/tenant-unknown-error.js +4 -1
  70. package/dist/context/tenant-unknown-error.js.map +1 -1
  71. package/dist/delivery/delivery-supervisor.d.ts.map +1 -1
  72. package/dist/delivery/delivery-supervisor.js +12 -3
  73. package/dist/delivery/delivery-supervisor.js.map +1 -1
  74. package/dist/delivery/delivery.d.ts +4 -3
  75. package/dist/delivery/delivery.d.ts.map +1 -1
  76. package/dist/delivery/delivery.js +82 -34
  77. package/dist/delivery/delivery.js.map +1 -1
  78. package/dist/delivery/reception-observation.d.ts +3 -3
  79. package/dist/delivery/reception-observation.js +2 -2
  80. package/dist/entity/entity.d.ts +59 -0
  81. package/dist/entity/entity.d.ts.map +1 -1
  82. package/dist/entity/entity.js +60 -0
  83. package/dist/entity/entity.js.map +1 -1
  84. package/dist/handler/app-query-handler.d.ts +60 -0
  85. package/dist/handler/app-query-handler.d.ts.map +1 -0
  86. package/dist/handler/app-query-handler.js +23 -0
  87. package/dist/handler/app-query-handler.js.map +1 -0
  88. package/dist/handler/generated-registry-discovery.d.ts +6 -0
  89. package/dist/handler/generated-registry-discovery.d.ts.map +1 -1
  90. package/dist/handler/generated-registry-discovery.js +71 -2
  91. package/dist/handler/generated-registry-discovery.js.map +1 -1
  92. package/dist/handler/handler-decorators.d.ts +8 -29
  93. package/dist/handler/handler-decorators.d.ts.map +1 -1
  94. package/dist/handler/handler-decorators.js +33 -210
  95. package/dist/handler/handler-decorators.js.map +1 -1
  96. package/dist/handler/spine-metadata-inventory.d.ts +99 -0
  97. package/dist/handler/spine-metadata-inventory.d.ts.map +1 -0
  98. package/dist/handler/spine-metadata-inventory.js +162 -0
  99. package/dist/handler/spine-metadata-inventory.js.map +1 -0
  100. package/dist/index.d.ts +9 -4
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +6 -2
  103. package/dist/index.js.map +1 -1
  104. package/dist/repository/command-routing.d.ts +1 -1
  105. package/dist/repository/command-routing.js +1 -1
  106. package/dist/repository/event-routing.d.ts +1 -1
  107. package/dist/repository/event-routing.js +1 -1
  108. package/dist/repository/repository.d.ts +61 -26
  109. package/dist/repository/repository.d.ts.map +1 -1
  110. package/dist/repository/repository.js +612 -306
  111. package/dist/repository/repository.js.map +1 -1
  112. package/dist/repository/state-update-routing.d.ts +1 -1
  113. package/dist/repository/state-update-routing.js +1 -1
  114. package/dist/runtime/signal-publisher.d.ts +23 -8
  115. package/dist/runtime/signal-publisher.d.ts.map +1 -1
  116. package/dist/runtime/signal-publisher.js +11 -8
  117. package/dist/runtime/signal-publisher.js.map +1 -1
  118. package/dist/server/managed-server-application.d.ts +12 -11
  119. package/dist/server/managed-server-application.d.ts.map +1 -1
  120. package/dist/server/managed-server-application.js +41 -25
  121. package/dist/server/managed-server-application.js.map +1 -1
  122. package/dist/server/node-coordinator.d.ts +18 -5
  123. package/dist/server/node-coordinator.d.ts.map +1 -1
  124. package/dist/server/node-coordinator.js +744 -108
  125. package/dist/server/node-coordinator.js.map +1 -1
  126. package/dist/server/reserved-spine-rpc-paths.json +1 -0
  127. package/dist/server/server-environment.d.ts +23 -5
  128. package/dist/server/server-environment.d.ts.map +1 -1
  129. package/dist/server/server-environment.js +89 -24
  130. package/dist/server/server-environment.js.map +1 -1
  131. package/dist/server/server.d.ts +45 -0
  132. package/dist/server/server.d.ts.map +1 -1
  133. package/dist/server/server.js +112 -3
  134. package/dist/server/server.js.map +1 -1
  135. package/dist/services/app-query-execution.d.ts +53 -0
  136. package/dist/services/app-query-execution.d.ts.map +1 -0
  137. package/dist/services/app-query-execution.js +408 -0
  138. package/dist/services/app-query-execution.js.map +1 -0
  139. package/dist/services/native-operation.d.ts +34 -0
  140. package/dist/services/native-operation.d.ts.map +1 -0
  141. package/dist/services/native-operation.js +77 -0
  142. package/dist/services/native-operation.js.map +1 -0
  143. package/dist/services/query-reader.d.ts +32 -7
  144. package/dist/services/query-reader.d.ts.map +1 -1
  145. package/dist/services/query-reader.js +22 -2
  146. package/dist/services/query-reader.js.map +1 -1
  147. package/dist/services/read-request-status.d.ts +65 -0
  148. package/dist/services/read-request-status.d.ts.map +1 -0
  149. package/dist/services/read-request-status.js +151 -0
  150. package/dist/services/read-request-status.js.map +1 -0
  151. package/dist/services/spine-services.d.ts +42 -7
  152. package/dist/services/spine-services.d.ts.map +1 -1
  153. package/dist/services/spine-services.js +1005 -175
  154. package/dist/services/spine-services.js.map +1 -1
  155. package/dist/services/trusted-reader-ingress.d.ts +110 -0
  156. package/dist/services/trusted-reader-ingress.d.ts.map +1 -0
  157. package/dist/services/trusted-reader-ingress.js +102 -0
  158. package/dist/services/trusted-reader-ingress.js.map +1 -0
  159. package/dist/spi/handler-registry.d.ts +2 -0
  160. package/dist/spi/handler-registry.d.ts.map +1 -1
  161. package/dist/spi/handler-registry.js +1 -1
  162. package/dist/spi/handler-registry.js.map +1 -1
  163. package/dist/stand/read-side-filter-engine.d.ts +46 -0
  164. package/dist/stand/read-side-filter-engine.d.ts.map +1 -0
  165. package/dist/stand/read-side-filter-engine.js +356 -0
  166. package/dist/stand/read-side-filter-engine.js.map +1 -0
  167. package/dist/stand/read-side-filter.d.ts +247 -0
  168. package/dist/stand/read-side-filter.d.ts.map +1 -0
  169. package/dist/stand/read-side-filter.js +39 -0
  170. package/dist/stand/read-side-filter.js.map +1 -0
  171. package/dist/stand/read-side-message.d.ts +66 -0
  172. package/dist/stand/read-side-message.d.ts.map +1 -0
  173. package/dist/stand/read-side-message.js +167 -0
  174. package/dist/stand/read-side-message.js.map +1 -0
  175. package/dist/stand/stand-direct-delivery.d.ts +161 -0
  176. package/dist/stand/stand-direct-delivery.d.ts.map +1 -0
  177. package/dist/stand/stand-direct-delivery.js +210 -0
  178. package/dist/stand/stand-direct-delivery.js.map +1 -0
  179. package/dist/stand/stand.d.ts +81 -43
  180. package/dist/stand/stand.d.ts.map +1 -1
  181. package/dist/stand/stand.js +411 -55
  182. package/dist/stand/stand.js.map +1 -1
  183. package/dist/stand/subscription-observer.d.ts.map +1 -1
  184. package/dist/stand/subscription-observer.js +103 -0
  185. package/dist/stand/subscription-observer.js.map +1 -1
  186. package/dist/stand/subscription-read-filters.d.ts +40 -0
  187. package/dist/stand/subscription-read-filters.d.ts.map +1 -0
  188. package/dist/stand/subscription-read-filters.js +91 -0
  189. package/dist/stand/subscription-read-filters.js.map +1 -0
  190. package/dist/stand/subscription-reader-binding.d.ts +20 -0
  191. package/dist/stand/subscription-reader-binding.d.ts.map +1 -0
  192. package/dist/stand/subscription-reader-binding.js +56 -0
  193. package/dist/stand/subscription-reader-binding.js.map +1 -0
  194. package/dist/stand/subscription-records.d.ts +30 -3
  195. package/dist/stand/subscription-records.d.ts.map +1 -1
  196. package/dist/stand/subscription-records.js +61 -16
  197. package/dist/stand/subscription-records.js.map +1 -1
  198. package/dist/stand/subscription-registry.d.ts +49 -14
  199. package/dist/stand/subscription-registry.d.ts.map +1 -1
  200. package/dist/stand/subscription-registry.js +150 -31
  201. package/dist/stand/subscription-registry.js.map +1 -1
  202. package/dist/stand/subscription-runtime.d.ts +7 -2
  203. package/dist/stand/subscription-runtime.d.ts.map +1 -1
  204. package/dist/stand/subscription-runtime.js +131 -25
  205. package/dist/stand/subscription-runtime.js.map +1 -1
  206. package/dist/tsconfig.tsbuildinfo +1 -1
  207. package/package.json +16 -14
@@ -11,10 +11,6 @@
11
11
  * or implied. See the License for the specific language governing permissions and limitations under
12
12
  * the License.
13
13
  */
14
- import { constants as fsConstants } from "node:fs";
15
- import { access, realpath } from "node:fs/promises";
16
- import { isAbsolute, relative, resolve, sep } from "node:path";
17
- import { fileURLToPath, pathToFileURL } from "node:url";
18
14
  import { clone, create, getOption, hasOption } from "@bufbuild/protobuf";
19
15
  import { freezeRegistry, isAiModel, registryOptions } from "@spine-event-engine/ai/spi/runtime";
20
16
  import { TypeUrls } from "@spine-event-engine/core";
@@ -28,18 +24,20 @@ import { DeliveryReadiness } from "./local-inbox-handoff.js";
28
24
  import { LocalEntityInbox } from "./entity-inbox.js";
29
25
  import { LocalProjectionInbox } from "./projection-handoff.js";
30
26
  import { TenantAdmissions } from "./tenant-unknown-error.js";
27
+ import { SINGLE_TENANT } from "./tenant-index.js";
31
28
  import { AgentExecutionCapacity, } from "../agent/agent-execution-capacity.js";
32
29
  import { AgentAcceptanceReceipts, agentAcceptanceReceiptGroup, agentAcceptanceReceiptSpec, } from "../agent/agent-acceptance-receipts.js";
33
30
  import { Repository, repositoryAccess, } from "../repository/repository.js";
34
- import { GeneratedRegistryDiscovery } from "../handler/generated-registry-discovery.js";
31
+ import { SpineMetadataInventory } from "../handler/spine-metadata-inventory.js";
35
32
  import { HandlerRegistryIngestor, } from "../handler/generated-handler-registry.js";
36
33
  import { AbstractAssignee, AbstractCommander, AbstractEventReactor, AbstractEventSubscriber, } from "../handler/standalone.js";
37
34
  import { HandlerMetadataRegistry, } from "../handler/handler-metadata.js";
38
35
  import { SignalMetadata } from "../runtime/signal-metadata.js";
39
36
  import { runtimeAccess } from "../runtime/runtime.js";
40
37
  import { Stand, standAccess } from "../stand/stand.js";
38
+ import { ReadSideFilter } from "../stand/read-side-filter.js";
41
39
  import { SubscriptionRuntime, subscriptionRuntimeAccess } from "../stand/subscription-runtime.js";
42
- import { StorageSubscriptionRegistry, subscriptionRecordSpec, } from "../stand/subscription-registry.js";
40
+ import { StorageSubscriptionRegistry, subscriptionRecordGroup, } from "../stand/subscription-registry.js";
43
41
  import { IntegrationBroker } from "../integration/integration-broker.js";
44
42
  import { ServerEnvironment, serverEnvironmentAccess } from "../server/server-environment.js";
45
43
  import { SignalPublisher } from "../runtime/signal-publisher.js";
@@ -100,11 +98,8 @@ export class DuplicateEntityStateException extends Error {
100
98
  const frameworkConstructionToken = Object.freeze({
101
99
  frameworkConstructionToken: true,
102
100
  });
103
- const generatedRegistryFile = "generated/handler/generated-handler-registry.js";
104
101
  const errorDetailLimit = 500;
105
- const moduleSchemeRe = /^[A-Za-z][A-Za-z\d+.-]*:/;
106
102
  const internalStoragePrefix = "__spine/";
107
- const generatedRegistryLoadAttempts = new Map();
108
103
  const eventSubscribers = new WeakMap();
109
104
  const contextSystemPairings = new WeakMap();
110
105
  const contextTenantIndexes = new WeakMap();
@@ -158,7 +153,7 @@ export class BoundedContext {
158
153
  #agentReceipts;
159
154
  #agentController = new AbortController();
160
155
  #agentReservations = new Set();
161
- #activeReplays = new Set();
156
+ #activeDeliveries = new Set();
162
157
  #activePreparations = new Set();
163
158
  #tenantPreparations = new Map();
164
159
  #preparationController = new AbortController();
@@ -232,7 +227,7 @@ export class BoundedContext {
232
227
  eventBusAccess.bindEnvironment(this.#eventBus, this.#environment);
233
228
  eventBusAccess.bindEnvironment(this.#systemEventBus, this.#environment);
234
229
  commandBusAccess.setAdmission(this.#commandBus, (command) => this.#admitTenant(command.context?.actorContext?.tenantId));
235
- eventBusAccess.setAdmission(this.#eventBus, (event) => this.#admitTenant(ContextParts.readReplayTenant(event)));
230
+ eventBusAccess.setAdmission(this.#eventBus, (event) => this.#admitTenant(ContextParts.readCatchUpTenant(event)));
236
231
  standAccess.setAdmission(this.#stand, (tenantId) => this.#admitTenant(tenantId));
237
232
  standAccess.setPublicAdmission(this.#stand, () => {
238
233
  if (closingContexts.has(this) && !runtimeAccess.isRunningAdmittedWork(this.#environment))
@@ -265,11 +260,12 @@ export class BoundedContext {
265
260
  * @returns Shared preparation completion.
266
261
  */
267
262
  #prepareShared(mode) {
268
- if (!(this.#subscriptionRuntime.registry() instanceof StorageSubscriptionRegistry))
263
+ const registry = this.#subscriptionRuntime.registry();
264
+ if (!(registry instanceof StorageSubscriptionRegistry))
269
265
  return Promise.resolve();
270
266
  if (this.#sharedPreparation !== undefined)
271
267
  return this.#sharedPreparation;
272
- const attempt = this.#trackPreparation(() => StoragePreparations.for(this.#storageFactory).prepareRecord(ContextParts.createSubscriptionStorageContext(this.#snapshot.spec), subscriptionRecordSpec, undefined, mode));
268
+ const attempt = this.#trackPreparation(() => registry.prepare(mode));
273
269
  this.#sharedPreparation = attempt;
274
270
  void attempt.catch(() => {
275
271
  if (this.#sharedPreparation === attempt)
@@ -379,10 +375,13 @@ export class BoundedContext {
379
375
  async #prepareCurrentTenants(mode) {
380
376
  if (!this.isMultitenant)
381
377
  return this.#requiredPrepare(() => this.#prepareTenant(undefined, mode));
378
+ const index = this.tenantIndex;
379
+ if (index === SINGLE_TENANT)
380
+ throw new Error("Multitenant preparation requires a tenant index.");
382
381
  let after;
383
382
  do {
384
383
  serverEnvironmentAccess.requireAvailable(this.#environment);
385
- const page = await this.tenantIndex.page({
384
+ const page = await index.page({
386
385
  count: 64,
387
386
  signal: this.#preparationController.signal,
388
387
  ...(after === undefined ? {} : { after }),
@@ -390,7 +389,7 @@ export class BoundedContext {
390
389
  serverEnvironmentAccess.requireAvailable(this.#environment);
391
390
  for (const id of page.ids) {
392
391
  serverEnvironmentAccess.requireAvailable(this.#environment);
393
- await TenantAdmissions.require(this.tenantIndex, this.name.value, true, id);
392
+ await TenantAdmissions.require(index, this.name.value, true, id);
394
393
  serverEnvironmentAccess.requireAvailable(this.#environment);
395
394
  await this.#requiredPrepare(() => this.#prepareTenant(id, mode));
396
395
  }
@@ -473,8 +472,8 @@ export class BoundedContext {
473
472
  /**
474
473
  * Creates tenant-aware Inboxes and their shared readiness signal.
475
474
  *
476
- * @param tenantIndex Effective membership index for this context.
477
- * @returns Inboxes, tenant index, and readiness signal.
475
+ * @param tenantIndex Captured marker or directory for this Bounded Context.
476
+ * @returns Inboxes, tenant setting, and readiness signal.
478
477
  */
479
478
  #createDelivery(tenantIndex) {
480
479
  const deliveryReadiness = this.#createReadiness();
@@ -482,8 +481,8 @@ export class BoundedContext {
482
481
  const entityInbox = new LocalEntityInbox(this.#snapshot.name.value, deliveryReadiness, onAdmit, this.#environment.delivery.strategy);
483
482
  const projectionInbox = new LocalProjectionInbox(this.#snapshot.name.value, deliveryReadiness, onAdmit);
484
483
  deliveryReadiness.configureDirect((message, tenantId) => message.label === "UPDATE_SUBSCRIBER"
485
- ? projectionInbox.replay(message, tenantId)
486
- : entityInbox.replay(message, tenantId));
484
+ ? projectionInbox.deliver(message, tenantId)
485
+ : entityInbox.deliver(message, tenantId));
487
486
  return { deliveryReadiness, tenantIndex, entityInbox, projectionInbox };
488
487
  }
489
488
  /**
@@ -523,10 +522,14 @@ export class BoundedContext {
523
522
  this.#beginClose();
524
523
  },
525
524
  drain: () => this.#drainWork(),
526
- trackReplay: (onReplay) => this.#trackReplay(onReplay),
525
+ trackDelivery: (onDelivery) => this.#trackDelivery(onDelivery),
527
526
  });
528
527
  contextDeliveryDescriptors.set(this, ContextParts.createDeliveryDescriptor(this.#entityInbox, this.#projectionInbox));
529
528
  }
529
+ /**
530
+ * Prepares repository registrations, reserves their state types, and commits their Stand and
531
+ * inbox targets; rolls back uncommitted storage preparation on failure.
532
+ */
530
533
  #registerRepositories(repositories) {
531
534
  const preparedRepositories = this.#prepareRepositories(repositories);
532
535
  try {
@@ -566,6 +569,10 @@ export class BoundedContext {
566
569
  throw new DuplicateEntityStateException(stateType, prior.name.value, this.name.value);
567
570
  }
568
571
  }
572
+ /**
573
+ * Builds prepared repository registrations using this context’s storage and delivery services,
574
+ * cleaning earlier preparations if a later one fails.
575
+ */
569
576
  #prepareRepositories(repositories) {
570
577
  const registration = this.#repositoryRegistration();
571
578
  const preparedRepositories = [];
@@ -641,6 +648,10 @@ export class BoundedContext {
641
648
  ...(tenantId === undefined ? {} : { tenantId }),
642
649
  });
643
650
  }
651
+ /**
652
+ * Closes storage for repositories that were prepared but not committed, then rethrows the
653
+ * registration error with cleanup failures aggregated.
654
+ */
644
655
  #failRegistration(error, preparedRepositories) {
645
656
  const uncommitted = preparedRepositories.filter(({ repository }) => !this.#repositoryViews.has(repository));
646
657
  const closeErrors = ContextParts.closePreparedRepositories(uncommitted);
@@ -692,9 +703,10 @@ export class BoundedContext {
692
703
  return this.#snapshot.tenantMode === "multitenant";
693
704
  }
694
705
  /**
695
- * Returns the live index configured for this Bounded Context.
706
+ * Returns the captured tenant setting for this Bounded Context.
696
707
  *
697
- * @returns The effective configured index.
708
+ * @returns SINGLE_TENANT in single mode, even when the environment has an index;
709
+ * otherwise the effective directory. Narrow before calling index methods.
698
710
  */
699
711
  get tenantIndex() {
700
712
  return ContextParts.requireTenantIndex(this);
@@ -774,17 +786,16 @@ export class BoundedContext {
774
786
  throw new Error("Entity constructor is not registered in this Bounded Context.");
775
787
  }
776
788
  /**
777
- * Clears and locally replays every registered Projection from already-stored events.
789
+ * Clears registered Projection state and processes already-stored Events locally.
778
790
  *
779
- * Despite its legacy name, this method is not Projection catch-up. It is a
780
- * process-local maintenance helper and provides no Projection targeting,
791
+ * This process-local maintenance helper provides no Projection targeting,
781
792
  * historical starting point, durable operation identity, progress,
782
793
  * historical/live coordination, restart, resumption, or multi-node work.
783
794
  *
784
795
  * Supported boundary:
785
796
  * - projection subscribers only;
786
797
  * - already-stored events only;
787
- * - clear then replay, with no event re-append;
798
+ * - clear then dispatch stored Events, with no event re-append;
788
799
  * - single-tenant contexts reject `tenantId`;
789
800
  * - multitenant contexts require the exact non-blank `tenantId`;
790
801
  * - process-local sequential execution on the same EventBus runtime queue as
@@ -796,11 +807,15 @@ export class BoundedContext {
796
807
  * - durable live-traffic catch-up orchestration across processes.
797
808
  *
798
809
  * @param options Selects the tenant slice to rebuild.
799
- * @returns Resolves to replay and clear counts for the selected slice.
810
+ * @returns Dispatch and clear counts for the selected slice.
800
811
  */
801
812
  async catchUpReadSide(options = {}) {
802
813
  return eventBusAccess.runExclusive(this.#eventBus, () => this.#catchUpReadSideOnce(options));
803
814
  }
815
+ /**
816
+ * Clears registered Projection state for the selected tenant slice, reads stored events,
817
+ * validates each Event’s tenant, and dispatches stored Events sequentially.
818
+ */
804
819
  async #catchUpReadSideOnce(options) {
805
820
  const storageContext = ContextParts.catchUpStorageContext(this.#snapshot.spec, options);
806
821
  const tenantOptions = ContextParts.catchUpStandOptions(storageContext);
@@ -808,7 +823,7 @@ export class BoundedContext {
808
823
  const clearTargets = ContextParts.projectionStateClearTargets(projections);
809
824
  const clearedStateTypes = [];
810
825
  let clearedEntityCount = 0;
811
- let replayedEventCount = 0;
826
+ let processedEventCount = 0;
812
827
  for (const target of clearTargets) {
813
828
  clearedEntityCount += await this.#stand.clear(target.schema, tenantOptions);
814
829
  clearedStateTypes.push(target.typeUrl);
@@ -816,15 +831,15 @@ export class BoundedContext {
816
831
  const events = await ContextParts.readStoredEvents(storageContext, this.#storageFactory);
817
832
  for (const event of events) {
818
833
  try {
819
- ContextParts.validateReplayTenant(storageContext, event);
820
- replayedEventCount += await ContextParts.dispatchStoredProjectionEvent(projections, event, true);
834
+ ContextParts.validateCatchUpTenant(storageContext, event);
835
+ processedEventCount += await ContextParts.dispatchStoredProjectionEvent(projections, event, true);
821
836
  }
822
837
  catch (error) {
823
- throw ContextParts.catchUpReplayError(event, error);
838
+ throw ContextParts.catchUpDispatchError(event, error);
824
839
  }
825
840
  }
826
841
  return Object.freeze({
827
- replayedEventCount,
842
+ processedEventCount,
828
843
  clearedEntityCount,
829
844
  clearedStateTypes: Object.freeze(clearedStateTypes),
830
845
  });
@@ -891,13 +906,12 @@ export class BoundedContext {
891
906
  ContextParts.clearContextMetadata(this);
892
907
  }
893
908
  /**
894
- * Settles entered replay, local follow-ups, and reserved Agent turns.
909
+ * Settles entered deliveries, outgoing handoffs, and reserved Agent turns.
895
910
  * @param errors Collects independent close failures.
896
911
  */
897
912
  async #drainAdmittedContextWork(errors) {
898
913
  await this.#closePart(() => Promise.allSettled([...this.#activePreparations]).then(() => undefined), errors);
899
- await this.#closePart(() => Promise.allSettled([...this.#activeReplays]).then(() => undefined), errors);
900
- await this.#closePart(() => this.#entityInbox.whenFollowUpsIdle(), errors);
914
+ await this.#closePart(() => Promise.allSettled([...this.#activeDeliveries]).then(() => undefined), errors);
901
915
  await this.#closePart(() => Promise.allSettled([...this.#agentReservations]).then(() => undefined), errors);
902
916
  }
903
917
  /**
@@ -932,15 +946,15 @@ export class BoundedContext {
932
946
  }
933
947
  }
934
948
  /**
935
- * Registers an entered shared replay through its complete follow-up callback.
936
- * @param onReplay Complete replay callback.
937
- * @returns Completion of the captured callback.
949
+ * Registers an entered Inbox delivery through recipient processing and outgoing handoff.
950
+ * @param onDelivery Delivers the stored message.
951
+ * @returns Completion after the delivery callback returns.
938
952
  */
939
- #trackReplay(onReplay) {
940
- const replay = Promise.resolve().then(onReplay);
941
- this.#activeReplays.add(replay);
942
- void replay.then(() => this.#activeReplays.delete(replay), () => this.#activeReplays.delete(replay));
943
- return replay;
953
+ #trackDelivery(onDelivery) {
954
+ const delivery = Promise.resolve().then(onDelivery);
955
+ this.#activeDeliveries.add(delivery);
956
+ void delivery.then(() => this.#activeDeliveries.delete(delivery), () => this.#activeDeliveries.delete(delivery));
957
+ return delivery;
944
958
  }
945
959
  /**
946
960
  * Calls a close action and collects its failure before later cleanup.
@@ -992,7 +1006,7 @@ export const boundedContextAccess = Object.freeze({
992
1006
  const phase = contextClosePhases.get(context);
993
1007
  if (prepare === undefined || phase === undefined)
994
1008
  return Promise.reject(new TypeError("Service input requires a built context."));
995
- return phase.trackReplay(async () => {
1009
+ return phase.trackDelivery(async () => {
996
1010
  await onMembership();
997
1011
  await prepare(tenantId);
998
1012
  await onReady?.();
@@ -1011,13 +1025,16 @@ export const boundedContextAccess = Object.freeze({
1011
1025
  const phase = contextClosePhases.get(context);
1012
1026
  if (phase === undefined)
1013
1027
  return Promise.reject(new TypeError("Service registry requires a built context."));
1014
- return phase.trackReplay(onOperation);
1028
+ return phase.trackDelivery(onOperation);
1015
1029
  },
1016
- trackReplay(context, onReplay) {
1030
+ /**
1031
+ * Registers entered delivery work with the context close lifecycle.
1032
+ */
1033
+ trackDelivery(context, onDelivery) {
1017
1034
  const phase = contextClosePhases.get(context);
1018
1035
  if (phase === undefined)
1019
- return Promise.reject(new TypeError("Replay requires a built context."));
1020
- return phase.trackReplay(onReplay);
1036
+ return Promise.reject(new TypeError("Delivery requires a built context."));
1037
+ return phase.trackDelivery(onDelivery);
1021
1038
  },
1022
1039
  /**
1023
1040
  * Resolves a view only within the Bounded Context that issued it.
@@ -1035,18 +1052,27 @@ export const boundedContextAccess = Object.freeze({
1035
1052
  ? issued.repository
1036
1053
  : undefined;
1037
1054
  },
1055
+ /**
1056
+ * Stops new work at this Bounded Context and begins shutdown of its runtime facilities.
1057
+ */
1038
1058
  beginClose(context) {
1039
1059
  const phase = contextClosePhases.get(context);
1040
1060
  if (phase === undefined)
1041
1061
  throw new TypeError("Close phase requires a built BoundedContext.");
1042
1062
  phase.begin();
1043
1063
  },
1064
+ /**
1065
+ * Waits for accepted runtime work and entered delivery operations to finish.
1066
+ */
1044
1067
  drainWork(context) {
1045
1068
  const phase = contextClosePhases.get(context);
1046
1069
  if (phase === undefined)
1047
1070
  return Promise.reject(new TypeError("Drain requires a built BoundedContext."));
1048
1071
  return phase.drain();
1049
1072
  },
1073
+ /**
1074
+ * Installs the context logger on both EventBuses, the signal publisher, and subscription runtime.
1075
+ */
1050
1076
  installLogger(context, logger) {
1051
1077
  if (!contextStorageFactories.has(context)) {
1052
1078
  throw new TypeError("Context logger requires a built BoundedContext instance.");
@@ -1068,6 +1094,9 @@ export const boundedContextAccess = Object.freeze({
1068
1094
  }
1069
1095
  subscriptionRuntimeAccess.installLogger(runtime, logger, context.name.value);
1070
1096
  },
1097
+ /**
1098
+ * Returns the diagnostic logger installed on this Bounded Context.
1099
+ */
1071
1100
  loggerFor(context) {
1072
1101
  const logger = contextLoggers.get(context);
1073
1102
  if (logger === undefined) {
@@ -1075,11 +1104,17 @@ export const boundedContextAccess = Object.freeze({
1075
1104
  }
1076
1105
  return logger;
1077
1106
  },
1107
+ /**
1108
+ * Checks whether a value is a BoundedContextBuilder created by this module.
1109
+ */
1078
1110
  isBuilder(value) {
1079
1111
  return (typeof value === "object" &&
1080
1112
  value !== null &&
1081
1113
  builderBuilds.has(value));
1082
1114
  },
1115
+ /**
1116
+ * Builds the supplied context builder and completes its configured initialization.
1117
+ */
1083
1118
  build(builder) {
1084
1119
  const build = builderBuilds.get(builder);
1085
1120
  if (build === undefined) {
@@ -1087,6 +1122,9 @@ export const boundedContextAccess = Object.freeze({
1087
1122
  }
1088
1123
  return build();
1089
1124
  },
1125
+ /**
1126
+ * Attaches a subscriber to the context domain EventBus for the requested event type.
1127
+ */
1090
1128
  subscribeToEvent(context, typeUrl, subscriber) {
1091
1129
  const subscribe = eventSubscribers.get(context);
1092
1130
  if (subscribe === undefined) {
@@ -1094,6 +1132,9 @@ export const boundedContextAccess = Object.freeze({
1094
1132
  }
1095
1133
  return subscribe(typeUrl, subscriber);
1096
1134
  },
1135
+ /**
1136
+ * Posts a framework System Event through the context System EventBus.
1137
+ */
1097
1138
  postSystemEvent(context, event) {
1098
1139
  const post = systemEventPosters.get(context);
1099
1140
  if (post === undefined) {
@@ -1101,12 +1142,18 @@ export const boundedContextAccess = Object.freeze({
1101
1142
  }
1102
1143
  return post(event);
1103
1144
  },
1145
+ /**
1146
+ * Posts an external domain Event through the context EventBus.
1147
+ */
1104
1148
  postExternalEvent(context, event) {
1105
1149
  const imported = clone(EventSchema, event);
1106
1150
  imported.context = clone(EventContextSchema, imported.context ?? create(EventContextSchema));
1107
1151
  imported.context.external = true;
1108
1152
  return context.eventBus().post(imported);
1109
1153
  },
1154
+ /**
1155
+ * Subscribes to events produced by this context and forwards matching signals to the supplied observer.
1156
+ */
1110
1157
  observeProducedSignals(context, observer) {
1111
1158
  const publisher = contextSignalPublishers.get(context);
1112
1159
  if (publisher === undefined) {
@@ -1114,12 +1161,21 @@ export const boundedContextAccess = Object.freeze({
1114
1161
  }
1115
1162
  return publisher.observe(observer);
1116
1163
  },
1164
+ /**
1165
+ * Returns the domain/System specification pairing captured when the context was built.
1166
+ */
1117
1167
  systemPairing(context) {
1118
1168
  return ContextParts.cloneSystemPairing(ContextParts.requireSystemPairing(context));
1119
1169
  },
1170
+ /**
1171
+ * Returns the captured marker or directory for this Bounded Context.
1172
+ */
1120
1173
  tenantIndex(context) {
1121
1174
  return ContextParts.requireTenantIndex(context);
1122
1175
  },
1176
+ /**
1177
+ * Returns the storage factory captured for repository and Stand operations in this context.
1178
+ */
1123
1179
  storageFactory(context) {
1124
1180
  const storageFactory = contextStorageFactories.get(context);
1125
1181
  if (storageFactory === undefined) {
@@ -1127,6 +1183,9 @@ export const boundedContextAccess = Object.freeze({
1127
1183
  }
1128
1184
  return storageFactory;
1129
1185
  },
1186
+ /**
1187
+ * Returns the persistent or in-memory registry used to store Stand subscription definitions.
1188
+ */
1130
1189
  subscriptionRegistry(context) {
1131
1190
  const runtime = contextSubscriptionRuntimes.get(context);
1132
1191
  if (runtime === undefined) {
@@ -1134,13 +1193,24 @@ export const boundedContextAccess = Object.freeze({
1134
1193
  }
1135
1194
  return runtime.registry();
1136
1195
  },
1137
- consumeSubscription(context, id, onUpdate) {
1196
+ /**
1197
+ * Consumes a registered Stand subscription update and returns its managed delivery handle.
1198
+ */
1199
+ consumeSubscription(context, id, onUpdate, onInvalidated, expected) {
1138
1200
  const runtime = contextSubscriptionRuntimes.get(context);
1139
1201
  if (runtime === undefined) {
1140
1202
  throw new TypeError("Subscription consumption requires a built BoundedContext instance.");
1141
1203
  }
1142
- return runtime.consume(id, onUpdate);
1204
+ return runtime.consume(id, (update) => {
1205
+ onUpdate({
1206
+ update,
1207
+ filter: (facts) => standAccess.filterSubscription(context.stand(), update, facts),
1208
+ });
1209
+ }, onInvalidated, expected);
1143
1210
  },
1211
+ /**
1212
+ * Returns the context delivery descriptor used to route Entity and Projection inbox work.
1213
+ */
1144
1214
  delivery(context) {
1145
1215
  const descriptor = contextDeliveryDescriptors.get(context);
1146
1216
  if (descriptor === undefined) {
@@ -1161,12 +1231,12 @@ export class BoundedContextBuilder {
1161
1231
  #eventReceivers = [];
1162
1232
  #repositories = new Set();
1163
1233
  #entityTypes = new Set();
1234
+ #readFilters = new Set();
1164
1235
  #generatedRepositoryOptions = new Map();
1165
1236
  #aiRegistry;
1166
1237
  #tenantIndex;
1167
1238
  #subscriptionRegistry;
1168
1239
  #persistSystemEvents = false;
1169
- #generatedRegistryRoot;
1170
1240
  /**
1171
1241
  * Registers the framework-only construction hook for this module.
1172
1242
  */
@@ -1217,6 +1287,15 @@ export class BoundedContextBuilder {
1217
1287
  isMultitenant() {
1218
1288
  return this.#specSnapshot.multitenant;
1219
1289
  }
1290
+ /**
1291
+ * Adds an outward read policy to this Bounded Context.
1292
+ * @param filter Policy applied before Server-level outward policies.
1293
+ * @returns This builder for further configuration.
1294
+ */
1295
+ addReadSideFilter(filter) {
1296
+ this.#readFilters.add(filter);
1297
+ return this;
1298
+ }
1220
1299
  /**
1221
1300
  * Adds an explicitly assembled repository or an Entity class.
1222
1301
  *
@@ -1321,7 +1400,9 @@ export class BoundedContextBuilder {
1321
1400
  return this;
1322
1401
  }
1323
1402
  /**
1324
- * Sets a live tenant index for this Bounded Context.
1403
+ * Sets a directory that overrides the environment index for this multitenant Bounded Context.
1404
+ * The environment must already have a real index at build time; an override
1405
+ * cannot enable multitenancy in a SINGLE_TENANT environment.
1325
1406
  *
1326
1407
  * @param tenantIndex Application membership index.
1327
1408
  * @returns This builder.
@@ -1365,16 +1446,6 @@ export class BoundedContextBuilder {
1365
1446
  this.#subscriptionRegistry = registry;
1366
1447
  return this;
1367
1448
  }
1368
- /**
1369
- * Sets a trusted compiled application root for generated handler metadata.
1370
- *
1371
- * @param root Names the compiled package or application root.
1372
- * @returns Returns this builder for further configuration.
1373
- */
1374
- withGeneratedRegistryRoot(root) {
1375
- this.#generatedRegistryRoot = root;
1376
- return this;
1377
- }
1378
1449
  /**
1379
1450
  * Builds a context after loading generated handlers and preparing runtime resources.
1380
1451
  *
@@ -1384,6 +1455,10 @@ export class BoundedContextBuilder {
1384
1455
  build(options = {}) {
1385
1456
  return this.#buildAsyncWith(options);
1386
1457
  }
1458
+ /**
1459
+ * Loads generated handlers and repositories, assembles context facilities, and completes
1460
+ * requested initialization before publishing the context.
1461
+ */
1387
1462
  async #buildAsyncWith(options = {}) {
1388
1463
  const environment = ServerEnvironment.instance();
1389
1464
  const finishBuild = serverEnvironmentAccess.beginContextBuild(environment);
@@ -1415,10 +1490,11 @@ export class BoundedContextBuilder {
1415
1490
  * @returns The integrated context.
1416
1491
  */
1417
1492
  async #buildAsyncReady(options, environment) {
1418
- const generated = await this.#loadGeneratedArtifacts([...this.#entityTypes]);
1493
+ const tenantIndex = this.#selectTenantIndex(environment);
1494
+ const generated = this.#loadGeneratedArtifacts([...this.#entityTypes]);
1419
1495
  serverEnvironmentAccess.requireAvailable(environment);
1420
1496
  const repositories = [...this.#repositories, ...generated.repositories];
1421
- const context = await this.#buildWithResources(repositories, environment.storageFactory, generated.standalone, this.#aiRegistry ?? environment.ai);
1497
+ const context = await this.#buildWithResources(repositories, environment.storageFactory, generated.standalone, this.#aiRegistry ?? environment.ai, tenantIndex);
1422
1498
  try {
1423
1499
  await this.#integrateBuiltContext(context, options, environment);
1424
1500
  }
@@ -1427,6 +1503,20 @@ export class BoundedContextBuilder {
1427
1503
  }
1428
1504
  return context;
1429
1505
  }
1506
+ /**
1507
+ * Captures tenant selection before acquiring business storage.
1508
+ *
1509
+ * @param environment Process facilities selected for this build.
1510
+ * @returns Marker for single mode or the effective directory for multi mode.
1511
+ */
1512
+ #selectTenantIndex(environment) {
1513
+ if (!this.#specSnapshot.multitenant)
1514
+ return SINGLE_TENANT;
1515
+ const configured = environment.tenantIndex;
1516
+ if (configured === SINGLE_TENANT)
1517
+ throw new Error("Multitenant Bounded Context requires an environment tenant index.");
1518
+ return this.#tenantIndex ?? configured;
1519
+ }
1430
1520
  /**
1431
1521
  * Completes required integration and requested initialization before publication.
1432
1522
  * @param context Constructed context.
@@ -1475,20 +1565,33 @@ export class BoundedContextBuilder {
1475
1565
  * @param storageFactory Storage factory for the context.
1476
1566
  * @param standalone Generated standalone handler groups.
1477
1567
  * @param ai Optional AI registry.
1568
+ * @param tenantIndex Captured marker or directory for admission.
1478
1569
  * @returns The assembled context.
1479
1570
  */
1480
- async #buildWithResources(repositories, storageFactory, standalone, ai) {
1571
+ async #buildWithResources(repositories, storageFactory, standalone, ai, tenantIndex) {
1481
1572
  const resources = this.#subscriptionRegistry === undefined ? {} : { registry: this.#subscriptionRegistry };
1482
1573
  this.#subscriptionRegistry = undefined;
1483
1574
  const registeredRepositories = [...repositories];
1484
1575
  try {
1485
- return this.#assembleContext(registeredRepositories, storageFactory, standalone, resources, ai);
1576
+ return this.#assembleContext(registeredRepositories, storageFactory, standalone, resources, ai, tenantIndex);
1486
1577
  }
1487
1578
  catch (error) {
1488
1579
  return await this.#failBuild(resources, error);
1489
1580
  }
1490
1581
  }
1491
- #assembleContext(repositories, storageFactory, standalone, resources, ai) {
1582
+ /**
1583
+ * Assembles dispatchers, buses, Stand, subscription runtime, and integration hooks, registering
1584
+ * the completed context with its environment.
1585
+ *
1586
+ * @param repositories Registered repository views.
1587
+ * @param storageFactory Provider for business records.
1588
+ * @param standalone Generated standalone handlers.
1589
+ * @param resources Partial resources to close if assembly fails.
1590
+ * @param ai Selected AI registry, when configured.
1591
+ * @param tenantIndex Captured marker or directory.
1592
+ * @returns Assembled Bounded Context.
1593
+ */
1594
+ #assembleContext(repositories, storageFactory, standalone, resources, ai, tenantIndex) {
1492
1595
  ContextParts.preflightRepositories(repositories);
1493
1596
  this.#preflightAgents(repositories, storageFactory, ai);
1494
1597
  if (ai !== undefined)
@@ -1502,17 +1605,28 @@ export class BoundedContextBuilder {
1502
1605
  ...ContextParts.repositoryProducedEventSchemas(repositories),
1503
1606
  ...ContextParts.standaloneProducedEventSchemas(standalone),
1504
1607
  ]);
1505
- const running = this.#buildRuntime(storageFactory, buses, resources);
1506
- const context = this.#createContext(repositories, storageFactory, buses, running, ai);
1608
+ const running = this.#buildRuntime(storageFactory, buses, resources, tenantIndex);
1609
+ const context = this.#createContext(repositories, storageFactory, buses, running, tenantIndex, ai);
1507
1610
  resources.context = context;
1508
1611
  if (registerBoundedContext === undefined)
1509
1612
  throw new Error("Context registration is unavailable.");
1510
1613
  registerBoundedContext(context, repositories);
1614
+ this.#attachIntegration(context, buses, dispatchers, standaloneEvent);
1615
+ return context;
1616
+ }
1617
+ /**
1618
+ * Registers external Event routes after Bounded Context construction.
1619
+ *
1620
+ * @param context Completed Bounded Context.
1621
+ * @param buses Signal buses and System metadata.
1622
+ * @param dispatchers Repository dispatchers.
1623
+ * @param standaloneEvent Generated standalone Event dispatcher, when present.
1624
+ */
1625
+ #attachIntegration(context, buses, dispatchers, standaloneEvent) {
1511
1626
  ContextParts.attachIntegration(context, buses.eventBus, buses.systemSpec, ContextParts.externalEventSchemas([
1512
1627
  ...dispatchers.domain,
1513
1628
  ...(standaloneEvent === undefined ? [] : [standaloneEvent]),
1514
1629
  ]));
1515
- return context;
1516
1630
  }
1517
1631
  /**
1518
1632
  * Rejects missing Agent prerequisites before repository intake is installed.
@@ -1540,6 +1654,9 @@ export class BoundedContextBuilder {
1540
1654
  throw new TypeError("Agent repository capabilities must be factory-created models.");
1541
1655
  }
1542
1656
  }
1657
+ /**
1658
+ * Collects repository and configured event recipients into domain and System dispatcher groups.
1659
+ */
1543
1660
  #buildDispatchers(repositories) {
1544
1661
  const event = [
1545
1662
  ...ContextParts.repositoryEventDispatchers(repositories),
@@ -1553,6 +1670,9 @@ export class BoundedContextBuilder {
1553
1670
  ],
1554
1671
  };
1555
1672
  }
1673
+ /**
1674
+ * Creates command, domain Event, and System Event buses plus their stores and signal publisher.
1675
+ */
1556
1676
  #buildBuses(repositories, storageFactory, dispatchers, resources) {
1557
1677
  const commandBus = new CommandBus([
1558
1678
  ...this.#commandDispatchers,
@@ -1577,6 +1697,9 @@ export class BoundedContextBuilder {
1577
1697
  systemSpec: system.spec,
1578
1698
  };
1579
1699
  }
1700
+ /**
1701
+ * Creates the System context store, EventBus, and Stand, then registers System event dispatchers.
1702
+ */
1580
1703
  #buildSystemBuses(storageFactory, dispatchers, resources) {
1581
1704
  const systemSpec = ContextParts.createSystemSpec(this.#specSnapshot, this.#persistSystemEvents);
1582
1705
  const systemEventStore = systemSpec.storesEvents
@@ -1600,6 +1723,10 @@ export class BoundedContextBuilder {
1600
1723
  spec: systemSpec,
1601
1724
  };
1602
1725
  }
1726
+ /**
1727
+ * Matches generated standalone handlers to configured instances and registers their dispatchers
1728
+ * on the appropriate buses.
1729
+ */
1603
1730
  #installStandalone(standalone, buses) {
1604
1731
  ContextParts.assertUniqueCommandReceptors(standalone);
1605
1732
  const runtime = standalone.length === 0
@@ -1616,34 +1743,60 @@ export class BoundedContextBuilder {
1616
1743
  buses.systemEventBus.register(state);
1617
1744
  return event;
1618
1745
  }
1619
- #buildRuntime(storageFactory, buses, resources) {
1620
- const stand = this.#buildStand(storageFactory);
1746
+ /**
1747
+ * Creates the context Stand, subscription registry, and runtime that observes domain and System events.
1748
+ *
1749
+ * @param storageFactory Provider for business records.
1750
+ * @param buses Assembled signal buses.
1751
+ * @param resources Partial resources to close if assembly fails.
1752
+ * @param tenantIndex Captured marker or directory.
1753
+ * @returns Assembled Stand and subscription runtime.
1754
+ */
1755
+ #buildRuntime(storageFactory, buses, resources, tenantIndex) {
1756
+ const stand = this.#buildStand(storageFactory, tenantIndex);
1621
1757
  resources.stand = stand;
1622
1758
  const registry = resources.registry ??
1623
- new StorageSubscriptionRegistry(ContextParts.createSubscriptionStorageContext(this.#specSnapshot), storageFactory);
1759
+ new StorageSubscriptionRegistry(ContextParts.createSubscriptionStorageContext(this.#specSnapshot), storageFactory, subscriptionRecordGroup(this.#specSnapshot.name.value));
1624
1760
  resources.registry = registry;
1625
1761
  const runtime = new SubscriptionRuntime(stand, buses.systemStand, buses.eventBus, buses.systemEventBus, registry);
1626
1762
  resources.runtime = runtime;
1627
1763
  return { stand, registry, runtime };
1628
1764
  }
1629
1765
  /**
1630
- * Builds the context Stand with a captured live membership directory.
1766
+ * Builds the Bounded Context Stand with captured tenant admission.
1631
1767
  *
1632
1768
  * @param storageFactory Provider used for Entity records.
1769
+ * @param tenantIndex Captured marker or directory.
1633
1770
  * @returns Direct read-side Stand.
1634
1771
  */
1635
- #buildStand(storageFactory) {
1636
- const tenantIndex = this.#tenantIndex ?? ServerEnvironment.instance().tenantIndex;
1772
+ #buildStand(storageFactory, tenantIndex) {
1637
1773
  const stand = new Stand({
1638
1774
  context: ContextParts.createStorageMode(this.#specSnapshot),
1639
1775
  storageFactory,
1640
1776
  });
1777
+ if (this.#readFilters.size > 0)
1778
+ standAccess.installReadFilters(stand, [...this.#readFilters]);
1641
1779
  standAccess.setAdmission(stand, (tenantId) => TenantAdmissions.require(tenantIndex, this.#specSnapshot.name.value, this.#specSnapshot.multitenant, tenantId));
1642
1780
  return stand;
1643
1781
  }
1644
- #createContext(repositories, storageFactory, buses, running, ai) {
1645
- return ContextParts.createBoundedContext(this.#specSnapshot, storageFactory, repositories, ServerEnvironment.instance().delivery.strategy, buses, running, ai, this.#tenantIndex ?? ServerEnvironment.instance().tenantIndex);
1782
+ /**
1783
+ * Constructs the immutable BoundedContext using assembled buses, runtime parts, environment
1784
+ * delivery strategy, and tenant setting.
1785
+ *
1786
+ * @param repositories Registered repository views.
1787
+ * @param storageFactory Provider for business records.
1788
+ * @param buses Assembled signal buses.
1789
+ * @param running Prepared Stand and subscription runtime.
1790
+ * @param tenantIndex Captured marker or directory.
1791
+ * @param ai Selected AI registry, when configured.
1792
+ * @returns Immutable Bounded Context.
1793
+ */
1794
+ #createContext(repositories, storageFactory, buses, running, tenantIndex, ai) {
1795
+ return ContextParts.createBoundedContext(this.#specSnapshot, storageFactory, repositories, ServerEnvironment.instance().delivery.strategy, buses, running, ai, tenantIndex);
1646
1796
  }
1797
+ /**
1798
+ * Closes partial context resources after assembly fails and aggregates cleanup errors with the original failure.
1799
+ */
1647
1800
  async #failBuild(resources, error) {
1648
1801
  const cleanupErrors = [];
1649
1802
  if (resources.context !== undefined) {
@@ -1675,28 +1828,27 @@ export class BoundedContextBuilder {
1675
1828
  }
1676
1829
  throw error;
1677
1830
  }
1678
- async #loadGeneratedArtifacts(entityTypes) {
1831
+ /**
1832
+ * Selects generated metadata for configured Entity and standalone handler types and creates
1833
+ * repository views and handler groups.
1834
+ */
1835
+ #loadGeneratedArtifacts(entityTypes) {
1679
1836
  if (entityTypes.length === 0 && this.#standaloneInstances().length === 0) {
1680
1837
  return Object.freeze({ repositories: Object.freeze([]), standalone: Object.freeze([]) });
1681
1838
  }
1682
- const root = ContextParts.requireGeneratedRegistryRoot(this.#generatedRegistryRoot);
1683
- const discovery = new GeneratedRegistryDiscovery();
1684
- const registryModule = await ContextParts.trustedGeneratedRegistryModule(root);
1685
- const registryKey = registryModule.href;
1686
- const registries = (await discovery
1687
- .load({
1688
- modules: [registryModule],
1689
- ...ContextParts.generatedRegistryCacheBust(registryKey),
1690
- })
1691
- .catch((error) => {
1692
- ContextParts.recordGeneratedRegistryFailure(registryKey);
1693
- throw error;
1694
- }));
1839
+ const selected = [
1840
+ ...entityTypes,
1841
+ ...this.#standaloneInstances().map((instance) => instance.constructor),
1842
+ ];
1843
+ const registries = [SpineMetadataInventory.select(selected)];
1695
1844
  const metadata = ContextParts.ingestGeneratedRegistries(registries);
1696
1845
  const repositories = Object.freeze(entityTypes.map((entityType) => ContextParts.createGeneratedRepository(entityType, registries, metadata, this.#generatedRepositoryOptions.get(entityType))));
1697
1846
  const standalone = Object.freeze(registries.flatMap((registry) => registry.receivers.filter((receiver) => receiver.receiverKind === "standalone")));
1698
1847
  return Object.freeze({ repositories, standalone });
1699
1848
  }
1849
+ /**
1850
+ * Returns a frozen list of configured standalone assignee, commander, and event-receiver instances.
1851
+ */
1700
1852
  #standaloneInstances() {
1701
1853
  return Object.freeze([...this.#assignees, ...this.#commanders, ...this.#eventReceivers]);
1702
1854
  }
@@ -1775,28 +1927,28 @@ export class ContextSpec {
1775
1927
  }
1776
1928
  const registeredRepositories = new WeakMap();
1777
1929
  /**
1778
- * Reports failure while redispatching a stored event during read-side replay.
1930
+ * Reports failure while dispatching a stored Event during read-side catch-up.
1779
1931
  */
1780
- class CatchUpReplayError extends Error {
1781
- code = "READ_SIDE_CATCH_UP_REPLAY_FAILED";
1932
+ class CatchUpDispatchError extends Error {
1933
+ code = "READ_SIDE_CATCH_UP_DISPATCH_FAILED";
1782
1934
  eventId;
1783
1935
  detail;
1784
1936
  /**
1785
- * Creates a replay error tied to the stored event that failed.
1937
+ * Creates a catch-up error tied to the stored Event that failed.
1786
1938
  *
1787
1939
  * @param eventId Identifies the stored event.
1788
1940
  * @param detail Contains the bounded cause name and message.
1789
1941
  */
1790
1942
  constructor(eventId, detail) {
1791
1943
  super(`Read-side catch-up failed for stored event "${eventId}".`);
1792
- this.name = "ReadCatchUpReplayError";
1944
+ this.name = "ReadSideCatchUpDispatchError";
1793
1945
  this.eventId = eventId;
1794
1946
  this.detail = detail;
1795
1947
  Object.setPrototypeOf(this, new.target.prototype);
1796
1948
  }
1797
1949
  }
1798
1950
  /**
1799
- * Assembles private bounded-context lifecycle and replay details.
1951
+ * Assembles private bounded-context lifecycle and catch-up details.
1800
1952
  */
1801
1953
  const ContextParts = Object.freeze({
1802
1954
  /**
@@ -1941,11 +2093,11 @@ const ContextParts = Object.freeze({
1941
2093
  * @param buses Prepared domain and System buses.
1942
2094
  * @param running Prepared Stand and subscription runtime.
1943
2095
  * @param ai Effective AI registry when Agent repositories are present.
1944
- * @param tenantIndex Effective live membership index.
2096
+ * @param tenantIndex Captured marker or directory.
1945
2097
  * @returns Constructed bounded context.
1946
2098
  */
1947
2099
  createBoundedContext(specSnapshot, storageFactory, repositories, deliveryStrategy, buses, running, ai, tenantIndex) {
1948
- return constructBoundedContext({
2100
+ const context = constructBoundedContext({
1949
2101
  snapshot: ContextParts.snapshotFor(specSnapshot),
1950
2102
  commandBus: buses.commandBus,
1951
2103
  eventBus: buses.eventBus,
@@ -1956,12 +2108,14 @@ const ContextParts = Object.freeze({
1956
2108
  subscriptionRuntime: running.runtime,
1957
2109
  systemSpec: buses.systemSpec,
1958
2110
  storageFactory,
1959
- tenantIndex: tenantIndex ?? ServerEnvironment.instance().tenantIndex,
2111
+ tenantIndex,
1960
2112
  repositories,
1961
2113
  deliveryStrategy,
1962
2114
  ...(ai === undefined ? {} : { ai }),
1963
2115
  token: frameworkConstructionToken,
1964
2116
  });
2117
+ standAccess.bindProducingContext(running.stand, context);
2118
+ return context;
1965
2119
  },
1966
2120
  /**
1967
2121
  * Captures public context identity from the build specification.
@@ -2229,7 +2383,7 @@ const ContextParts = Object.freeze({
2229
2383
  * @param event Imported event to validate.
2230
2384
  */
2231
2385
  validateImportedTenant(context, event) {
2232
- const tenantId = ContextParts.readReplayTenant(event);
2386
+ const tenantId = ContextParts.readCatchUpTenant(event);
2233
2387
  if (!context.isMultitenant) {
2234
2388
  if (tenantId !== undefined) {
2235
2389
  throw new Error(`Single-tenant context "${context.name.value}" does not accept tenantId.`);
@@ -2284,10 +2438,10 @@ const ContextParts = Object.freeze({
2284
2438
  return pairing;
2285
2439
  },
2286
2440
  /**
2287
- * Reads the tenant index recorded for a built context.
2441
+ * Reads the marker or directory recorded for a built Bounded Context.
2288
2442
  *
2289
- * @param context Context to inspect.
2290
- * @returns Tenant index; throws if unavailable.
2443
+ * @param context Bounded Context to inspect.
2444
+ * @returns Captured marker or directory; throws if unavailable.
2291
2445
  */
2292
2446
  requireTenantIndex(context) {
2293
2447
  const tenantIndex = contextTenantIndexes.get(context);
@@ -2318,151 +2472,6 @@ const ContextParts = Object.freeze({
2318
2472
  throw new TypeError(`${operation} requires a Repository instance. Use an entity class only with build().`);
2319
2473
  }
2320
2474
  },
2321
- /**
2322
- * Validates that a root exists when generated metadata must be loaded.
2323
- *
2324
- * @param root Configured registry location.
2325
- * @returns Configured root; throws when absent.
2326
- */
2327
- requireGeneratedRegistryRoot(root) {
2328
- if (root !== undefined) {
2329
- return root;
2330
- }
2331
- throw new Error("BoundedContextBuilder.build() requires withGeneratedRegistryRoot(root) " +
2332
- "when assembling entity classes from generated metadata.");
2333
- },
2334
- /**
2335
- * Resolves the generated registry module and verifies it stays beneath the configured root.
2336
- *
2337
- * @param root Configured filesystem path or file URL.
2338
- * @returns Registry module file URL.
2339
- */
2340
- async trustedGeneratedRegistryModule(root) {
2341
- const trustedRoot = await ContextParts.canonicalGeneratedRegistryRoot(root);
2342
- const registryPath = resolve(trustedRoot, generatedRegistryFile);
2343
- const canonicalRegistryPath = await ContextParts.canonicalReadableRegistryPath(registryPath);
2344
- if (ContextParts.resolvesOutsideRoot(trustedRoot, canonicalRegistryPath)) {
2345
- throw new Error(`Generated handler registry module "${canonicalRegistryPath}" must resolve within ` +
2346
- `the configured generated registry root "${trustedRoot}".`);
2347
- }
2348
- return pathToFileURL(canonicalRegistryPath);
2349
- },
2350
- /**
2351
- * Resolves a generated registry root to its canonical filesystem path.
2352
- *
2353
- * @param root Configured path or file URL.
2354
- * @returns Canonical directory path.
2355
- */
2356
- async canonicalGeneratedRegistryRoot(root) {
2357
- const rootPath = ContextParts.generatedRegistryRootPath(root);
2358
- try {
2359
- return await realpath(rootPath);
2360
- }
2361
- catch (error) {
2362
- throw new Error(`Generated registry root "${rootPath}" must be an existing readable directory.`, { cause: error });
2363
- }
2364
- },
2365
- /**
2366
- * Converts a registry root path or file URL to an absolute filesystem path.
2367
- *
2368
- * @param root Configured path or file URL.
2369
- * @returns Absolute filesystem path.
2370
- */
2371
- generatedRegistryRootPath(root) {
2372
- if (root instanceof URL) {
2373
- return ContextParts.fileUrlPath(root, "Generated registry root");
2374
- }
2375
- if (ContextParts.isUrlLike(root)) {
2376
- return ContextParts.fileUrlPath(ContextParts.parseRootUrl(root), "Generated registry root");
2377
- }
2378
- return resolve(root);
2379
- },
2380
- /**
2381
- * Validates a file URL and converts it to an absolute filesystem path.
2382
- *
2383
- * @param url File URL to convert.
2384
- * @param label Label used in validation errors.
2385
- * @returns Filesystem path.
2386
- */
2387
- fileUrlPath(url, label) {
2388
- if (url.protocol !== "file:") {
2389
- throw new Error(`${label} "${url.href}" must use the file: URL scheme.`);
2390
- }
2391
- if (url.search.length > 0 || url.hash.length > 0) {
2392
- throw new Error(`${label} "${url.href}" must not include a query or hash.`);
2393
- }
2394
- return resolve(fileURLToPath(url));
2395
- },
2396
- /**
2397
- * Parses a generated registry root string as a URL.
2398
- *
2399
- * @param root Registry root string.
2400
- * @returns Parsed URL.
2401
- */
2402
- parseRootUrl(root) {
2403
- try {
2404
- return new URL(root);
2405
- }
2406
- catch (error) {
2407
- throw new Error(`Generated registry root "${root}" is not a valid URL.`, { cause: error });
2408
- }
2409
- },
2410
- /**
2411
- * Checks strings for a URL scheme, excluding Windows drive paths.
2412
- *
2413
- * @param value String to inspect.
2414
- * @returns True when the string has a URL scheme.
2415
- */
2416
- isUrlLike(value) {
2417
- return moduleSchemeRe.test(value) && !/^[A-Za-z]:[\\/]/.test(value);
2418
- },
2419
- /**
2420
- * Verifies registry-module readability and resolves its canonical path.
2421
- *
2422
- * @param registryPath Registry module filesystem path.
2423
- * @returns Canonical readable path.
2424
- */
2425
- async canonicalReadableRegistryPath(registryPath) {
2426
- try {
2427
- await access(registryPath, fsConstants.R_OK);
2428
- return await realpath(registryPath);
2429
- }
2430
- catch (error) {
2431
- throw new Error(`Generated handler registry module "${registryPath}" must exist and be readable.`, { cause: error });
2432
- }
2433
- },
2434
- /**
2435
- * Checks whether a canonical path escapes a canonical root.
2436
- *
2437
- * @param canonicalRoot Root directory.
2438
- * @param canonicalPath Candidate path.
2439
- * @returns True when the candidate escapes the root.
2440
- */
2441
- resolvesOutsideRoot(canonicalRoot, canonicalPath) {
2442
- const relativePath = relative(canonicalRoot, canonicalPath);
2443
- return (relativePath.startsWith("..") ||
2444
- relativePath === ".." ||
2445
- relativePath.split(sep).includes("..") ||
2446
- isAbsolute(relativePath));
2447
- },
2448
- /**
2449
- * Creates a cache-bust query after a prior registry load failure.
2450
- *
2451
- * @param registryKey Canonical registry identifier.
2452
- * @returns Retry query, or an empty object on the first attempt.
2453
- */
2454
- generatedRegistryCacheBust(registryKey) {
2455
- const attempt = generatedRegistryLoadAttempts.get(registryKey) ?? 0;
2456
- return attempt === 0 ? {} : { cacheBust: `retry-${attempt.toString()}` };
2457
- },
2458
- /**
2459
- * Records another failed load for a registry.
2460
- *
2461
- * @param registryKey Canonical registry identifier.
2462
- */
2463
- recordGeneratedRegistryFailure(registryKey) {
2464
- generatedRegistryLoadAttempts.set(registryKey, (generatedRegistryLoadAttempts.get(registryKey) ?? 0) + 1);
2465
- },
2466
2475
  /**
2467
2476
  * Loads generated handler registries into a metadata registry.
2468
2477
  *
@@ -2758,9 +2767,9 @@ const ContextParts = Object.freeze({
2758
2767
  createDeliveryDescriptor(entityInbox, projections) {
2759
2768
  return Object.freeze({
2760
2769
  endpoints: () => Object.freeze([...entityInbox.endpoints(), ...projections.endpoints()]),
2761
- replay: (message, tenantId) => message.label === "UPDATE_SUBSCRIBER"
2762
- ? projections.replay(message, tenantId)
2763
- : entityInbox.replay(message, tenantId),
2770
+ deliver: (message, tenantId) => message.label === "UPDATE_SUBSCRIBER"
2771
+ ? projections.deliver(message, tenantId)
2772
+ : entityInbox.deliver(message, tenantId),
2764
2773
  });
2765
2774
  },
2766
2775
  /**
@@ -3031,12 +3040,12 @@ const ContextParts = Object.freeze({
3031
3040
  * @param context Catch-up storage context.
3032
3041
  * @param event Stored event to validate.
3033
3042
  */
3034
- validateReplayTenant(context, event) {
3043
+ validateCatchUpTenant(context, event) {
3035
3044
  if (!context.multitenant) {
3036
3045
  return;
3037
3046
  }
3038
3047
  const expectedTenantId = context.tenantId;
3039
- const envelopeTenantId = ContextParts.readReplayTenant(event);
3048
+ const envelopeTenantId = ContextParts.readCatchUpTenant(event);
3040
3049
  if (envelopeTenantId === undefined) {
3041
3050
  throw new Error("Read-side catch-up requires stored event envelope tenant.");
3042
3051
  }
@@ -3050,7 +3059,7 @@ const ContextParts = Object.freeze({
3050
3059
  * @param event Event whose origin is inspected.
3051
3060
  * @returns Cloned tenant ID when present.
3052
3061
  */
3053
- readReplayTenant(event) {
3062
+ readCatchUpTenant(event) {
3054
3063
  switch (event.context?.origin.case) {
3055
3064
  case "importContext":
3056
3065
  return ContextParts.tenantIdValue(event.context.origin.value.tenantId);
@@ -3092,14 +3101,14 @@ const ContextParts = Object.freeze({
3092
3101
  return matching.length > 0 ? 1 : 0;
3093
3102
  },
3094
3103
  /**
3095
- * Creates a replay error with the event ID and bounded cause details.
3104
+ * Creates a catch-up error with the Event ID and bounded cause details.
3096
3105
  *
3097
3106
  * @param event Event that failed.
3098
- * @param cause Thrown replay cause.
3099
- * @returns Replay error.
3107
+ * @param cause Thrown catch-up cause.
3108
+ * @returns Catch-up error.
3100
3109
  */
3101
- catchUpReplayError(event, cause) {
3102
- return new CatchUpReplayError(event.id?.value ?? "(missing)", ContextParts.catchUpReplayDetail(cause));
3110
+ catchUpDispatchError(event, cause) {
3111
+ return new CatchUpDispatchError(event.id?.value ?? "(missing)", ContextParts.catchUpDispatchDetail(cause));
3103
3112
  },
3104
3113
  /**
3105
3114
  * Returns an error string truncated to a maximum length.
@@ -3117,7 +3126,7 @@ const ContextParts = Object.freeze({
3117
3126
  * @param error Thrown value.
3118
3127
  * @returns Immutable bounded details.
3119
3128
  */
3120
- catchUpReplayDetail(error) {
3129
+ catchUpDispatchDetail(error) {
3121
3130
  if (error instanceof Error) {
3122
3131
  return Object.freeze({
3123
3132
  name: ContextParts.boundedErrorString(error.name, errorDetailLimit) || "Error",
@@ -3160,9 +3169,15 @@ const ContextParts = Object.freeze({
3160
3169
  * @internal
3161
3170
  */
3162
3171
  export const boundedContextIntegrationAccess = Object.freeze({
3172
+ /**
3173
+ * Publishes an imported Event through the context’s import path.
3174
+ */
3163
3175
  publishImported(context, event) {
3164
3176
  return ContextParts.publishImported(context, event);
3165
3177
  },
3178
+ /**
3179
+ * Signals that standalone context integration is ready.
3180
+ */
3166
3181
  ready(context) {
3167
3182
  return ContextParts.integrationReady(context);
3168
3183
  },