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

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 (150) hide show
  1. package/README.md +49 -9
  2. package/REFERENCE.md +339 -22
  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-read-runtime.d.ts +12 -1
  8. package/dist/agent/agent-live-read-runtime.d.ts.map +1 -1
  9. package/dist/agent/agent-live-read-runtime.js +5 -4
  10. package/dist/agent/agent-live-read-runtime.js.map +1 -1
  11. package/dist/browser/browser-server.d.ts +163 -0
  12. package/dist/browser/browser-server.d.ts.map +1 -1
  13. package/dist/browser/browser-server.js +579 -188
  14. package/dist/browser/browser-server.js.map +1 -1
  15. package/dist/browser/durable-subscription-bindings.d.ts +5 -0
  16. package/dist/browser/durable-subscription-bindings.d.ts.map +1 -1
  17. package/dist/browser/durable-subscription-bindings.js +226 -38
  18. package/dist/browser/durable-subscription-bindings.js.map +1 -1
  19. package/dist/browser/options.d.ts +5 -0
  20. package/dist/browser/options.d.ts.map +1 -1
  21. package/dist/bus/command-bus.d.ts +3 -0
  22. package/dist/bus/command-bus.d.ts.map +1 -1
  23. package/dist/bus/command-bus.js +69 -9
  24. package/dist/bus/command-bus.js.map +1 -1
  25. package/dist/bus/command-errors.d.ts +20 -4
  26. package/dist/bus/command-errors.d.ts.map +1 -1
  27. package/dist/bus/command-errors.js +47 -7
  28. package/dist/bus/command-errors.js.map +1 -1
  29. package/dist/bus/event-bus.d.ts +3 -0
  30. package/dist/bus/event-bus.d.ts.map +1 -1
  31. package/dist/bus/event-bus.js +130 -8
  32. package/dist/bus/event-bus.js.map +1 -1
  33. package/dist/context/bounded-context.d.ts +29 -11
  34. package/dist/context/bounded-context.d.ts.map +1 -1
  35. package/dist/context/bounded-context.js +147 -186
  36. package/dist/context/bounded-context.js.map +1 -1
  37. package/dist/entity/entity.d.ts +59 -0
  38. package/dist/entity/entity.d.ts.map +1 -1
  39. package/dist/entity/entity.js +60 -0
  40. package/dist/entity/entity.js.map +1 -1
  41. package/dist/handler/app-query-handler.d.ts +60 -0
  42. package/dist/handler/app-query-handler.d.ts.map +1 -0
  43. package/dist/handler/app-query-handler.js +23 -0
  44. package/dist/handler/app-query-handler.js.map +1 -0
  45. package/dist/handler/generated-registry-discovery.d.ts +6 -0
  46. package/dist/handler/generated-registry-discovery.d.ts.map +1 -1
  47. package/dist/handler/generated-registry-discovery.js +71 -2
  48. package/dist/handler/generated-registry-discovery.js.map +1 -1
  49. package/dist/handler/handler-decorators.d.ts +8 -29
  50. package/dist/handler/handler-decorators.d.ts.map +1 -1
  51. package/dist/handler/handler-decorators.js +33 -210
  52. package/dist/handler/handler-decorators.js.map +1 -1
  53. package/dist/handler/spine-metadata-inventory.d.ts +99 -0
  54. package/dist/handler/spine-metadata-inventory.d.ts.map +1 -0
  55. package/dist/handler/spine-metadata-inventory.js +162 -0
  56. package/dist/handler/spine-metadata-inventory.js.map +1 -0
  57. package/dist/index.d.ts +5 -3
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +4 -2
  60. package/dist/index.js.map +1 -1
  61. package/dist/repository/repository.d.ts +44 -1
  62. package/dist/repository/repository.d.ts.map +1 -1
  63. package/dist/repository/repository.js +368 -88
  64. package/dist/repository/repository.js.map +1 -1
  65. package/dist/server/managed-server-application.d.ts +12 -11
  66. package/dist/server/managed-server-application.d.ts.map +1 -1
  67. package/dist/server/managed-server-application.js +41 -25
  68. package/dist/server/managed-server-application.js.map +1 -1
  69. package/dist/server/node-coordinator.d.ts +18 -5
  70. package/dist/server/node-coordinator.d.ts.map +1 -1
  71. package/dist/server/node-coordinator.js +554 -84
  72. package/dist/server/node-coordinator.js.map +1 -1
  73. package/dist/server/reserved-spine-rpc-paths.json +1 -0
  74. package/dist/server/server-environment.d.ts +17 -0
  75. package/dist/server/server-environment.d.ts.map +1 -1
  76. package/dist/server/server-environment.js +77 -16
  77. package/dist/server/server-environment.js.map +1 -1
  78. package/dist/server/server.d.ts +45 -0
  79. package/dist/server/server.d.ts.map +1 -1
  80. package/dist/server/server.js +111 -3
  81. package/dist/server/server.js.map +1 -1
  82. package/dist/services/app-query-execution.d.ts +53 -0
  83. package/dist/services/app-query-execution.d.ts.map +1 -0
  84. package/dist/services/app-query-execution.js +408 -0
  85. package/dist/services/app-query-execution.js.map +1 -0
  86. package/dist/services/query-reader.d.ts +32 -7
  87. package/dist/services/query-reader.d.ts.map +1 -1
  88. package/dist/services/query-reader.js +22 -2
  89. package/dist/services/query-reader.js.map +1 -1
  90. package/dist/services/read-request-status.d.ts +65 -0
  91. package/dist/services/read-request-status.d.ts.map +1 -0
  92. package/dist/services/read-request-status.js +151 -0
  93. package/dist/services/read-request-status.js.map +1 -0
  94. package/dist/services/spine-services.d.ts +36 -7
  95. package/dist/services/spine-services.d.ts.map +1 -1
  96. package/dist/services/spine-services.js +900 -168
  97. package/dist/services/spine-services.js.map +1 -1
  98. package/dist/services/trusted-reader-ingress.d.ts +92 -0
  99. package/dist/services/trusted-reader-ingress.d.ts.map +1 -0
  100. package/dist/services/trusted-reader-ingress.js +89 -0
  101. package/dist/services/trusted-reader-ingress.js.map +1 -0
  102. package/dist/spi/handler-registry.d.ts +2 -0
  103. package/dist/spi/handler-registry.d.ts.map +1 -1
  104. package/dist/spi/handler-registry.js +1 -1
  105. package/dist/spi/handler-registry.js.map +1 -1
  106. package/dist/stand/read-side-filter-engine.d.ts +46 -0
  107. package/dist/stand/read-side-filter-engine.d.ts.map +1 -0
  108. package/dist/stand/read-side-filter-engine.js +356 -0
  109. package/dist/stand/read-side-filter-engine.js.map +1 -0
  110. package/dist/stand/read-side-filter.d.ts +247 -0
  111. package/dist/stand/read-side-filter.d.ts.map +1 -0
  112. package/dist/stand/read-side-filter.js +39 -0
  113. package/dist/stand/read-side-filter.js.map +1 -0
  114. package/dist/stand/read-side-message.d.ts +66 -0
  115. package/dist/stand/read-side-message.d.ts.map +1 -0
  116. package/dist/stand/read-side-message.js +167 -0
  117. package/dist/stand/read-side-message.js.map +1 -0
  118. package/dist/stand/stand-direct-delivery.d.ts +161 -0
  119. package/dist/stand/stand-direct-delivery.d.ts.map +1 -0
  120. package/dist/stand/stand-direct-delivery.js +210 -0
  121. package/dist/stand/stand-direct-delivery.js.map +1 -0
  122. package/dist/stand/stand.d.ts +80 -42
  123. package/dist/stand/stand.d.ts.map +1 -1
  124. package/dist/stand/stand.js +410 -54
  125. package/dist/stand/stand.js.map +1 -1
  126. package/dist/stand/subscription-observer.d.ts.map +1 -1
  127. package/dist/stand/subscription-observer.js +103 -0
  128. package/dist/stand/subscription-observer.js.map +1 -1
  129. package/dist/stand/subscription-read-filters.d.ts +40 -0
  130. package/dist/stand/subscription-read-filters.d.ts.map +1 -0
  131. package/dist/stand/subscription-read-filters.js +91 -0
  132. package/dist/stand/subscription-read-filters.js.map +1 -0
  133. package/dist/stand/subscription-reader-binding.d.ts +20 -0
  134. package/dist/stand/subscription-reader-binding.d.ts.map +1 -0
  135. package/dist/stand/subscription-reader-binding.js +56 -0
  136. package/dist/stand/subscription-reader-binding.js.map +1 -0
  137. package/dist/stand/subscription-records.d.ts +30 -3
  138. package/dist/stand/subscription-records.d.ts.map +1 -1
  139. package/dist/stand/subscription-records.js +61 -16
  140. package/dist/stand/subscription-records.js.map +1 -1
  141. package/dist/stand/subscription-registry.d.ts +49 -14
  142. package/dist/stand/subscription-registry.d.ts.map +1 -1
  143. package/dist/stand/subscription-registry.js +150 -31
  144. package/dist/stand/subscription-registry.js.map +1 -1
  145. package/dist/stand/subscription-runtime.d.ts +7 -2
  146. package/dist/stand/subscription-runtime.d.ts.map +1 -1
  147. package/dist/stand/subscription-runtime.js +131 -25
  148. package/dist/stand/subscription-runtime.js.map +1 -1
  149. package/dist/tsconfig.tsbuildinfo +1 -1
  150. package/package.json +14 -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";
@@ -31,15 +27,16 @@ import { TenantAdmissions } from "./tenant-unknown-error.js";
31
27
  import { AgentExecutionCapacity, } from "../agent/agent-execution-capacity.js";
32
28
  import { AgentAcceptanceReceipts, agentAcceptanceReceiptGroup, agentAcceptanceReceiptSpec, } from "../agent/agent-acceptance-receipts.js";
33
29
  import { Repository, repositoryAccess, } from "../repository/repository.js";
34
- import { GeneratedRegistryDiscovery } from "../handler/generated-registry-discovery.js";
30
+ import { SpineMetadataInventory } from "../handler/spine-metadata-inventory.js";
35
31
  import { HandlerRegistryIngestor, } from "../handler/generated-handler-registry.js";
36
32
  import { AbstractAssignee, AbstractCommander, AbstractEventReactor, AbstractEventSubscriber, } from "../handler/standalone.js";
37
33
  import { HandlerMetadataRegistry, } from "../handler/handler-metadata.js";
38
34
  import { SignalMetadata } from "../runtime/signal-metadata.js";
39
35
  import { runtimeAccess } from "../runtime/runtime.js";
40
36
  import { Stand, standAccess } from "../stand/stand.js";
37
+ import { ReadSideFilter } from "../stand/read-side-filter.js";
41
38
  import { SubscriptionRuntime, subscriptionRuntimeAccess } from "../stand/subscription-runtime.js";
42
- import { StorageSubscriptionRegistry, subscriptionRecordSpec, } from "../stand/subscription-registry.js";
39
+ import { StorageSubscriptionRegistry, subscriptionRecordGroup, } from "../stand/subscription-registry.js";
43
40
  import { IntegrationBroker } from "../integration/integration-broker.js";
44
41
  import { ServerEnvironment, serverEnvironmentAccess } from "../server/server-environment.js";
45
42
  import { SignalPublisher } from "../runtime/signal-publisher.js";
@@ -100,11 +97,8 @@ export class DuplicateEntityStateException extends Error {
100
97
  const frameworkConstructionToken = Object.freeze({
101
98
  frameworkConstructionToken: true,
102
99
  });
103
- const generatedRegistryFile = "generated/handler/generated-handler-registry.js";
104
100
  const errorDetailLimit = 500;
105
- const moduleSchemeRe = /^[A-Za-z][A-Za-z\d+.-]*:/;
106
101
  const internalStoragePrefix = "__spine/";
107
- const generatedRegistryLoadAttempts = new Map();
108
102
  const eventSubscribers = new WeakMap();
109
103
  const contextSystemPairings = new WeakMap();
110
104
  const contextTenantIndexes = new WeakMap();
@@ -265,11 +259,12 @@ export class BoundedContext {
265
259
  * @returns Shared preparation completion.
266
260
  */
267
261
  #prepareShared(mode) {
268
- if (!(this.#subscriptionRuntime.registry() instanceof StorageSubscriptionRegistry))
262
+ const registry = this.#subscriptionRuntime.registry();
263
+ if (!(registry instanceof StorageSubscriptionRegistry))
269
264
  return Promise.resolve();
270
265
  if (this.#sharedPreparation !== undefined)
271
266
  return this.#sharedPreparation;
272
- const attempt = this.#trackPreparation(() => StoragePreparations.for(this.#storageFactory).prepareRecord(ContextParts.createSubscriptionStorageContext(this.#snapshot.spec), subscriptionRecordSpec, undefined, mode));
267
+ const attempt = this.#trackPreparation(() => registry.prepare(mode));
273
268
  this.#sharedPreparation = attempt;
274
269
  void attempt.catch(() => {
275
270
  if (this.#sharedPreparation === attempt)
@@ -527,6 +522,10 @@ export class BoundedContext {
527
522
  });
528
523
  contextDeliveryDescriptors.set(this, ContextParts.createDeliveryDescriptor(this.#entityInbox, this.#projectionInbox));
529
524
  }
525
+ /**
526
+ * Prepares repository registrations, reserves their state types, and commits their Stand and
527
+ * inbox targets; rolls back uncommitted storage preparation on failure.
528
+ */
530
529
  #registerRepositories(repositories) {
531
530
  const preparedRepositories = this.#prepareRepositories(repositories);
532
531
  try {
@@ -566,6 +565,10 @@ export class BoundedContext {
566
565
  throw new DuplicateEntityStateException(stateType, prior.name.value, this.name.value);
567
566
  }
568
567
  }
568
+ /**
569
+ * Builds prepared repository registrations using this context’s storage and delivery services,
570
+ * cleaning earlier preparations if a later one fails.
571
+ */
569
572
  #prepareRepositories(repositories) {
570
573
  const registration = this.#repositoryRegistration();
571
574
  const preparedRepositories = [];
@@ -641,6 +644,10 @@ export class BoundedContext {
641
644
  ...(tenantId === undefined ? {} : { tenantId }),
642
645
  });
643
646
  }
647
+ /**
648
+ * Closes storage for repositories that were prepared but not committed, then rethrows the
649
+ * registration error with cleanup failures aggregated.
650
+ */
644
651
  #failRegistration(error, preparedRepositories) {
645
652
  const uncommitted = preparedRepositories.filter(({ repository }) => !this.#repositoryViews.has(repository));
646
653
  const closeErrors = ContextParts.closePreparedRepositories(uncommitted);
@@ -801,6 +808,10 @@ export class BoundedContext {
801
808
  async catchUpReadSide(options = {}) {
802
809
  return eventBusAccess.runExclusive(this.#eventBus, () => this.#catchUpReadSideOnce(options));
803
810
  }
811
+ /**
812
+ * Clears registered Projection state for the selected tenant slice, reads stored events,
813
+ * validates each event’s tenant, and dispatches replay sequentially.
814
+ */
804
815
  async #catchUpReadSideOnce(options) {
805
816
  const storageContext = ContextParts.catchUpStorageContext(this.#snapshot.spec, options);
806
817
  const tenantOptions = ContextParts.catchUpStandOptions(storageContext);
@@ -1013,6 +1024,9 @@ export const boundedContextAccess = Object.freeze({
1013
1024
  return Promise.reject(new TypeError("Service registry requires a built context."));
1014
1025
  return phase.trackReplay(onOperation);
1015
1026
  },
1027
+ /**
1028
+ * Registers replay work with the context close lifecycle so shutdown waits for it.
1029
+ */
1016
1030
  trackReplay(context, onReplay) {
1017
1031
  const phase = contextClosePhases.get(context);
1018
1032
  if (phase === undefined)
@@ -1035,18 +1049,27 @@ export const boundedContextAccess = Object.freeze({
1035
1049
  ? issued.repository
1036
1050
  : undefined;
1037
1051
  },
1052
+ /**
1053
+ * Stops new work at this Bounded Context and begins shutdown of its runtime facilities.
1054
+ */
1038
1055
  beginClose(context) {
1039
1056
  const phase = contextClosePhases.get(context);
1040
1057
  if (phase === undefined)
1041
1058
  throw new TypeError("Close phase requires a built BoundedContext.");
1042
1059
  phase.begin();
1043
1060
  },
1061
+ /**
1062
+ * Waits for accepted runtime work and replay operations to finish.
1063
+ */
1044
1064
  drainWork(context) {
1045
1065
  const phase = contextClosePhases.get(context);
1046
1066
  if (phase === undefined)
1047
1067
  return Promise.reject(new TypeError("Drain requires a built BoundedContext."));
1048
1068
  return phase.drain();
1049
1069
  },
1070
+ /**
1071
+ * Installs the context logger on both EventBuses, the signal publisher, and subscription runtime.
1072
+ */
1050
1073
  installLogger(context, logger) {
1051
1074
  if (!contextStorageFactories.has(context)) {
1052
1075
  throw new TypeError("Context logger requires a built BoundedContext instance.");
@@ -1068,6 +1091,9 @@ export const boundedContextAccess = Object.freeze({
1068
1091
  }
1069
1092
  subscriptionRuntimeAccess.installLogger(runtime, logger, context.name.value);
1070
1093
  },
1094
+ /**
1095
+ * Returns the diagnostic logger installed on this Bounded Context.
1096
+ */
1071
1097
  loggerFor(context) {
1072
1098
  const logger = contextLoggers.get(context);
1073
1099
  if (logger === undefined) {
@@ -1075,11 +1101,17 @@ export const boundedContextAccess = Object.freeze({
1075
1101
  }
1076
1102
  return logger;
1077
1103
  },
1104
+ /**
1105
+ * Checks whether a value is a BoundedContextBuilder created by this module.
1106
+ */
1078
1107
  isBuilder(value) {
1079
1108
  return (typeof value === "object" &&
1080
1109
  value !== null &&
1081
1110
  builderBuilds.has(value));
1082
1111
  },
1112
+ /**
1113
+ * Builds the supplied context builder and completes its configured initialization.
1114
+ */
1083
1115
  build(builder) {
1084
1116
  const build = builderBuilds.get(builder);
1085
1117
  if (build === undefined) {
@@ -1087,6 +1119,9 @@ export const boundedContextAccess = Object.freeze({
1087
1119
  }
1088
1120
  return build();
1089
1121
  },
1122
+ /**
1123
+ * Attaches a subscriber to the context domain EventBus for the requested event type.
1124
+ */
1090
1125
  subscribeToEvent(context, typeUrl, subscriber) {
1091
1126
  const subscribe = eventSubscribers.get(context);
1092
1127
  if (subscribe === undefined) {
@@ -1094,6 +1129,9 @@ export const boundedContextAccess = Object.freeze({
1094
1129
  }
1095
1130
  return subscribe(typeUrl, subscriber);
1096
1131
  },
1132
+ /**
1133
+ * Posts a framework System Event through the context System EventBus.
1134
+ */
1097
1135
  postSystemEvent(context, event) {
1098
1136
  const post = systemEventPosters.get(context);
1099
1137
  if (post === undefined) {
@@ -1101,12 +1139,18 @@ export const boundedContextAccess = Object.freeze({
1101
1139
  }
1102
1140
  return post(event);
1103
1141
  },
1142
+ /**
1143
+ * Posts an external domain Event through the context EventBus.
1144
+ */
1104
1145
  postExternalEvent(context, event) {
1105
1146
  const imported = clone(EventSchema, event);
1106
1147
  imported.context = clone(EventContextSchema, imported.context ?? create(EventContextSchema));
1107
1148
  imported.context.external = true;
1108
1149
  return context.eventBus().post(imported);
1109
1150
  },
1151
+ /**
1152
+ * Subscribes to events produced by this context and forwards matching signals to the supplied observer.
1153
+ */
1110
1154
  observeProducedSignals(context, observer) {
1111
1155
  const publisher = contextSignalPublishers.get(context);
1112
1156
  if (publisher === undefined) {
@@ -1114,12 +1158,21 @@ export const boundedContextAccess = Object.freeze({
1114
1158
  }
1115
1159
  return publisher.observe(observer);
1116
1160
  },
1161
+ /**
1162
+ * Returns the domain/System specification pairing captured when the context was built.
1163
+ */
1117
1164
  systemPairing(context) {
1118
1165
  return ContextParts.cloneSystemPairing(ContextParts.requireSystemPairing(context));
1119
1166
  },
1167
+ /**
1168
+ * Returns the live tenant membership index configured for this context.
1169
+ */
1120
1170
  tenantIndex(context) {
1121
1171
  return ContextParts.requireTenantIndex(context);
1122
1172
  },
1173
+ /**
1174
+ * Returns the storage factory captured for repository and Stand operations in this context.
1175
+ */
1123
1176
  storageFactory(context) {
1124
1177
  const storageFactory = contextStorageFactories.get(context);
1125
1178
  if (storageFactory === undefined) {
@@ -1127,6 +1180,9 @@ export const boundedContextAccess = Object.freeze({
1127
1180
  }
1128
1181
  return storageFactory;
1129
1182
  },
1183
+ /**
1184
+ * Returns the persistent or in-memory registry used to store Stand subscription definitions.
1185
+ */
1130
1186
  subscriptionRegistry(context) {
1131
1187
  const runtime = contextSubscriptionRuntimes.get(context);
1132
1188
  if (runtime === undefined) {
@@ -1134,13 +1190,24 @@ export const boundedContextAccess = Object.freeze({
1134
1190
  }
1135
1191
  return runtime.registry();
1136
1192
  },
1137
- consumeSubscription(context, id, onUpdate) {
1193
+ /**
1194
+ * Consumes a registered Stand subscription update and returns its managed delivery handle.
1195
+ */
1196
+ consumeSubscription(context, id, onUpdate, onInvalidated, expected) {
1138
1197
  const runtime = contextSubscriptionRuntimes.get(context);
1139
1198
  if (runtime === undefined) {
1140
1199
  throw new TypeError("Subscription consumption requires a built BoundedContext instance.");
1141
1200
  }
1142
- return runtime.consume(id, onUpdate);
1201
+ return runtime.consume(id, (update) => {
1202
+ onUpdate({
1203
+ update,
1204
+ filter: (facts) => standAccess.filterSubscription(context.stand(), update, facts),
1205
+ });
1206
+ }, onInvalidated, expected);
1143
1207
  },
1208
+ /**
1209
+ * Returns the context delivery descriptor used to route Entity and Projection inbox work.
1210
+ */
1144
1211
  delivery(context) {
1145
1212
  const descriptor = contextDeliveryDescriptors.get(context);
1146
1213
  if (descriptor === undefined) {
@@ -1161,12 +1228,12 @@ export class BoundedContextBuilder {
1161
1228
  #eventReceivers = [];
1162
1229
  #repositories = new Set();
1163
1230
  #entityTypes = new Set();
1231
+ #readFilters = new Set();
1164
1232
  #generatedRepositoryOptions = new Map();
1165
1233
  #aiRegistry;
1166
1234
  #tenantIndex;
1167
1235
  #subscriptionRegistry;
1168
1236
  #persistSystemEvents = false;
1169
- #generatedRegistryRoot;
1170
1237
  /**
1171
1238
  * Registers the framework-only construction hook for this module.
1172
1239
  */
@@ -1217,6 +1284,15 @@ export class BoundedContextBuilder {
1217
1284
  isMultitenant() {
1218
1285
  return this.#specSnapshot.multitenant;
1219
1286
  }
1287
+ /**
1288
+ * Adds an outward read policy to this Bounded Context.
1289
+ * @param filter Policy applied before Server-level outward policies.
1290
+ * @returns This builder for further configuration.
1291
+ */
1292
+ addReadSideFilter(filter) {
1293
+ this.#readFilters.add(filter);
1294
+ return this;
1295
+ }
1220
1296
  /**
1221
1297
  * Adds an explicitly assembled repository or an Entity class.
1222
1298
  *
@@ -1365,16 +1441,6 @@ export class BoundedContextBuilder {
1365
1441
  this.#subscriptionRegistry = registry;
1366
1442
  return this;
1367
1443
  }
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
1444
  /**
1379
1445
  * Builds a context after loading generated handlers and preparing runtime resources.
1380
1446
  *
@@ -1384,6 +1450,10 @@ export class BoundedContextBuilder {
1384
1450
  build(options = {}) {
1385
1451
  return this.#buildAsyncWith(options);
1386
1452
  }
1453
+ /**
1454
+ * Loads generated handlers and repositories, assembles context facilities, and completes
1455
+ * requested initialization before publishing the context.
1456
+ */
1387
1457
  async #buildAsyncWith(options = {}) {
1388
1458
  const environment = ServerEnvironment.instance();
1389
1459
  const finishBuild = serverEnvironmentAccess.beginContextBuild(environment);
@@ -1415,7 +1485,7 @@ export class BoundedContextBuilder {
1415
1485
  * @returns The integrated context.
1416
1486
  */
1417
1487
  async #buildAsyncReady(options, environment) {
1418
- const generated = await this.#loadGeneratedArtifacts([...this.#entityTypes]);
1488
+ const generated = this.#loadGeneratedArtifacts([...this.#entityTypes]);
1419
1489
  serverEnvironmentAccess.requireAvailable(environment);
1420
1490
  const repositories = [...this.#repositories, ...generated.repositories];
1421
1491
  const context = await this.#buildWithResources(repositories, environment.storageFactory, generated.standalone, this.#aiRegistry ?? environment.ai);
@@ -1488,6 +1558,10 @@ export class BoundedContextBuilder {
1488
1558
  return await this.#failBuild(resources, error);
1489
1559
  }
1490
1560
  }
1561
+ /**
1562
+ * Assembles dispatchers, buses, Stand, subscription runtime, and integration hooks, registering
1563
+ * the completed context with its environment.
1564
+ */
1491
1565
  #assembleContext(repositories, storageFactory, standalone, resources, ai) {
1492
1566
  ContextParts.preflightRepositories(repositories);
1493
1567
  this.#preflightAgents(repositories, storageFactory, ai);
@@ -1540,6 +1614,9 @@ export class BoundedContextBuilder {
1540
1614
  throw new TypeError("Agent repository capabilities must be factory-created models.");
1541
1615
  }
1542
1616
  }
1617
+ /**
1618
+ * Collects repository and configured event recipients into domain and System dispatcher groups.
1619
+ */
1543
1620
  #buildDispatchers(repositories) {
1544
1621
  const event = [
1545
1622
  ...ContextParts.repositoryEventDispatchers(repositories),
@@ -1553,6 +1630,9 @@ export class BoundedContextBuilder {
1553
1630
  ],
1554
1631
  };
1555
1632
  }
1633
+ /**
1634
+ * Creates command, domain Event, and System Event buses plus their stores and signal publisher.
1635
+ */
1556
1636
  #buildBuses(repositories, storageFactory, dispatchers, resources) {
1557
1637
  const commandBus = new CommandBus([
1558
1638
  ...this.#commandDispatchers,
@@ -1577,6 +1657,9 @@ export class BoundedContextBuilder {
1577
1657
  systemSpec: system.spec,
1578
1658
  };
1579
1659
  }
1660
+ /**
1661
+ * Creates the System context store, EventBus, and Stand, then registers System event dispatchers.
1662
+ */
1580
1663
  #buildSystemBuses(storageFactory, dispatchers, resources) {
1581
1664
  const systemSpec = ContextParts.createSystemSpec(this.#specSnapshot, this.#persistSystemEvents);
1582
1665
  const systemEventStore = systemSpec.storesEvents
@@ -1600,6 +1683,10 @@ export class BoundedContextBuilder {
1600
1683
  spec: systemSpec,
1601
1684
  };
1602
1685
  }
1686
+ /**
1687
+ * Matches generated standalone handlers to configured instances and registers their dispatchers
1688
+ * on the appropriate buses.
1689
+ */
1603
1690
  #installStandalone(standalone, buses) {
1604
1691
  ContextParts.assertUniqueCommandReceptors(standalone);
1605
1692
  const runtime = standalone.length === 0
@@ -1616,11 +1703,14 @@ export class BoundedContextBuilder {
1616
1703
  buses.systemEventBus.register(state);
1617
1704
  return event;
1618
1705
  }
1706
+ /**
1707
+ * Creates the context Stand, subscription registry, and runtime that observes domain and System events.
1708
+ */
1619
1709
  #buildRuntime(storageFactory, buses, resources) {
1620
1710
  const stand = this.#buildStand(storageFactory);
1621
1711
  resources.stand = stand;
1622
1712
  const registry = resources.registry ??
1623
- new StorageSubscriptionRegistry(ContextParts.createSubscriptionStorageContext(this.#specSnapshot), storageFactory);
1713
+ new StorageSubscriptionRegistry(ContextParts.createSubscriptionStorageContext(this.#specSnapshot), storageFactory, subscriptionRecordGroup(this.#specSnapshot.name.value));
1624
1714
  resources.registry = registry;
1625
1715
  const runtime = new SubscriptionRuntime(stand, buses.systemStand, buses.eventBus, buses.systemEventBus, registry);
1626
1716
  resources.runtime = runtime;
@@ -1638,12 +1728,21 @@ export class BoundedContextBuilder {
1638
1728
  context: ContextParts.createStorageMode(this.#specSnapshot),
1639
1729
  storageFactory,
1640
1730
  });
1731
+ if (this.#readFilters.size > 0)
1732
+ standAccess.installReadFilters(stand, [...this.#readFilters]);
1641
1733
  standAccess.setAdmission(stand, (tenantId) => TenantAdmissions.require(tenantIndex, this.#specSnapshot.name.value, this.#specSnapshot.multitenant, tenantId));
1642
1734
  return stand;
1643
1735
  }
1736
+ /**
1737
+ * Constructs the immutable BoundedContext using assembled buses, runtime parts, environment
1738
+ * delivery strategy, and tenant index.
1739
+ */
1644
1740
  #createContext(repositories, storageFactory, buses, running, ai) {
1645
1741
  return ContextParts.createBoundedContext(this.#specSnapshot, storageFactory, repositories, ServerEnvironment.instance().delivery.strategy, buses, running, ai, this.#tenantIndex ?? ServerEnvironment.instance().tenantIndex);
1646
1742
  }
1743
+ /**
1744
+ * Closes partial context resources after assembly fails and aggregates cleanup errors with the original failure.
1745
+ */
1647
1746
  async #failBuild(resources, error) {
1648
1747
  const cleanupErrors = [];
1649
1748
  if (resources.context !== undefined) {
@@ -1675,28 +1774,27 @@ export class BoundedContextBuilder {
1675
1774
  }
1676
1775
  throw error;
1677
1776
  }
1678
- async #loadGeneratedArtifacts(entityTypes) {
1777
+ /**
1778
+ * Selects generated metadata for configured Entity and standalone handler types and creates
1779
+ * repository views and handler groups.
1780
+ */
1781
+ #loadGeneratedArtifacts(entityTypes) {
1679
1782
  if (entityTypes.length === 0 && this.#standaloneInstances().length === 0) {
1680
1783
  return Object.freeze({ repositories: Object.freeze([]), standalone: Object.freeze([]) });
1681
1784
  }
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
- }));
1785
+ const selected = [
1786
+ ...entityTypes,
1787
+ ...this.#standaloneInstances().map((instance) => instance.constructor),
1788
+ ];
1789
+ const registries = [SpineMetadataInventory.select(selected)];
1695
1790
  const metadata = ContextParts.ingestGeneratedRegistries(registries);
1696
1791
  const repositories = Object.freeze(entityTypes.map((entityType) => ContextParts.createGeneratedRepository(entityType, registries, metadata, this.#generatedRepositoryOptions.get(entityType))));
1697
1792
  const standalone = Object.freeze(registries.flatMap((registry) => registry.receivers.filter((receiver) => receiver.receiverKind === "standalone")));
1698
1793
  return Object.freeze({ repositories, standalone });
1699
1794
  }
1795
+ /**
1796
+ * Returns a frozen list of configured standalone assignee, commander, and event-receiver instances.
1797
+ */
1700
1798
  #standaloneInstances() {
1701
1799
  return Object.freeze([...this.#assignees, ...this.#commanders, ...this.#eventReceivers]);
1702
1800
  }
@@ -1945,7 +2043,7 @@ const ContextParts = Object.freeze({
1945
2043
  * @returns Constructed bounded context.
1946
2044
  */
1947
2045
  createBoundedContext(specSnapshot, storageFactory, repositories, deliveryStrategy, buses, running, ai, tenantIndex) {
1948
- return constructBoundedContext({
2046
+ const context = constructBoundedContext({
1949
2047
  snapshot: ContextParts.snapshotFor(specSnapshot),
1950
2048
  commandBus: buses.commandBus,
1951
2049
  eventBus: buses.eventBus,
@@ -1962,6 +2060,8 @@ const ContextParts = Object.freeze({
1962
2060
  ...(ai === undefined ? {} : { ai }),
1963
2061
  token: frameworkConstructionToken,
1964
2062
  });
2063
+ standAccess.bindProducingContext(running.stand, context);
2064
+ return context;
1965
2065
  },
1966
2066
  /**
1967
2067
  * Captures public context identity from the build specification.
@@ -2318,151 +2418,6 @@ const ContextParts = Object.freeze({
2318
2418
  throw new TypeError(`${operation} requires a Repository instance. Use an entity class only with build().`);
2319
2419
  }
2320
2420
  },
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
2421
  /**
2467
2422
  * Loads generated handler registries into a metadata registry.
2468
2423
  *
@@ -3160,9 +3115,15 @@ const ContextParts = Object.freeze({
3160
3115
  * @internal
3161
3116
  */
3162
3117
  export const boundedContextIntegrationAccess = Object.freeze({
3118
+ /**
3119
+ * Publishes an imported Event through the context’s import path.
3120
+ */
3163
3121
  publishImported(context, event) {
3164
3122
  return ContextParts.publishImported(context, event);
3165
3123
  },
3124
+ /**
3125
+ * Signals that standalone context integration is ready.
3126
+ */
3166
3127
  ready(context) {
3167
3128
  return ContextParts.integrationReady(context);
3168
3129
  },