@spine-event-engine/server 2.0.0-snapshot.24 → 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 (374) hide show
  1. package/README.md +86 -19
  2. package/REFERENCE.md +538 -150
  3. package/dist/agent/agent-acceptance-receipts.d.ts +69 -0
  4. package/dist/agent/agent-acceptance-receipts.d.ts.map +1 -0
  5. package/dist/agent/agent-acceptance-receipts.js +198 -0
  6. package/dist/agent/agent-acceptance-receipts.js.map +1 -0
  7. package/dist/agent/agent-execution-capacity.d.ts +37 -9
  8. package/dist/agent/agent-execution-capacity.d.ts.map +1 -1
  9. package/dist/agent/agent-execution-capacity.js +73 -15
  10. package/dist/agent/agent-execution-capacity.js.map +1 -1
  11. package/dist/agent/agent-history.d.ts +33 -12
  12. package/dist/agent/agent-history.d.ts.map +1 -1
  13. package/dist/agent/agent-history.js +61 -18
  14. package/dist/agent/agent-history.js.map +1 -1
  15. package/dist/agent/agent-live-ai-audit.d.ts +95 -0
  16. package/dist/agent/agent-live-ai-audit.d.ts.map +1 -0
  17. package/dist/agent/agent-live-ai-audit.js +290 -0
  18. package/dist/agent/agent-live-ai-audit.js.map +1 -0
  19. package/dist/agent/agent-live-ai-runtime.d.ts +300 -0
  20. package/dist/agent/agent-live-ai-runtime.d.ts.map +1 -0
  21. package/dist/agent/agent-live-ai-runtime.js +611 -0
  22. package/dist/agent/agent-live-ai-runtime.js.map +1 -0
  23. package/dist/agent/agent-live-audit.d.ts +67 -0
  24. package/dist/agent/agent-live-audit.d.ts.map +1 -0
  25. package/dist/agent/agent-live-audit.js +121 -0
  26. package/dist/agent/agent-live-audit.js.map +1 -0
  27. package/dist/agent/agent-live-budget.d.ts +84 -0
  28. package/dist/agent/agent-live-budget.d.ts.map +1 -0
  29. package/dist/agent/agent-live-budget.js +161 -0
  30. package/dist/agent/agent-live-budget.js.map +1 -0
  31. package/dist/agent/agent-live-fault.d.ts +18 -0
  32. package/dist/agent/agent-live-fault.d.ts.map +1 -0
  33. package/dist/agent/{agent-execution-fault.js → agent-live-fault.js} +7 -7
  34. package/dist/agent/agent-live-fault.js.map +1 -0
  35. package/dist/agent/agent-live-inputs.d.ts +74 -0
  36. package/dist/agent/agent-live-inputs.d.ts.map +1 -0
  37. package/dist/agent/agent-live-inputs.js +15 -0
  38. package/dist/agent/agent-live-inputs.js.map +1 -0
  39. package/dist/agent/agent-live-interaction-events.d.ts +46 -0
  40. package/dist/agent/agent-live-interaction-events.d.ts.map +1 -0
  41. package/dist/agent/agent-live-interaction-events.js +104 -0
  42. package/dist/agent/agent-live-interaction-events.js.map +1 -0
  43. package/dist/agent/agent-live-mcp-controls.d.ts +129 -0
  44. package/dist/agent/agent-live-mcp-controls.d.ts.map +1 -0
  45. package/dist/agent/agent-live-mcp-controls.js +228 -0
  46. package/dist/agent/agent-live-mcp-controls.js.map +1 -0
  47. package/dist/agent/agent-live-mcp-host.d.ts +88 -0
  48. package/dist/agent/agent-live-mcp-host.d.ts.map +1 -0
  49. package/dist/agent/{agent-mcp-host.js → agent-live-mcp-host.js} +1 -1
  50. package/dist/agent/agent-live-mcp-host.js.map +1 -0
  51. package/dist/agent/{agent-mcp-runtime.d.ts → agent-live-mcp-runtime.d.ts} +6 -6
  52. package/dist/agent/agent-live-mcp-runtime.d.ts.map +1 -0
  53. package/dist/agent/{agent-mcp-runtime.js → agent-live-mcp-runtime.js} +13 -42
  54. package/dist/agent/agent-live-mcp-runtime.js.map +1 -0
  55. package/dist/agent/agent-live-read-runtime.d.ts +72 -0
  56. package/dist/agent/agent-live-read-runtime.d.ts.map +1 -0
  57. package/dist/agent/agent-live-read-runtime.js +122 -0
  58. package/dist/agent/agent-live-read-runtime.js.map +1 -0
  59. package/dist/agent/agent-model-selection.d.ts +21 -34
  60. package/dist/agent/agent-model-selection.d.ts.map +1 -1
  61. package/dist/agent/agent-model-selection.js +61 -78
  62. package/dist/agent/agent-model-selection.js.map +1 -1
  63. package/dist/browser/browser-server.d.ts +163 -0
  64. package/dist/browser/browser-server.d.ts.map +1 -1
  65. package/dist/browser/browser-server.js +579 -188
  66. package/dist/browser/browser-server.js.map +1 -1
  67. package/dist/browser/durable-subscription-bindings.d.ts +5 -0
  68. package/dist/browser/durable-subscription-bindings.d.ts.map +1 -1
  69. package/dist/browser/durable-subscription-bindings.js +226 -38
  70. package/dist/browser/durable-subscription-bindings.js.map +1 -1
  71. package/dist/browser/options.d.ts +5 -0
  72. package/dist/browser/options.d.ts.map +1 -1
  73. package/dist/bus/command-bus.d.ts +17 -18
  74. package/dist/bus/command-bus.d.ts.map +1 -1
  75. package/dist/bus/command-bus.js +97 -54
  76. package/dist/bus/command-bus.js.map +1 -1
  77. package/dist/bus/command-errors.d.ts +20 -4
  78. package/dist/bus/command-errors.d.ts.map +1 -1
  79. package/dist/bus/command-errors.js +47 -7
  80. package/dist/bus/command-errors.js.map +1 -1
  81. package/dist/bus/event-bus.d.ts +20 -4
  82. package/dist/bus/event-bus.d.ts.map +1 -1
  83. package/dist/bus/event-bus.js +166 -14
  84. package/dist/bus/event-bus.js.map +1 -1
  85. package/dist/context/bounded-context.d.ts +169 -98
  86. package/dist/context/bounded-context.d.ts.map +1 -1
  87. package/dist/context/bounded-context.js +837 -503
  88. package/dist/context/bounded-context.js.map +1 -1
  89. package/dist/context/entity-inbox.d.ts +7 -2
  90. package/dist/context/entity-inbox.d.ts.map +1 -1
  91. package/dist/context/entity-inbox.js +22 -13
  92. package/dist/context/entity-inbox.js.map +1 -1
  93. package/dist/context/local-inbox-handoff.d.ts +34 -34
  94. package/dist/context/local-inbox-handoff.d.ts.map +1 -1
  95. package/dist/context/local-inbox-handoff.js +108 -150
  96. package/dist/context/local-inbox-handoff.js.map +1 -1
  97. package/dist/context/projection-handoff.d.ts +2 -2
  98. package/dist/context/projection-handoff.d.ts.map +1 -1
  99. package/dist/context/projection-handoff.js +10 -8
  100. package/dist/context/projection-handoff.js.map +1 -1
  101. package/dist/context/tenant-index.d.ts +36 -31
  102. package/dist/context/tenant-index.d.ts.map +1 -1
  103. package/dist/context/tenant-index.js +123 -125
  104. package/dist/context/tenant-index.js.map +1 -1
  105. package/dist/context/tenant-unknown-error.d.ts +33 -0
  106. package/dist/context/tenant-unknown-error.d.ts.map +1 -0
  107. package/dist/context/tenant-unknown-error.js +75 -0
  108. package/dist/context/tenant-unknown-error.js.map +1 -0
  109. package/dist/delivery/agent-delivery-scope.d.ts +66 -0
  110. package/dist/delivery/agent-delivery-scope.d.ts.map +1 -0
  111. package/dist/delivery/agent-delivery-scope.js +44 -0
  112. package/dist/delivery/agent-delivery-scope.js.map +1 -0
  113. package/dist/delivery/delivery-builder.d.ts +25 -1
  114. package/dist/delivery/delivery-builder.d.ts.map +1 -1
  115. package/dist/delivery/delivery-builder.js +89 -0
  116. package/dist/delivery/delivery-builder.js.map +1 -1
  117. package/dist/delivery/delivery-monitor.d.ts +1 -1
  118. package/dist/delivery/delivery-monitor.js +1 -1
  119. package/dist/delivery/delivery-run-control.d.ts +6 -0
  120. package/dist/delivery/delivery-run-control.d.ts.map +1 -1
  121. package/dist/delivery/delivery-run-control.js +10 -0
  122. package/dist/delivery/delivery-run-control.js.map +1 -1
  123. package/dist/delivery/delivery-supervisor.d.ts.map +1 -1
  124. package/dist/delivery/delivery-supervisor.js +28 -4
  125. package/dist/delivery/delivery-supervisor.js.map +1 -1
  126. package/dist/delivery/delivery-tenant.d.ts +17 -0
  127. package/dist/delivery/delivery-tenant.d.ts.map +1 -0
  128. package/dist/delivery/delivery-tenant.js +50 -0
  129. package/dist/delivery/delivery-tenant.js.map +1 -0
  130. package/dist/delivery/delivery.d.ts +96 -2
  131. package/dist/delivery/delivery.d.ts.map +1 -1
  132. package/dist/delivery/delivery.js +693 -53
  133. package/dist/delivery/delivery.js.map +1 -1
  134. package/dist/delivery/inbox-records.d.ts.map +1 -1
  135. package/dist/delivery/inbox-records.js +9 -16
  136. package/dist/delivery/inbox-records.js.map +1 -1
  137. package/dist/delivery/inbox-storage.d.ts +6 -0
  138. package/dist/delivery/inbox-storage.d.ts.map +1 -1
  139. package/dist/delivery/inbox-storage.js +43 -6
  140. package/dist/delivery/inbox-storage.js.map +1 -1
  141. package/dist/delivery/inbox.d.ts.map +1 -1
  142. package/dist/delivery/inbox.js +3 -2
  143. package/dist/delivery/inbox.js.map +1 -1
  144. package/dist/delivery/reception-observation.d.ts +12 -4
  145. package/dist/delivery/reception-observation.d.ts.map +1 -1
  146. package/dist/delivery/reception-observation.js +14 -3
  147. package/dist/delivery/reception-observation.js.map +1 -1
  148. package/dist/delivery/sharded-work-registry.d.ts +6 -0
  149. package/dist/delivery/sharded-work-registry.d.ts.map +1 -1
  150. package/dist/delivery/sharded-work-registry.js +41 -4
  151. package/dist/delivery/sharded-work-registry.js.map +1 -1
  152. package/dist/entity/entity-storage-descriptor.d.ts.map +1 -1
  153. package/dist/entity/entity-storage-descriptor.js +7 -4
  154. package/dist/entity/entity-storage-descriptor.js.map +1 -1
  155. package/dist/entity/entity.d.ts +59 -0
  156. package/dist/entity/entity.d.ts.map +1 -1
  157. package/dist/entity/entity.js +60 -0
  158. package/dist/entity/entity.js.map +1 -1
  159. package/dist/handler/app-query-handler.d.ts +60 -0
  160. package/dist/handler/app-query-handler.d.ts.map +1 -0
  161. package/dist/{bus/saved-dispatcher-binding.js → handler/app-query-handler.js} +8 -12
  162. package/dist/handler/app-query-handler.js.map +1 -0
  163. package/dist/handler/generated-registry-discovery.d.ts +6 -0
  164. package/dist/handler/generated-registry-discovery.d.ts.map +1 -1
  165. package/dist/handler/generated-registry-discovery.js +71 -2
  166. package/dist/handler/generated-registry-discovery.js.map +1 -1
  167. package/dist/handler/handler-decorators.d.ts +8 -29
  168. package/dist/handler/handler-decorators.d.ts.map +1 -1
  169. package/dist/handler/handler-decorators.js +33 -210
  170. package/dist/handler/handler-decorators.js.map +1 -1
  171. package/dist/handler/spine-metadata-inventory.d.ts +99 -0
  172. package/dist/handler/spine-metadata-inventory.d.ts.map +1 -0
  173. package/dist/handler/spine-metadata-inventory.js +162 -0
  174. package/dist/handler/spine-metadata-inventory.js.map +1 -0
  175. package/dist/index.d.ts +9 -5
  176. package/dist/index.d.ts.map +1 -1
  177. package/dist/index.js +6 -3
  178. package/dist/index.js.map +1 -1
  179. package/dist/integration/external-messages.d.ts.map +1 -1
  180. package/dist/integration/external-messages.js +9 -15
  181. package/dist/integration/external-messages.js.map +1 -1
  182. package/dist/integration/integration-broker.d.ts.map +1 -1
  183. package/dist/integration/integration-broker.js +112 -6
  184. package/dist/integration/integration-broker.js.map +1 -1
  185. package/dist/integration/third-party-context.d.ts.map +1 -1
  186. package/dist/integration/third-party-context.js +7 -10
  187. package/dist/integration/third-party-context.js.map +1 -1
  188. package/dist/repository/primitive-id.js +26 -11
  189. package/dist/repository/primitive-id.js.map +1 -1
  190. package/dist/repository/repository.d.ts +75 -60
  191. package/dist/repository/repository.d.ts.map +1 -1
  192. package/dist/repository/repository.js +1757 -1818
  193. package/dist/repository/repository.js.map +1 -1
  194. package/dist/runtime/runtime.d.ts +13 -0
  195. package/dist/runtime/runtime.d.ts.map +1 -1
  196. package/dist/runtime/runtime.js +18 -0
  197. package/dist/runtime/runtime.js.map +1 -1
  198. package/dist/runtime/signal-metadata.d.ts.map +1 -1
  199. package/dist/runtime/signal-metadata.js +6 -6
  200. package/dist/runtime/signal-metadata.js.map +1 -1
  201. package/dist/runtime/signal-publisher.d.ts +0 -6
  202. package/dist/runtime/signal-publisher.d.ts.map +1 -1
  203. package/dist/runtime/signal-publisher.js +1 -9
  204. package/dist/runtime/signal-publisher.js.map +1 -1
  205. package/dist/runtime/standalone-handler-runtime.d.ts +0 -7
  206. package/dist/runtime/standalone-handler-runtime.d.ts.map +1 -1
  207. package/dist/runtime/standalone-handler-runtime.js +13 -99
  208. package/dist/runtime/standalone-handler-runtime.js.map +1 -1
  209. package/dist/server/managed-delivery-gate.d.ts +34 -0
  210. package/dist/server/managed-delivery-gate.d.ts.map +1 -0
  211. package/dist/server/managed-delivery-gate.js +70 -0
  212. package/dist/server/managed-delivery-gate.js.map +1 -0
  213. package/dist/server/managed-server-application.d.ts +12 -11
  214. package/dist/server/managed-server-application.d.ts.map +1 -1
  215. package/dist/server/managed-server-application.js +48 -29
  216. package/dist/server/managed-server-application.js.map +1 -1
  217. package/dist/server/node-coordinator.d.ts +18 -5
  218. package/dist/server/node-coordinator.d.ts.map +1 -1
  219. package/dist/server/node-coordinator.js +554 -84
  220. package/dist/server/node-coordinator.js.map +1 -1
  221. package/dist/server/reserved-spine-rpc-paths.json +1 -0
  222. package/dist/server/server-environment.d.ts +220 -25
  223. package/dist/server/server-environment.d.ts.map +1 -1
  224. package/dist/server/server-environment.js +734 -145
  225. package/dist/server/server-environment.js.map +1 -1
  226. package/dist/server/server-log.d.ts +7 -0
  227. package/dist/server/server-log.d.ts.map +1 -1
  228. package/dist/server/server-log.js +14 -1
  229. package/dist/server/server-log.js.map +1 -1
  230. package/dist/server/server.d.ts +60 -29
  231. package/dist/server/server.d.ts.map +1 -1
  232. package/dist/server/server.js +155 -201
  233. package/dist/server/server.js.map +1 -1
  234. package/dist/services/app-query-execution.d.ts +53 -0
  235. package/dist/services/app-query-execution.d.ts.map +1 -0
  236. package/dist/services/app-query-execution.js +408 -0
  237. package/dist/services/app-query-execution.js.map +1 -0
  238. package/dist/services/query-reader.d.ts +32 -7
  239. package/dist/services/query-reader.d.ts.map +1 -1
  240. package/dist/services/query-reader.js +22 -2
  241. package/dist/services/query-reader.js.map +1 -1
  242. package/dist/services/read-request-status.d.ts +65 -0
  243. package/dist/services/read-request-status.d.ts.map +1 -0
  244. package/dist/services/read-request-status.js +151 -0
  245. package/dist/services/read-request-status.js.map +1 -0
  246. package/dist/services/spine-services.d.ts +37 -8
  247. package/dist/services/spine-services.d.ts.map +1 -1
  248. package/dist/services/spine-services.js +1103 -470
  249. package/dist/services/spine-services.js.map +1 -1
  250. package/dist/services/trusted-reader-ingress.d.ts +92 -0
  251. package/dist/services/trusted-reader-ingress.d.ts.map +1 -0
  252. package/dist/services/trusted-reader-ingress.js +89 -0
  253. package/dist/services/trusted-reader-ingress.js.map +1 -0
  254. package/dist/spi/handler-registry.d.ts +2 -0
  255. package/dist/spi/handler-registry.d.ts.map +1 -1
  256. package/dist/spi/handler-registry.js +1 -1
  257. package/dist/spi/handler-registry.js.map +1 -1
  258. package/dist/stand/event-origin-filter.js +3 -3
  259. package/dist/stand/event-origin-filter.js.map +1 -1
  260. package/dist/stand/read-side-filter-engine.d.ts +46 -0
  261. package/dist/stand/read-side-filter-engine.d.ts.map +1 -0
  262. package/dist/stand/read-side-filter-engine.js +356 -0
  263. package/dist/stand/read-side-filter-engine.js.map +1 -0
  264. package/dist/stand/read-side-filter.d.ts +247 -0
  265. package/dist/stand/read-side-filter.d.ts.map +1 -0
  266. package/dist/stand/read-side-filter.js +39 -0
  267. package/dist/stand/read-side-filter.js.map +1 -0
  268. package/dist/stand/read-side-message.d.ts +66 -0
  269. package/dist/stand/read-side-message.d.ts.map +1 -0
  270. package/dist/stand/read-side-message.js +167 -0
  271. package/dist/stand/read-side-message.js.map +1 -0
  272. package/dist/stand/stand-direct-delivery.d.ts +161 -0
  273. package/dist/stand/stand-direct-delivery.d.ts.map +1 -0
  274. package/dist/stand/stand-direct-delivery.js +210 -0
  275. package/dist/stand/stand-direct-delivery.js.map +1 -0
  276. package/dist/stand/stand.d.ts +94 -42
  277. package/dist/stand/stand.d.ts.map +1 -1
  278. package/dist/stand/stand.js +460 -60
  279. package/dist/stand/stand.js.map +1 -1
  280. package/dist/stand/subscription-observer.d.ts.map +1 -1
  281. package/dist/stand/subscription-observer.js +157 -40
  282. package/dist/stand/subscription-observer.js.map +1 -1
  283. package/dist/stand/subscription-read-filters.d.ts +40 -0
  284. package/dist/stand/subscription-read-filters.d.ts.map +1 -0
  285. package/dist/stand/subscription-read-filters.js +91 -0
  286. package/dist/stand/subscription-read-filters.js.map +1 -0
  287. package/dist/stand/subscription-reader-binding.d.ts +20 -0
  288. package/dist/stand/subscription-reader-binding.d.ts.map +1 -0
  289. package/dist/stand/subscription-reader-binding.js +56 -0
  290. package/dist/stand/subscription-reader-binding.js.map +1 -0
  291. package/dist/stand/subscription-records.d.ts +30 -3
  292. package/dist/stand/subscription-records.d.ts.map +1 -1
  293. package/dist/stand/subscription-records.js +61 -16
  294. package/dist/stand/subscription-records.js.map +1 -1
  295. package/dist/stand/subscription-registry.d.ts +54 -15
  296. package/dist/stand/subscription-registry.d.ts.map +1 -1
  297. package/dist/stand/subscription-registry.js +164 -41
  298. package/dist/stand/subscription-registry.js.map +1 -1
  299. package/dist/stand/subscription-runtime.d.ts +12 -5
  300. package/dist/stand/subscription-runtime.d.ts.map +1 -1
  301. package/dist/stand/subscription-runtime.js +183 -34
  302. package/dist/stand/subscription-runtime.js.map +1 -1
  303. package/dist/testing/index.d.ts.map +1 -1
  304. package/dist/testing/index.js +4 -3
  305. package/dist/testing/index.js.map +1 -1
  306. package/dist/tsconfig.tsbuildinfo +1 -1
  307. package/package.json +14 -14
  308. package/dist/agent/agent-admission.d.ts +0 -80
  309. package/dist/agent/agent-admission.d.ts.map +0 -1
  310. package/dist/agent/agent-admission.js +0 -127
  311. package/dist/agent/agent-admission.js.map +0 -1
  312. package/dist/agent/agent-ai-runtime.d.ts +0 -76
  313. package/dist/agent/agent-ai-runtime.d.ts.map +0 -1
  314. package/dist/agent/agent-ai-runtime.js +0 -1269
  315. package/dist/agent/agent-ai-runtime.js.map +0 -1
  316. package/dist/agent/agent-execution-fault.d.ts +0 -18
  317. package/dist/agent/agent-execution-fault.d.ts.map +0 -1
  318. package/dist/agent/agent-execution-fault.js.map +0 -1
  319. package/dist/agent/agent-execution-session.d.ts +0 -103
  320. package/dist/agent/agent-execution-session.d.ts.map +0 -1
  321. package/dist/agent/agent-execution-session.js +0 -238
  322. package/dist/agent/agent-execution-session.js.map +0 -1
  323. package/dist/agent/agent-interaction-audit.d.ts +0 -58
  324. package/dist/agent/agent-interaction-audit.d.ts.map +0 -1
  325. package/dist/agent/agent-interaction-audit.js +0 -114
  326. package/dist/agent/agent-interaction-audit.js.map +0 -1
  327. package/dist/agent/agent-interaction-events.d.ts +0 -40
  328. package/dist/agent/agent-interaction-events.d.ts.map +0 -1
  329. package/dist/agent/agent-interaction-events.js +0 -129
  330. package/dist/agent/agent-interaction-events.js.map +0 -1
  331. package/dist/agent/agent-mcp-budget.d.ts +0 -125
  332. package/dist/agent/agent-mcp-budget.d.ts.map +0 -1
  333. package/dist/agent/agent-mcp-budget.js +0 -566
  334. package/dist/agent/agent-mcp-budget.js.map +0 -1
  335. package/dist/agent/agent-mcp-host.d.ts +0 -125
  336. package/dist/agent/agent-mcp-host.d.ts.map +0 -1
  337. package/dist/agent/agent-mcp-host.js.map +0 -1
  338. package/dist/agent/agent-mcp-runtime.d.ts.map +0 -1
  339. package/dist/agent/agent-mcp-runtime.js.map +0 -1
  340. package/dist/agent/agent-read-runtime.d.ts +0 -70
  341. package/dist/agent/agent-read-runtime.d.ts.map +0 -1
  342. package/dist/agent/agent-read-runtime.js +0 -320
  343. package/dist/agent/agent-read-runtime.js.map +0 -1
  344. package/dist/agent/agent-revisions.d.ts +0 -35
  345. package/dist/agent/agent-revisions.d.ts.map +0 -1
  346. package/dist/agent/agent-revisions.js +0 -133
  347. package/dist/agent/agent-revisions.js.map +0 -1
  348. package/dist/agent/agent-scheduler.d.ts +0 -99
  349. package/dist/agent/agent-scheduler.d.ts.map +0 -1
  350. package/dist/agent/agent-scheduler.js +0 -398
  351. package/dist/agent/agent-scheduler.js.map +0 -1
  352. package/dist/bus/saved-dispatcher-binding.d.ts +0 -53
  353. package/dist/bus/saved-dispatcher-binding.d.ts.map +0 -1
  354. package/dist/bus/saved-dispatcher-binding.js.map +0 -1
  355. package/dist/delivery/delivery-run-coordinator.d.ts +0 -218
  356. package/dist/delivery/delivery-run-coordinator.d.ts.map +0 -1
  357. package/dist/delivery/delivery-run-coordinator.js +0 -609
  358. package/dist/delivery/delivery-run-coordinator.js.map +0 -1
  359. package/dist/delivery/parked-delivery-obligations.d.ts +0 -182
  360. package/dist/delivery/parked-delivery-obligations.d.ts.map +0 -1
  361. package/dist/delivery/parked-delivery-obligations.js +0 -390
  362. package/dist/delivery/parked-delivery-obligations.js.map +0 -1
  363. package/dist/server/environment-attachment.d.ts +0 -356
  364. package/dist/server/environment-attachment.d.ts.map +0 -1
  365. package/dist/server/environment-attachment.js +0 -2062
  366. package/dist/server/environment-attachment.js.map +0 -1
  367. package/dist/server/environment-delivery-records.d.ts +0 -77
  368. package/dist/server/environment-delivery-records.d.ts.map +0 -1
  369. package/dist/server/environment-delivery-records.js +0 -282
  370. package/dist/server/environment-delivery-records.js.map +0 -1
  371. package/dist/server/environment-delivery-worker.d.ts +0 -161
  372. package/dist/server/environment-delivery-worker.d.ts.map +0 -1
  373. package/dist/server/environment-delivery-worker.js +0 -973
  374. package/dist/server/environment-delivery-worker.js.map +0 -1
@@ -11,45 +11,34 @@
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";
21
17
  import { EventSchema, EventContextSchema, BoundedContextNameSchema, TenantIdSchema, } from "@spine-event-engine/proto";
22
18
  import { SPI_type, internal_all, internal_type } from "@spine-event-engine/proto";
23
- import { ColumnTypes, EventStore, InMemoryStorageFactory, RecordColumn, StorageGroup, } from "@spine-event-engine/storage";
24
- import { AgentExecutionStorageFactories, AgentHistoryStorageFactories, TenantBoundary, } from "@spine-event-engine/storage/provider";
25
- /**
26
- * Storage family shared by paired System Event publication and trusted reads.
27
- * @internal
28
- */
29
- export const systemEventGroup = new StorageGroup("spine_system_events");
19
+ import { ColumnTypes, EventStore, RecordColumn, } from "@spine-event-engine/storage";
20
+ import { AgentHistoryStorageFactories, EventStoreGroups, eventStoreRecordSpec, StoragePreparations, TenantBoundary, } from "@spine-event-engine/storage/provider";
30
21
  import { CommandBus, commandBusAccess } from "../bus/command-bus.js";
31
22
  import { EventBus, eventBusAccess, isSystemEventSchema, } from "../bus/event-bus.js";
32
- import { DeliveryReadiness, } from "./local-inbox-handoff.js";
23
+ import { DeliveryReadiness } from "./local-inbox-handoff.js";
33
24
  import { LocalEntityInbox } from "./entity-inbox.js";
34
25
  import { LocalProjectionInbox } from "./projection-handoff.js";
35
- import { TenantIndexes } from "./tenant-index.js";
36
- import { EffectiveTenants } from "./effective-tenant.js";
37
- import { AgentScheduler } from "../agent/agent-scheduler.js";
38
- import { AgentExecutionCapacity } from "../agent/agent-execution-capacity.js";
26
+ import { TenantAdmissions } from "./tenant-unknown-error.js";
27
+ import { AgentExecutionCapacity, } from "../agent/agent-execution-capacity.js";
28
+ import { AgentAcceptanceReceipts, agentAcceptanceReceiptGroup, agentAcceptanceReceiptSpec, } from "../agent/agent-acceptance-receipts.js";
39
29
  import { Repository, repositoryAccess, } from "../repository/repository.js";
40
- import { GeneratedRegistryDiscovery } from "../handler/generated-registry-discovery.js";
30
+ import { SpineMetadataInventory } from "../handler/spine-metadata-inventory.js";
41
31
  import { HandlerRegistryIngestor, } from "../handler/generated-handler-registry.js";
42
32
  import { AbstractAssignee, AbstractCommander, AbstractEventReactor, AbstractEventSubscriber, } from "../handler/standalone.js";
43
33
  import { HandlerMetadataRegistry, } from "../handler/handler-metadata.js";
44
34
  import { SignalMetadata } from "../runtime/signal-metadata.js";
45
- import { Stand } from "../stand/stand.js";
35
+ import { runtimeAccess } from "../runtime/runtime.js";
36
+ import { Stand, standAccess } from "../stand/stand.js";
37
+ import { ReadSideFilter } from "../stand/read-side-filter.js";
46
38
  import { SubscriptionRuntime, subscriptionRuntimeAccess } from "../stand/subscription-runtime.js";
47
- import { StorageSubscriptionRegistry, } from "../stand/subscription-registry.js";
48
- import { UniformAcrossAllShards } from "../delivery/delivery-builder.js";
49
- import { InboxTargets } from "../delivery/inbox.js";
50
- import { ShardIndex } from "../delivery/shard-index.js";
39
+ import { StorageSubscriptionRegistry, subscriptionRecordGroup, } from "../stand/subscription-registry.js";
51
40
  import { IntegrationBroker } from "../integration/integration-broker.js";
52
- import { ServerEnvironment } from "../server/server-environment.js";
41
+ import { ServerEnvironment, serverEnvironmentAccess } from "../server/server-environment.js";
53
42
  import { SignalPublisher } from "../runtime/signal-publisher.js";
54
43
  import { StandaloneHandlerRuntime, } from "../runtime/standalone-handler-runtime.js";
55
44
  /**
@@ -73,20 +62,51 @@ export class BoundedContextNameError extends Error {
73
62
  Object.setPrototypeOf(this, new.target.prototype);
74
63
  }
75
64
  }
65
+ /**
66
+ * Rejects an Entity state type already served by another open domain context.
67
+ */
68
+ export class DuplicateEntityStateException extends Error {
69
+ // prettier-ignore
70
+ /**
71
+ * Wire type URL of the conflicting Entity state.
72
+ */
73
+ stateType;
74
+ /**
75
+ * Name of the context that already serves the state.
76
+ */
77
+ registeredContext;
78
+ /**
79
+ * Name of the context attempting registration.
80
+ */
81
+ conflictingContext;
82
+ /**
83
+ * Creates a duplicate-state registration error.
84
+ * @param stateType Entity state wire type URL.
85
+ * @param registeredContext Existing domain context name.
86
+ * @param conflictingContext New domain context name.
87
+ */
88
+ constructor(stateType, registeredContext, conflictingContext) {
89
+ super(`Entity state "${stateType}" is already registered in "${registeredContext}"; ` +
90
+ `register it in only one open domain Bounded Context (attempted in "${conflictingContext}").`);
91
+ this.name = "DuplicateEntityStateException";
92
+ this.stateType = stateType;
93
+ this.registeredContext = registeredContext;
94
+ this.conflictingContext = conflictingContext;
95
+ }
96
+ }
76
97
  const frameworkConstructionToken = Object.freeze({
77
98
  frameworkConstructionToken: true,
78
99
  });
79
- const generatedRegistryFile = "generated/handler/generated-handler-registry.js";
80
100
  const errorDetailLimit = 500;
81
- const moduleSchemeRe = /^[A-Za-z][A-Za-z\d+.-]*:/;
82
101
  const internalStoragePrefix = "__spine/";
83
- const generatedRegistryLoadAttempts = new Map();
84
102
  const eventSubscribers = new WeakMap();
85
103
  const contextSystemPairings = new WeakMap();
86
104
  const contextTenantIndexes = new WeakMap();
87
105
  const contextStorageFactories = new WeakMap();
88
106
  const contextDeliveryDescriptors = new WeakMap();
89
107
  const contextSubscriptionRuntimes = new WeakMap();
108
+ const contextPreparations = new WeakMap();
109
+ const contextInputPreparations = new WeakMap();
90
110
  const contextLoggers = new WeakMap();
91
111
  const contextSignalPublishers = new WeakMap();
92
112
  const contextEventBuses = new WeakMap();
@@ -97,7 +117,9 @@ const contextClosePhases = new WeakMap();
97
117
  const contextIntegrations = new WeakMap();
98
118
  const systemEventPosters = new WeakMap();
99
119
  const builderBuilds = new WeakMap();
120
+ const explicitPreparations = new WeakMap();
100
121
  let constructBoundedContext;
122
+ let registerBoundedContext;
101
123
  let constructBoundedContextBuilder;
102
124
  let constructContextSpec;
103
125
  /**
@@ -113,22 +135,37 @@ export class BoundedContext {
113
135
  #eventEndpoint;
114
136
  #entityInbox;
115
137
  #projectionInbox;
116
- #deliveryStrategy;
138
+ #deliveryReadiness;
139
+ #stopAgentAvailability;
117
140
  #registeredRepositories = [];
118
141
  #repositoryViews = new Set();
119
142
  #storageFactory;
143
+ #environment;
120
144
  #ai;
121
145
  #stand;
122
146
  #systemStand;
123
147
  #subscriptionRuntime;
124
- #agentScheduler;
125
- #agentErrors = [];
148
+ /**
149
+ * Explicit storage and subscription preparation for this built context.
150
+ */
151
+ runtime;
152
+ #agentReceipts;
153
+ #agentController = new AbortController();
154
+ #agentReservations = new Set();
155
+ #activeReplays = new Set();
156
+ #activePreparations = new Set();
157
+ #tenantPreparations = new Map();
158
+ #preparationController = new AbortController();
159
+ #sharedPreparation;
126
160
  #closed;
127
161
  /**
128
162
  * Registers the framework-only construction hook for this module.
129
163
  */
130
164
  static {
131
165
  constructBoundedContext = (input) => new BoundedContext(input);
166
+ registerBoundedContext = (context, repositories) => {
167
+ context.#registerRepositories(repositories);
168
+ };
132
169
  }
133
170
  /**
134
171
  * Creates a Bounded Context from its prepared framework services.
@@ -145,9 +182,12 @@ export class BoundedContext {
145
182
  this.#stand = input.stand;
146
183
  this.#systemStand = input.systemStand;
147
184
  this.#subscriptionRuntime = input.subscriptionRuntime;
185
+ this.runtime = this.#createRuntime();
148
186
  this.#storageFactory = input.storageFactory;
187
+ this.#agentReceipts = new AgentAcceptanceReceipts(input.storageFactory);
188
+ this.#environment = ServerEnvironment.instance();
149
189
  this.#ai = input.ai;
150
- this.#deliveryStrategy = ContextParts.snapshotDeliveryStrategy(input.deliveryStrategy);
190
+ this.#installTenantAdmission();
151
191
  this.#commandEndpoint = Object.freeze({
152
192
  acceptedCommandTypes: () => this.#commandBus.acceptedCommandTypes(),
153
193
  post: (command) => this.#commandBus.post(command),
@@ -156,79 +196,305 @@ export class BoundedContext {
156
196
  acceptedEventTypes: () => ContextParts.exposedEventTypeUrls(this.#eventBus),
157
197
  post: (event) => ContextParts.postContextEvent(this, event),
158
198
  });
159
- const delivery = this.#createDelivery(input.storageFactory);
199
+ const delivery = this.#createDelivery(input.tenantIndex);
200
+ this.#deliveryReadiness = delivery.deliveryReadiness;
160
201
  this.#entityInbox = delivery.entityInbox;
161
202
  this.#projectionInbox = delivery.projectionInbox;
162
203
  this.#installReferences(input, delivery);
163
- this.#registerAndStart(input.repositories, delivery.tenantIndex);
164
- this.#startAgentScheduler(delivery.tenantIndex);
204
+ serverEnvironmentAccess.addContext(this.#environment, this);
165
205
  Object.freeze(this);
166
206
  }
167
207
  /**
168
- * Starts bounded indexed discovery for registered Agent repositories.
208
+ * Exposes explicit preparation for this built context.
209
+ *
210
+ * @returns Stable explicit preparation methods.
169
211
  */
170
- #startAgentScheduler(tenantIndex) {
171
- if (this.#ai === undefined)
172
- return;
173
- const repositories = [...this.#repositoryViews].filter((entry) => entry.entityFamily === "agent");
174
- if (repositories.length === 0)
175
- return;
176
- this.#agentScheduler = new AgentScheduler({
177
- repositories: repositories.length,
178
- page: (after, signal) => tenantIndex.page({
179
- count: 16,
180
- signal,
181
- ...(after === undefined ? {} : { after }),
182
- }),
183
- scope: (id, index) => {
184
- const repository = repositories[index];
185
- if (repository === undefined)
186
- throw new Error("Agent repository scope is unavailable.");
187
- return this.#agentScope(repository, tenantIndex.tenantMode === "single-tenant" ? undefined : id);
188
- },
189
- }, AgentExecutionCapacity.for(this.#ai), (error) => {
190
- if (this.#agentErrors.length < 32)
191
- this.#agentErrors.push(error);
212
+ #createRuntime() {
213
+ return Object.freeze({
214
+ createStorageStructures: (...ids) => this.#explicitStructures("create", ids),
215
+ checkStructureCompatibility: (...ids) => this.#explicitStructures("check", ids),
216
+ startSubscriptionMonitoring: () => this.#explicitMonitoring(),
192
217
  });
193
- this.#agentScheduler.start();
194
218
  }
195
219
  /**
196
- * Builds one scan scope from an accepted handoff or a catalog sweep.
220
+ * Installs live membership checks on context signal buses.
197
221
  *
198
- * @param repository Registered Agent repository.
199
- * @param tenantId Accepted tenant, absent only in a single-tenant Bounded Context.
200
- * @returns The matching indexed provider scan scope.
201
222
  */
202
- #agentScope(repository, tenantId) {
203
- const effective = EffectiveTenants.current(this.#snapshot.tenantMode === "multitenant", tenantId);
223
+ #installTenantAdmission() {
224
+ explicitPreparations.set(this, (mode, ids) => this.#prepareExplicitLocal(mode, ids));
225
+ commandBusAccess.bindEnvironment(this.#commandBus, this.#environment);
226
+ eventBusAccess.bindEnvironment(this.#eventBus, this.#environment);
227
+ eventBusAccess.bindEnvironment(this.#systemEventBus, this.#environment);
228
+ commandBusAccess.setAdmission(this.#commandBus, (command) => this.#admitTenant(command.context?.actorContext?.tenantId));
229
+ eventBusAccess.setAdmission(this.#eventBus, (event) => this.#admitTenant(ContextParts.readReplayTenant(event)));
230
+ standAccess.setAdmission(this.#stand, (tenantId) => this.#admitTenant(tenantId));
231
+ standAccess.setPublicAdmission(this.#stand, () => {
232
+ if (closingContexts.has(this) && !runtimeAccess.isRunningAdmittedWork(this.#environment))
233
+ throw new Error("Bounded Context is closing.");
234
+ });
235
+ }
236
+ /**
237
+ * Checks this context's live index before repository work.
238
+ *
239
+ * @param tenantId Captured tenant identity, when applicable.
240
+ */
241
+ async #admitTenant(tenantId) {
242
+ await TenantAdmissions.require(this.tenantIndex, this.#snapshot.name.value, this.isMultitenant, tenantId);
243
+ await this.#prepareInput(tenantId);
244
+ }
245
+ /**
246
+ * Initializes only the admitted input's complete context and tenant requirements.
247
+ * @param tenantId Captured input tenant identity, when applicable.
248
+ * @returns Completion after storage and first monitoring pass are ready.
249
+ */
250
+ async #prepareInput(tenantId) {
251
+ await this.#prepareShared("create");
252
+ await this.#prepareTenant(tenantId, "create");
253
+ if (!closingContexts.has(this))
254
+ await this.#subscriptionRuntime.start();
255
+ }
256
+ /**
257
+ * Prepares the fixed shared subscription registry layout once per context.
258
+ * @param mode Required provider preparation mode.
259
+ * @returns Shared preparation completion.
260
+ */
261
+ #prepareShared(mode) {
262
+ const registry = this.#subscriptionRuntime.registry();
263
+ if (!(registry instanceof StorageSubscriptionRegistry))
264
+ return Promise.resolve();
265
+ if (this.#sharedPreparation !== undefined)
266
+ return this.#sharedPreparation;
267
+ const attempt = this.#trackPreparation(() => registry.prepare(mode));
268
+ this.#sharedPreparation = attempt;
269
+ void attempt.catch(() => {
270
+ if (this.#sharedPreparation === attempt)
271
+ this.#sharedPreparation = undefined;
272
+ });
273
+ return attempt;
274
+ }
275
+ /**
276
+ * Shares one pending complete-tenant preparation without caching a stale model flag.
277
+ * @param tenantId Captured tenant identity, when applicable.
278
+ * @param mode Required provider preparation mode.
279
+ * @returns Complete tenant preparation attempt.
280
+ */
281
+ #prepareTenant(tenantId, mode) {
282
+ const context = this.#storageContextFor(tenantId);
283
+ const key = TenantBoundary.of(context).key;
284
+ const pending = this.#tenantPreparations.get(key);
285
+ if (pending !== undefined)
286
+ return pending;
287
+ const attempt = this.#trackPreparation(() => this.#prepareTenantNow(context, mode));
288
+ this.#tenantPreparations.set(key, attempt);
289
+ void attempt.then(() => this.#tenantPreparations.delete(key), () => this.#tenantPreparations.delete(key));
290
+ return attempt;
291
+ }
292
+ /**
293
+ * Prepares exact Event, Entity and Agent families for one captured destination.
294
+ * @param context Captured physical storage destination.
295
+ * @param mode Required provider preparation mode.
296
+ * @returns Completion after all declared families are ready.
297
+ */
298
+ async #prepareTenantNow(context, mode) {
299
+ const preparation = StoragePreparations.for(this.#storageFactory);
300
+ await preparation.prepareRecord(context, eventStoreRecordSpec, EventStoreGroups.forContext(context.name), mode);
301
+ const system = ContextParts.requireSystemPairing(this).system;
302
+ if (system.storesEvents) {
303
+ const systemContext = { ...context, name: system.name.value };
304
+ await preparation.prepareRecord(systemContext, eventStoreRecordSpec, EventStoreGroups.forContext(system.name.value, "system"), mode);
305
+ }
306
+ for (const repository of this.#repositoryViews)
307
+ await repositoryAccess.prepareStorage(repository, context, this.#storageFactory, mode);
308
+ if ([...this.#repositoryViews].some((repository) => repository.entityFamily === "agent"))
309
+ await preparation.prepareRecord(context, agentAcceptanceReceiptSpec, agentAcceptanceReceiptGroup, mode);
310
+ }
311
+ /**
312
+ * Captures the same tenant scope used by repository and Event storage.
313
+ * @param tenantId Complete selected tenant identity, when applicable.
314
+ * @returns A detached provider storage context.
315
+ */
316
+ #storageContextFor(tenantId) {
317
+ if (!this.isMultitenant)
318
+ return { name: this.name.value, multitenant: false };
319
+ if (tenantId === undefined)
320
+ throw new Error("Multitenant preparation requires tenantId.");
204
321
  return {
205
- id: JSON.stringify([
206
- this.#snapshot.name.value,
207
- String(TenantBoundary.from(effective).key),
208
- repository.stateFullTypeName,
209
- ]),
210
- pending: (after, count) => repositoryAccess.pendingAcceptedAgents(repository, tenantId, after, count),
211
- run: (key, signal) => repositoryAccess.runAcceptedAgent(repository, tenantId, key, signal),
322
+ name: this.name.value,
323
+ multitenant: true,
324
+ tenantId: TenantBoundary.from(tenantId).tenantId,
212
325
  };
213
326
  }
327
+ /**
328
+ * Retains an admitted preparation until closure can safely release its provider.
329
+ * @param onPrepare Complete provider preparation callback.
330
+ * @returns Tracked preparation completion.
331
+ */
332
+ #trackPreparation(onPrepare) {
333
+ const attempt = Promise.resolve().then(onPrepare);
334
+ this.#activePreparations.add(attempt);
335
+ void attempt.then(() => this.#activePreparations.delete(attempt), () => this.#activePreparations.delete(attempt));
336
+ return attempt;
337
+ }
338
+ /**
339
+ * Completes independently requested eager structure and monitor phases.
340
+ * @param options Independent build-time initialization choices.
341
+ * @returns Completion after requested phases settle.
342
+ */
343
+ async #prepareBuild(options) {
344
+ if (options.createStorageStructures !== false) {
345
+ await this.#requiredPrepare(() => this.#prepareShared("create"));
346
+ await this.#prepareCurrentTenants("create");
347
+ }
348
+ if (options.startSubscriptionMonitoring !== false) {
349
+ await this.#requiredPrepare(() => this.#prepareShared("create"));
350
+ await this.#requiredPrepare(() => this.#subscriptionRuntime.start());
351
+ }
352
+ }
353
+ /**
354
+ * Classifies a requested startup phase without changing shared lazy attempts.
355
+ * @param onPrepare Required provider or initial-monitor phase.
356
+ * @returns Completion of the requested phase.
357
+ */
358
+ async #requiredPrepare(onPrepare) {
359
+ serverEnvironmentAccess.requireAvailable(this.#environment);
360
+ try {
361
+ await onPrepare();
362
+ }
363
+ catch (error) {
364
+ serverEnvironmentAccess.failStartup(this.#environment, error);
365
+ throw error;
366
+ }
367
+ serverEnvironmentAccess.requireAvailable(this.#environment);
368
+ }
369
+ /**
370
+ * Traverses the current live index in finite pages without a tenant cap.
371
+ * @param mode Creation or read-only check.
372
+ * @returns Completion after currently listed tenants are prepared.
373
+ */
374
+ async #prepareCurrentTenants(mode) {
375
+ if (!this.isMultitenant)
376
+ return this.#requiredPrepare(() => this.#prepareTenant(undefined, mode));
377
+ let after;
378
+ do {
379
+ serverEnvironmentAccess.requireAvailable(this.#environment);
380
+ const page = await this.tenantIndex.page({
381
+ count: 64,
382
+ signal: this.#preparationController.signal,
383
+ ...(after === undefined ? {} : { after }),
384
+ });
385
+ serverEnvironmentAccess.requireAvailable(this.#environment);
386
+ for (const id of page.ids) {
387
+ serverEnvironmentAccess.requireAvailable(this.#environment);
388
+ await TenantAdmissions.require(this.tenantIndex, this.name.value, true, id);
389
+ serverEnvironmentAccess.requireAvailable(this.#environment);
390
+ await this.#requiredPrepare(() => this.#prepareTenant(id, mode));
391
+ }
392
+ after = page.hasMore ? page.after : undefined;
393
+ } while (after !== undefined);
394
+ }
395
+ /**
396
+ * Admits explicit structure work and joins fatal cleanup after local settlement.
397
+ * @param mode Creation or read-only compatibility mode.
398
+ * @param tenantIds Explicit complete tenant identities, if supplied.
399
+ * @returns Completion after the selected structures are ready.
400
+ */
401
+ async #explicitStructures(mode, tenantIds) {
402
+ const captured = tenantIds.map((id) => TenantBoundary.from(id).tenantId);
403
+ const admitted = boundedContextAccess.admitServiceRegistryOperation(this, () => this.#prepareExplicitLocal(mode, captured));
404
+ return this.#joinRequiredCleanup(admitted);
405
+ }
406
+ /**
407
+ * Runs explicit preparation while the caller holds this context's admission.
408
+ *
409
+ * @param mode Creation or read-only check.
410
+ * @param ids Captured tenant identities, when supplied.
411
+ * @returns Completion of the local preparation.
412
+ */
413
+ async #prepareExplicitLocal(mode, ids) {
414
+ const selected = await this.#selectedTenants(ids);
415
+ await this.#requiredPrepare(() => this.#prepareShared(mode));
416
+ if (selected === undefined)
417
+ return this.#prepareCurrentTenants(mode);
418
+ for (const id of selected) {
419
+ await TenantAdmissions.require(this.tenantIndex, this.name.value, this.isMultitenant, id);
420
+ await this.#requiredPrepare(() => this.#prepareTenant(id, mode));
421
+ }
422
+ }
423
+ /**
424
+ * Validates all explicit IDs before any provider preparation.
425
+ *
426
+ * @param ids Captured explicit tenant identities.
427
+ * @returns Distinct current IDs or undefined for all current tenants.
428
+ */
429
+ async #selectedTenants(ids) {
430
+ if (ids.length === 0)
431
+ return undefined;
432
+ const selected = new Map();
433
+ for (const id of ids) {
434
+ await TenantAdmissions.require(this.tenantIndex, this.name.value, this.isMultitenant, id);
435
+ const boundary = TenantBoundary.from(id);
436
+ selected.set(boundary.key, boundary.tenantId);
437
+ }
438
+ return [...selected.values()];
439
+ }
440
+ /**
441
+ * Starts the one initial monitor pass as an explicit required phase.
442
+ *
443
+ * @returns Completion after the initial pass.
444
+ */
445
+ #explicitMonitoring() {
446
+ const admitted = boundedContextAccess.admitServiceRegistryOperation(this, async () => {
447
+ await this.#requiredPrepare(() => this.#prepareShared("create"));
448
+ await this.#requiredPrepare(() => this.#subscriptionRuntime.start());
449
+ });
450
+ return this.#joinRequiredCleanup(admitted);
451
+ }
452
+ /**
453
+ * Waits for recorded fatal cleanup only after context admission settles.
454
+ *
455
+ * @param admitted Locally admitted operation.
456
+ * @returns Completion after local work or fatal cleanup.
457
+ */
458
+ async #joinRequiredCleanup(admitted) {
459
+ try {
460
+ await admitted;
461
+ }
462
+ catch (error) {
463
+ if (serverEnvironmentAccess.hasStartupFailure(this.#environment))
464
+ return serverEnvironmentAccess.awaitStartupFailure(this.#environment);
465
+ throw error;
466
+ }
467
+ }
214
468
  /**
215
469
  * Creates tenant-aware Inboxes and their shared readiness signal.
216
470
  *
217
- * @param storageFactory Provider supplying the tenant catalog.
471
+ * @param tenantIndex Effective membership index for this context.
218
472
  * @returns Inboxes, tenant index, and readiness signal.
219
473
  */
220
- #createDelivery(storageFactory) {
221
- const deliveryReadiness = new DeliveryReadiness();
222
- const tenantIndex = TenantIndexes.create({
223
- contextName: this.#snapshot.name.value,
224
- tenantMode: this.#snapshot.tenantMode,
225
- storageFactory,
226
- });
227
- const keepTenant = (tenantId) => tenantIndex.keep(tenantId);
228
- const entityInbox = new LocalEntityInbox(this.#snapshot.name.value, deliveryReadiness, keepTenant, this.#deliveryStrategy);
229
- const projectionInbox = new LocalProjectionInbox(this.#snapshot.name.value, deliveryReadiness, keepTenant);
474
+ #createDelivery(tenantIndex) {
475
+ const deliveryReadiness = this.#createReadiness();
476
+ const onAdmit = (tenantId) => this.#admitTenant(tenantId);
477
+ const entityInbox = new LocalEntityInbox(this.#snapshot.name.value, deliveryReadiness, onAdmit, this.#environment.delivery.strategy);
478
+ const projectionInbox = new LocalProjectionInbox(this.#snapshot.name.value, deliveryReadiness, onAdmit);
479
+ deliveryReadiness.configureDirect((message, tenantId) => message.label === "UPDATE_SUBSCRIBER"
480
+ ? projectionInbox.replay(message, tenantId)
481
+ : entityInbox.replay(message, tenantId));
230
482
  return { deliveryReadiness, tenantIndex, entityInbox, projectionInbox };
231
483
  }
484
+ /**
485
+ * Creates readiness for the shared Delivery and this context's Agent capacity.
486
+ * @returns Shared readiness with the Agent capacity wake installed.
487
+ */
488
+ #createReadiness() {
489
+ const readiness = new DeliveryReadiness(undefined, serverEnvironmentAccess.coreDelivery(this.#environment), (shard) => {
490
+ serverEnvironmentAccess.notifyDelivery(this.#environment, shard);
491
+ }, serverEnvironmentAccess.remoteSupervised(this.#environment));
492
+ if (this.#ai !== undefined)
493
+ this.#stopAgentAvailability = AgentExecutionCapacity.for(this.#ai).onAvailable(() => {
494
+ readiness.retryDeferred();
495
+ });
496
+ return readiness;
497
+ }
232
498
  /**
233
499
  * Installs framework references used after context construction.
234
500
  *
@@ -244,42 +510,29 @@ export class BoundedContext {
244
510
  contextTenantIndexes.set(this, delivery.tenantIndex);
245
511
  contextStorageFactories.set(this, input.storageFactory);
246
512
  contextSubscriptionRuntimes.set(this, this.#subscriptionRuntime);
513
+ contextPreparations.set(this, (options) => this.#prepareBuild(options));
514
+ contextInputPreparations.set(this, (tenantId) => this.#prepareInput(tenantId));
247
515
  contextSignalPublishers.set(this, this.#publisher);
248
516
  contextClosePhases.set(this, {
249
517
  begin: () => {
250
518
  this.#beginClose();
251
519
  },
252
520
  drain: () => this.#drainWork(),
521
+ trackReplay: (onReplay) => this.#trackReplay(onReplay),
253
522
  });
254
- contextDeliveryDescriptors.set(this, ContextParts.createDeliveryDescriptor(this.#snapshot, input.storageFactory, delivery.tenantIndex, this.#entityInbox, this.#projectionInbox, delivery.deliveryReadiness));
523
+ contextDeliveryDescriptors.set(this, ContextParts.createDeliveryDescriptor(this.#entityInbox, this.#projectionInbox));
255
524
  }
256
525
  /**
257
- * Registers repositories and starts subscriptions, closing the tenant index on failure.
258
- *
259
- * @param repositories Prepared repositories.
260
- * @param tenantIndex Index to close if registration fails.
526
+ * Prepares repository registrations, reserves their state types, and commits their Stand and
527
+ * inbox targets; rolls back uncommitted storage preparation on failure.
261
528
  */
262
- #registerAndStart(repositories, tenantIndex) {
263
- try {
264
- this.#registerRepositories(repositories);
265
- this.#subscriptionRuntime.start();
266
- }
267
- catch (error) {
268
- try {
269
- ContextParts.cleanupFailedContext(this, tenantIndex);
270
- }
271
- catch (cleanupError) {
272
- throw new AggregateError([error, cleanupError], "Bounded Context build failed, and tenant index cleanup also failed.");
273
- }
274
- throw error;
275
- }
276
- }
277
529
  #registerRepositories(repositories) {
278
530
  const preparedRepositories = this.#prepareRepositories(repositories);
279
531
  try {
280
532
  for (const preparedRepository of preparedRepositories) {
281
533
  ContextParts.rejectRegisteredRepository(preparedRepository.repository);
282
534
  }
535
+ this.#reserveStates(preparedRepositories);
283
536
  for (const preparedRepository of preparedRepositories) {
284
537
  this.#stand.register(preparedRepository.snapshot.stateSchema, {
285
538
  columns: ContextParts.repositoryColumns(preparedRepository.snapshot),
@@ -299,6 +552,23 @@ export class BoundedContext {
299
552
  this.#failRegistration(error, preparedRepositories);
300
553
  }
301
554
  }
555
+ /**
556
+ * Reserves Entity state types before registration mutates delivery targets.
557
+ *
558
+ * @param repositories Prepared repositories for this context.
559
+ */
560
+ #reserveStates(repositories) {
561
+ for (const repository of repositories) {
562
+ const stateType = TypeUrls.derive(repository.snapshot.stateSchema);
563
+ const prior = serverEnvironmentAccess.reserveState(this.#environment, this, stateType);
564
+ if (prior !== undefined)
565
+ throw new DuplicateEntityStateException(stateType, prior.name.value, this.name.value);
566
+ }
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
+ */
302
572
  #prepareRepositories(repositories) {
303
573
  const registration = this.#repositoryRegistration();
304
574
  const preparedRepositories = [];
@@ -312,14 +582,28 @@ export class BoundedContext {
312
582
  }
313
583
  return preparedRepositories;
314
584
  }
585
+ /**
586
+ * Reserves shared-registry capacity before Agent receipt admission.
587
+ */
588
+ #reserveAgent(key, signal) {
589
+ const reservation = this.#ai === undefined
590
+ ? undefined
591
+ : AgentExecutionCapacity.for(this.#ai).reserve(key, signal);
592
+ if (reservation !== undefined) {
593
+ this.#agentReservations.add(reservation.completed);
594
+ void reservation.completed.then(() => this.#agentReservations.delete(reservation.completed), () => this.#agentReservations.delete(reservation.completed));
595
+ }
596
+ return reservation;
597
+ }
315
598
  /**
316
599
  * Builds callbacks shared by every repository prepared for this Bounded Context.
317
600
  */
318
601
  #repositoryRegistration() {
319
- const registration = {
602
+ return {
320
603
  name: ContextParts.cloneName(this.#snapshot.name),
321
604
  storageContext: ContextParts.createStorageMode(this.#snapshot.spec),
322
605
  storageFactory: this.#storageFactory,
606
+ onAdmit: (tenantId) => this.#admitTenant(tenantId),
323
607
  ...(this.#ai === undefined ? {} : { ai: this.#ai }),
324
608
  stand: this.#stand,
325
609
  entityInbox: this.#entityInbox,
@@ -331,23 +615,42 @@ export class BoundedContext {
331
615
  registerSystemEventSchema: (schema) => {
332
616
  eventBusAccess.registerSchemas(this.#systemEventBus, [schema]);
333
617
  },
334
- publishAgentEvent: (event) => event.context?.rejection === undefined
335
- ? this.#publisher.publishEvent(event)
336
- : this.#publisher.publishRejectionEvent(event),
337
- prepareSavedCommand: (command) => commandBusAccess.prepareSaved(this.#commandBus, command),
338
- publishSavedCommand: (command, plan) => commandBusAccess.postSavedFollowUp(this.#commandBus, command, plan),
618
+ publishAgentEvent: (event) => this.#publishAgentEvent(event),
339
619
  publishAgentSystemEvent: (event) => this.#publisher.publishSystemEvent(event),
340
- recordAcceptedSaved: (signal) => {
341
- this.#publisher.recordAcceptedSaved(signal);
342
- },
343
- wakeAcceptedAgent: (repository, tenantId) => {
344
- this.#agentScheduler?.wake(this.#agentScope(repository, tenantId));
620
+ agentReceipts: this.#agentReceipts,
621
+ agentSignal: this.#agentController.signal,
622
+ reserveAgent: (key, signal) => this.#reserveAgent(key, signal),
623
+ deferAgent: (message, tenantId) => {
624
+ this.#deferAgent(message, tenantId);
345
625
  },
346
626
  };
347
- return registration;
348
627
  }
628
+ /**
629
+ * Publishes an Agent event on its selected ordinary or rejection bus.
630
+ */
631
+ #publishAgentEvent(event) {
632
+ return event.context?.rejection === undefined
633
+ ? this.#publisher.publishEvent(event)
634
+ : this.#publisher.publishRejectionEvent(event);
635
+ }
636
+ /**
637
+ * Records a deferred Agent shard for the next available capacity slot.
638
+ */
639
+ #deferAgent(message, tenantId) {
640
+ this.#deliveryReadiness.defer({
641
+ label: message.label,
642
+ targetTypeUrl: message.inboxId.targetTypeUrl,
643
+ shard: message.shard,
644
+ ...(tenantId === undefined ? {} : { tenantId }),
645
+ });
646
+ }
647
+ /**
648
+ * Closes storage for repositories that were prepared but not committed, then rethrows the
649
+ * registration error with cleanup failures aggregated.
650
+ */
349
651
  #failRegistration(error, preparedRepositories) {
350
- const closeErrors = ContextParts.closePreparedRepositories(preparedRepositories);
652
+ const uncommitted = preparedRepositories.filter(({ repository }) => !this.#repositoryViews.has(repository));
653
+ const closeErrors = ContextParts.closePreparedRepositories(uncommitted);
351
654
  if (closeErrors.length > 0) {
352
655
  throw new AggregateError([error, ...closeErrors], "Repository registration failed, and prepared repository storage cleanup also failed.");
353
656
  }
@@ -395,6 +698,14 @@ export class BoundedContext {
395
698
  get isMultitenant() {
396
699
  return this.#snapshot.tenantMode === "multitenant";
397
700
  }
701
+ /**
702
+ * Returns the live index configured for this Bounded Context.
703
+ *
704
+ * @returns The effective configured index.
705
+ */
706
+ get tenantIndex() {
707
+ return ContextParts.requireTenantIndex(this);
708
+ }
398
709
  /**
399
710
  * Returns a copy-safe specification used to build this context.
400
711
  *
@@ -497,6 +808,10 @@ export class BoundedContext {
497
808
  async catchUpReadSide(options = {}) {
498
809
  return eventBusAccess.runExclusive(this.#eventBus, () => this.#catchUpReadSideOnce(options));
499
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
+ */
500
815
  async #catchUpReadSideOnce(options) {
501
816
  const storageContext = ContextParts.catchUpStorageContext(this.#snapshot.spec, options);
502
817
  const tenantOptions = ContextParts.catchUpStandOptions(storageContext);
@@ -539,7 +854,9 @@ export class BoundedContext {
539
854
  */
540
855
  #beginClose() {
541
856
  closingContexts.add(this);
542
- this.#agentScheduler?.stop();
857
+ this.#preparationController.abort(new Error("Bounded Context closed."));
858
+ serverEnvironmentAccess.stopRecipients(this.#environment, this);
859
+ this.#agentController.abort(new Error("Bounded Context closed."));
543
860
  this.#publisher.beginClose();
544
861
  commandBusAccess.beginClose(this.#commandBus);
545
862
  eventBusAccess.beginClose(this.#eventBus);
@@ -565,22 +882,35 @@ export class BoundedContext {
565
882
  */
566
883
  async #closeOnce() {
567
884
  const errors = [];
568
- await this.#closePart(() => this.#agentScheduler?.close(), errors);
885
+ this.#stopAgentAvailability?.();
886
+ this.#stopAgentAvailability = undefined;
569
887
  await this.#closePart(() => this.#drainWork(), errors);
888
+ await this.#drainAdmittedContextWork(errors);
889
+ await this.#closePart(() => this.#deliveryReadiness.close(), errors);
890
+ await this.#closePart(() => ContextParts.drainContextWork(this.#commandBus, this.#eventBus, this.#systemEventBus, this.#publisher), errors);
891
+ await this.#closePart(() => this.#agentReceipts.close(), errors);
570
892
  await this.#closeBuses(errors);
571
893
  await this.#closePart(() => this.#stand.close(), errors);
572
894
  await this.#closePart(() => this.#systemStand.close(), errors);
573
895
  await this.#closePart(() => this.#subscriptionRuntime.finishClose(), errors);
574
- await this.#closePart(() => {
575
- ContextParts.requireTenantIndex(this).close();
576
- }, errors);
577
896
  await this.#closeRepositories(errors);
897
+ serverEnvironmentAccess.removeContext(this.#environment, this);
578
898
  if (errors.length > 0) {
579
899
  this.#closed = undefined;
580
900
  throw new AggregateError(ContextParts.flattenErrors(errors), "BoundedContext close failed.");
581
901
  }
582
902
  ContextParts.clearContextMetadata(this);
583
903
  }
904
+ /**
905
+ * Settles entered replay, local follow-ups, and reserved Agent turns.
906
+ * @param errors Collects independent close failures.
907
+ */
908
+ async #drainAdmittedContextWork(errors) {
909
+ await this.#closePart(() => Promise.allSettled([...this.#activePreparations]).then(() => undefined), errors);
910
+ await this.#closePart(() => Promise.allSettled([...this.#activeReplays]).then(() => undefined), errors);
911
+ await this.#closePart(() => this.#entityInbox.whenFollowUpsIdle(), errors);
912
+ await this.#closePart(() => Promise.allSettled([...this.#agentReservations]).then(() => undefined), errors);
913
+ }
584
914
  /**
585
915
  * Closes bus intake and subscription drains before the Stands.
586
916
  *
@@ -602,13 +932,27 @@ export class BoundedContext {
602
932
  * @param errors Failures collected during context closure.
603
933
  */
604
934
  async #closeRepositories(errors) {
605
- for (const repository of this.#repositoryViews) {
606
- await this.#closePart(() => {
607
- repositoryAccess.clearRuntime(repository);
935
+ const closing = [...this.#repositoryViews].map((repository) => {
936
+ return repositoryAccess.clearRuntime(repository).finally(() => {
608
937
  registeredRepositories.delete(repository);
609
- }, errors);
938
+ });
939
+ });
940
+ for (const result of await Promise.allSettled(closing)) {
941
+ if (result.status === "rejected")
942
+ errors.push(result.reason);
610
943
  }
611
944
  }
945
+ /**
946
+ * Registers an entered shared replay through its complete follow-up callback.
947
+ * @param onReplay Complete replay callback.
948
+ * @returns Completion of the captured callback.
949
+ */
950
+ #trackReplay(onReplay) {
951
+ const replay = Promise.resolve().then(onReplay);
952
+ this.#activeReplays.add(replay);
953
+ void replay.then(() => this.#activeReplays.delete(replay), () => this.#activeReplays.delete(replay));
954
+ return replay;
955
+ }
612
956
  /**
613
957
  * Calls a close action and collects its failure before later cleanup.
614
958
  * @param work Close action.
@@ -622,6 +966,73 @@ export class BoundedContext {
622
966
  * Exposes framework-only operations for built contexts and their builders.
623
967
  */
624
968
  export const boundedContextAccess = Object.freeze({
969
+ /**
970
+ * Reports whether close has started for the given context.
971
+ *
972
+ * @param context Completed context.
973
+ * @returns Whether close has started.
974
+ */
975
+ isClosing: (context) => closingContexts.has(context),
976
+ /**
977
+ * Prepares an admitted context without joining global fatal cleanup.
978
+ *
979
+ * @param context Admitted context.
980
+ * @param mode Creation or read-only check.
981
+ * @param ids Captured tenant identities.
982
+ * @returns Completion of local preparation.
983
+ */
984
+ prepareExplicit(context, mode, ids) {
985
+ const prepare = explicitPreparations.get(context);
986
+ if (prepare === undefined)
987
+ return Promise.reject(new TypeError("Preparation requires a built context."));
988
+ return prepare(mode, ids);
989
+ },
990
+ /**
991
+ * Admits a service membership and preparation chain before its first await.
992
+ *
993
+ * @param context Selected built context.
994
+ * @param tenantId Captured tenant identity, when applicable.
995
+ * @param onMembership Checks live tenant membership.
996
+ * @param onReady Performs the admitted one-shot registry mutation, if present.
997
+ * @returns Tracked completion of membership and model preparation.
998
+ */
999
+ admitServiceInput(context, tenantId, onMembership, onReady) {
1000
+ if (closingContexts.has(context))
1001
+ return Promise.reject(new Error("Bounded Context is closing."));
1002
+ const prepare = contextInputPreparations.get(context);
1003
+ const phase = contextClosePhases.get(context);
1004
+ if (prepare === undefined || phase === undefined)
1005
+ return Promise.reject(new TypeError("Service input requires a built context."));
1006
+ return phase.trackReplay(async () => {
1007
+ await onMembership();
1008
+ await prepare(tenantId);
1009
+ await onReady?.();
1010
+ });
1011
+ },
1012
+ /**
1013
+ * Admits one shared subscription-registry operation before asynchronous lookup.
1014
+ *
1015
+ * @param context Selected built context.
1016
+ * @param onOperation Complete registry operation.
1017
+ * @returns Tracked operation completion.
1018
+ */
1019
+ admitServiceRegistryOperation(context, onOperation) {
1020
+ if (closingContexts.has(context))
1021
+ return Promise.reject(new Error("Bounded Context is closing."));
1022
+ const phase = contextClosePhases.get(context);
1023
+ if (phase === undefined)
1024
+ return Promise.reject(new TypeError("Service registry requires a built context."));
1025
+ return phase.trackReplay(onOperation);
1026
+ },
1027
+ /**
1028
+ * Registers replay work with the context close lifecycle so shutdown waits for it.
1029
+ */
1030
+ trackReplay(context, onReplay) {
1031
+ const phase = contextClosePhases.get(context);
1032
+ if (phase === undefined)
1033
+ return Promise.reject(new TypeError("Replay requires a built context."));
1034
+ return phase.trackReplay(onReplay);
1035
+ },
625
1036
  /**
626
1037
  * Resolves a view only within the Bounded Context that issued it.
627
1038
  *
@@ -638,18 +1049,27 @@ export const boundedContextAccess = Object.freeze({
638
1049
  ? issued.repository
639
1050
  : undefined;
640
1051
  },
1052
+ /**
1053
+ * Stops new work at this Bounded Context and begins shutdown of its runtime facilities.
1054
+ */
641
1055
  beginClose(context) {
642
1056
  const phase = contextClosePhases.get(context);
643
1057
  if (phase === undefined)
644
1058
  throw new TypeError("Close phase requires a built BoundedContext.");
645
1059
  phase.begin();
646
1060
  },
1061
+ /**
1062
+ * Waits for accepted runtime work and replay operations to finish.
1063
+ */
647
1064
  drainWork(context) {
648
1065
  const phase = contextClosePhases.get(context);
649
1066
  if (phase === undefined)
650
1067
  return Promise.reject(new TypeError("Drain requires a built BoundedContext."));
651
1068
  return phase.drain();
652
1069
  },
1070
+ /**
1071
+ * Installs the context logger on both EventBuses, the signal publisher, and subscription runtime.
1072
+ */
653
1073
  installLogger(context, logger) {
654
1074
  if (!contextStorageFactories.has(context)) {
655
1075
  throw new TypeError("Context logger requires a built BoundedContext instance.");
@@ -669,8 +1089,11 @@ export const boundedContextAccess = Object.freeze({
669
1089
  if (runtime === undefined) {
670
1090
  throw new TypeError("Context logger requires a built BoundedContext instance.");
671
1091
  }
672
- subscriptionRuntimeAccess.installLogger(runtime, logger);
1092
+ subscriptionRuntimeAccess.installLogger(runtime, logger, context.name.value);
673
1093
  },
1094
+ /**
1095
+ * Returns the diagnostic logger installed on this Bounded Context.
1096
+ */
674
1097
  loggerFor(context) {
675
1098
  const logger = contextLoggers.get(context);
676
1099
  if (logger === undefined) {
@@ -678,18 +1101,27 @@ export const boundedContextAccess = Object.freeze({
678
1101
  }
679
1102
  return logger;
680
1103
  },
1104
+ /**
1105
+ * Checks whether a value is a BoundedContextBuilder created by this module.
1106
+ */
681
1107
  isBuilder(value) {
682
1108
  return (typeof value === "object" &&
683
1109
  value !== null &&
684
1110
  builderBuilds.has(value));
685
1111
  },
686
- build(builder, defaultStorageFactory, defaultAi) {
1112
+ /**
1113
+ * Builds the supplied context builder and completes its configured initialization.
1114
+ */
1115
+ build(builder) {
687
1116
  const build = builderBuilds.get(builder);
688
1117
  if (build === undefined) {
689
1118
  throw new TypeError("Builder access requires a BoundedContextBuilder instance.");
690
1119
  }
691
- return build(defaultStorageFactory, defaultAi);
1120
+ return build();
692
1121
  },
1122
+ /**
1123
+ * Attaches a subscriber to the context domain EventBus for the requested event type.
1124
+ */
693
1125
  subscribeToEvent(context, typeUrl, subscriber) {
694
1126
  const subscribe = eventSubscribers.get(context);
695
1127
  if (subscribe === undefined) {
@@ -697,6 +1129,9 @@ export const boundedContextAccess = Object.freeze({
697
1129
  }
698
1130
  return subscribe(typeUrl, subscriber);
699
1131
  },
1132
+ /**
1133
+ * Posts a framework System Event through the context System EventBus.
1134
+ */
700
1135
  postSystemEvent(context, event) {
701
1136
  const post = systemEventPosters.get(context);
702
1137
  if (post === undefined) {
@@ -704,12 +1139,18 @@ export const boundedContextAccess = Object.freeze({
704
1139
  }
705
1140
  return post(event);
706
1141
  },
1142
+ /**
1143
+ * Posts an external domain Event through the context EventBus.
1144
+ */
707
1145
  postExternalEvent(context, event) {
708
1146
  const imported = clone(EventSchema, event);
709
1147
  imported.context = clone(EventContextSchema, imported.context ?? create(EventContextSchema));
710
1148
  imported.context.external = true;
711
1149
  return context.eventBus().post(imported);
712
1150
  },
1151
+ /**
1152
+ * Subscribes to events produced by this context and forwards matching signals to the supplied observer.
1153
+ */
713
1154
  observeProducedSignals(context, observer) {
714
1155
  const publisher = contextSignalPublishers.get(context);
715
1156
  if (publisher === undefined) {
@@ -717,12 +1158,21 @@ export const boundedContextAccess = Object.freeze({
717
1158
  }
718
1159
  return publisher.observe(observer);
719
1160
  },
1161
+ /**
1162
+ * Returns the domain/System specification pairing captured when the context was built.
1163
+ */
720
1164
  systemPairing(context) {
721
1165
  return ContextParts.cloneSystemPairing(ContextParts.requireSystemPairing(context));
722
1166
  },
1167
+ /**
1168
+ * Returns the live tenant membership index configured for this context.
1169
+ */
723
1170
  tenantIndex(context) {
724
1171
  return ContextParts.requireTenantIndex(context);
725
1172
  },
1173
+ /**
1174
+ * Returns the storage factory captured for repository and Stand operations in this context.
1175
+ */
726
1176
  storageFactory(context) {
727
1177
  const storageFactory = contextStorageFactories.get(context);
728
1178
  if (storageFactory === undefined) {
@@ -730,6 +1180,9 @@ export const boundedContextAccess = Object.freeze({
730
1180
  }
731
1181
  return storageFactory;
732
1182
  },
1183
+ /**
1184
+ * Returns the persistent or in-memory registry used to store Stand subscription definitions.
1185
+ */
733
1186
  subscriptionRegistry(context) {
734
1187
  const runtime = contextSubscriptionRuntimes.get(context);
735
1188
  if (runtime === undefined) {
@@ -737,13 +1190,24 @@ export const boundedContextAccess = Object.freeze({
737
1190
  }
738
1191
  return runtime.registry();
739
1192
  },
740
- 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) {
741
1197
  const runtime = contextSubscriptionRuntimes.get(context);
742
1198
  if (runtime === undefined) {
743
1199
  throw new TypeError("Subscription consumption requires a built BoundedContext instance.");
744
1200
  }
745
- 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);
746
1207
  },
1208
+ /**
1209
+ * Returns the context delivery descriptor used to route Entity and Projection inbox work.
1210
+ */
747
1211
  delivery(context) {
748
1212
  const descriptor = contextDeliveryDescriptors.get(context);
749
1213
  if (descriptor === undefined) {
@@ -764,13 +1228,12 @@ export class BoundedContextBuilder {
764
1228
  #eventReceivers = [];
765
1229
  #repositories = new Set();
766
1230
  #entityTypes = new Set();
1231
+ #readFilters = new Set();
767
1232
  #generatedRepositoryOptions = new Map();
768
- #deliveryStrategy = UniformAcrossAllShards.singleShard();
769
- #storageFactory;
770
1233
  #aiRegistry;
1234
+ #tenantIndex;
771
1235
  #subscriptionRegistry;
772
1236
  #persistSystemEvents = false;
773
- #generatedRegistryRoot;
774
1237
  /**
775
1238
  * Registers the framework-only construction hook for this module.
776
1239
  */
@@ -786,7 +1249,7 @@ export class BoundedContextBuilder {
786
1249
  constructor(specSnapshot, token) {
787
1250
  ContextParts.requireFrameworkConstructionToken(token, "BoundedContextBuilder instances are framework-owned.");
788
1251
  this.#specSnapshot = ContextParts.cloneSpecSnapshot(specSnapshot);
789
- builderBuilds.set(this, (defaultStorageFactory, defaultAi) => this.#buildAsyncWith(defaultStorageFactory, defaultAi));
1252
+ builderBuilds.set(this, () => this.#buildAsyncWith());
790
1253
  Object.freeze(this);
791
1254
  }
792
1255
  /**
@@ -821,6 +1284,15 @@ export class BoundedContextBuilder {
821
1284
  isMultitenant() {
822
1285
  return this.#specSnapshot.multitenant;
823
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
+ }
824
1296
  /**
825
1297
  * Adds an explicitly assembled repository or an Entity class.
826
1298
  *
@@ -861,7 +1333,7 @@ export class BoundedContextBuilder {
861
1333
  *
862
1334
  * @param dispatcher Raw command dispatcher, or an `AbstractCommander` instance.
863
1335
  * A standalone commander requires generated receiver metadata and therefore
864
- * this builder's `buildAsync()` path. It installs its Command and Event sides
1336
+ * this builder's `build()` path. It installs its Command and Event sides
865
1337
  * exactly once.
866
1338
  *
867
1339
  * @returns This builder for further configuration.
@@ -888,7 +1360,7 @@ export class BoundedContextBuilder {
888
1360
  * Adds an event dispatcher to the context being built.
889
1361
  *
890
1362
  * @param dispatcher Raw event dispatcher, or a standalone reactor/subscriber.
891
- * Standalone receivers require generated receiver metadata and `buildAsync()`.
1363
+ * Standalone receivers require generated receiver metadata and `build()`.
892
1364
  *
893
1365
  * @returns This builder for further configuration.
894
1366
  */
@@ -905,7 +1377,7 @@ export class BoundedContextBuilder {
905
1377
  * Adds a generated standalone command assignee.
906
1378
  *
907
1379
  * The registered instance is matched by exact constructor to generated
908
- * receiver metadata when `buildAsync()` assembles the context.
1380
+ * receiver metadata when `build()` assembles the context.
909
1381
  *
910
1382
  * @param assignee Generated standalone assignee instance.
911
1383
  * @returns This builder for further configuration.
@@ -925,13 +1397,13 @@ export class BoundedContextBuilder {
925
1397
  return this;
926
1398
  }
927
1399
  /**
928
- * Sets a storage factory for event, repository-state, and Stand storage.
1400
+ * Sets a live tenant index for this Bounded Context.
929
1401
  *
930
- * @param storageFactory Creates the context's persistent storage.
931
- * @returns Returns this builder for further configuration.
1402
+ * @param tenantIndex Application membership index.
1403
+ * @returns This builder.
932
1404
  */
933
- withStorageFactory(storageFactory) {
934
- this.#storageFactory = storageFactory;
1405
+ withTenantIndex(tenantIndex) {
1406
+ this.#tenantIndex = tenantIndex;
935
1407
  return this;
936
1408
  }
937
1409
  /**
@@ -970,69 +1442,112 @@ export class BoundedContextBuilder {
970
1442
  return this;
971
1443
  }
972
1444
  /**
973
- * Sets how Entity Inbox targets are assigned to shards.
1445
+ * Builds a context after loading generated handlers and preparing runtime resources.
974
1446
  *
975
- * @param strategy Selects the durable shard for Aggregate and Process Manager targets.
976
- * @returns Returns this builder for further configuration.
1447
+ * @param options Independent eager structure and subscription timing choices.
1448
+ * @returns The built context after requested initialization and integration are ready.
977
1449
  */
978
- withDeliveryStrategy(strategy) {
979
- this.#deliveryStrategy = ContextParts.snapshotDeliveryStrategy(strategy);
980
- return this;
1450
+ build(options = {}) {
1451
+ return this.#buildAsyncWith(options);
981
1452
  }
982
1453
  /**
983
- * Sets a trusted compiled application root for generated handler metadata.
984
- *
985
- * @param root Names the compiled package or application root.
986
- * @returns Returns this builder for further configuration.
1454
+ * Loads generated handlers and repositories, assembles context facilities, and completes
1455
+ * requested initialization before publishing the context.
987
1456
  */
988
- withGeneratedRegistryRoot(root) {
989
- this.#generatedRegistryRoot = root;
990
- return this;
1457
+ async #buildAsyncWith(options = {}) {
1458
+ const environment = ServerEnvironment.instance();
1459
+ const finishBuild = serverEnvironmentAccess.beginContextBuild(environment);
1460
+ let built;
1461
+ let failed = false;
1462
+ let failure;
1463
+ try {
1464
+ await serverEnvironmentAccess.awaitSetup(environment);
1465
+ built = await this.#buildAsyncReady(options, environment);
1466
+ }
1467
+ catch (error) {
1468
+ failed = true;
1469
+ failure = error;
1470
+ }
1471
+ finally {
1472
+ finishBuild();
1473
+ }
1474
+ if (serverEnvironmentAccess.hasStartupFailure(environment))
1475
+ return serverEnvironmentAccess.awaitStartupFailure(environment);
1476
+ if (failed)
1477
+ throw failure;
1478
+ if (built === undefined)
1479
+ throw new Error("Bounded Context build did not complete.");
1480
+ return built;
991
1481
  }
992
1482
  /**
993
- * Builds a context from explicitly added repositories and dispatchers.
1483
+ * Loads generated metadata and finishes one asynchronous context assembly.
994
1484
  *
995
- * @returns Returns the built context.
1485
+ * @returns The integrated context.
996
1486
  */
997
- build() {
998
- if (this.#standaloneInstances().length > 0) {
999
- throw new Error("Standalone generated handlers require buildAsync().");
1487
+ async #buildAsyncReady(options, environment) {
1488
+ const generated = this.#loadGeneratedArtifacts([...this.#entityTypes]);
1489
+ serverEnvironmentAccess.requireAvailable(environment);
1490
+ const repositories = [...this.#repositories, ...generated.repositories];
1491
+ const context = await this.#buildWithResources(repositories, environment.storageFactory, generated.standalone, this.#aiRegistry ?? environment.ai);
1492
+ try {
1493
+ await this.#integrateBuiltContext(context, options, environment);
1494
+ }
1495
+ catch (error) {
1496
+ return this.#closeFailedBuild(context, environment, error);
1000
1497
  }
1001
- ContextParts.rejectSyncEntityAssembly(this.#entityTypes);
1002
- return this.#buildWith([...this.#repositories], this.#storageFactory ?? new InMemoryStorageFactory(), [], this.#aiRegistry);
1498
+ return context;
1003
1499
  }
1004
1500
  /**
1005
- * Builds a context after loading generated metadata for added entity classes
1006
- * and registered standalone handlers.
1007
- *
1008
- * Standalone assignees, commanders, reactors, and subscribers require this
1009
- * asynchronous path so their exact constructors can be matched to generated
1010
- * receiver metadata.
1011
- *
1012
- * @returns Resolves to the built context.
1501
+ * Completes required integration and requested initialization before publication.
1502
+ * @param context Constructed context.
1503
+ * @param options Requested eager initialization phases.
1504
+ * @param environment Captured environment lifetime.
1013
1505
  */
1014
- async buildAsync() {
1015
- return this.#buildAsyncWith();
1016
- }
1017
- async #buildAsyncWith(defaultStorageFactory, defaultAi) {
1018
- const generated = await this.#loadGeneratedArtifacts([...this.#entityTypes]);
1019
- const repositories = [...this.#repositories, ...generated.repositories];
1020
- const context = this.#buildWith(repositories, this.#storageFactory ?? defaultStorageFactory ?? new InMemoryStorageFactory(), generated.standalone, this.#aiRegistry ?? defaultAi);
1506
+ async #integrateBuiltContext(context, options, environment) {
1507
+ serverEnvironmentAccess.requireAvailable(environment);
1508
+ boundedContextAccess.installLogger(context, serverEnvironmentAccess.loggerFor(environment));
1509
+ await ContextParts.integrationReady(context).catch((error) => {
1510
+ serverEnvironmentAccess.failStartup(environment, error);
1511
+ throw error;
1512
+ });
1513
+ serverEnvironmentAccess.requireAvailable(environment);
1514
+ const prepare = contextPreparations.get(context);
1515
+ if (prepare === undefined)
1516
+ throw new Error("Context preparation is unavailable.");
1517
+ await prepare(options);
1518
+ serverEnvironmentAccess.requireAvailable(environment);
1519
+ await serverEnvironmentAccess.completeContext(environment, context);
1520
+ }
1521
+ /**
1522
+ * Releases a failed partial context before its build ticket settles.
1523
+ * @param context Context assembled before the failure.
1524
+ * @param environment Captured environment lifetime.
1525
+ * @param error Original build failure.
1526
+ * @returns Rejection retaining the original and any local cleanup failure.
1527
+ */
1528
+ async #closeFailedBuild(context, environment, error) {
1021
1529
  try {
1022
- await ContextParts.integrationReady(context);
1530
+ await context.close();
1023
1531
  }
1024
- catch (error) {
1025
- try {
1026
- await context.close();
1027
- }
1028
- catch (closeError) {
1029
- throw new AggregateError([error, closeError], "BoundedContext broker open failed during cleanup.");
1532
+ catch (closeError) {
1533
+ if (serverEnvironmentAccess.hasStartupFailure(environment)) {
1534
+ serverEnvironmentAccess.recordStartupCleanup(environment, closeError);
1535
+ throw error;
1030
1536
  }
1031
- throw error;
1537
+ throw new AggregateError([error, closeError], "BoundedContext open cleanup failed.");
1032
1538
  }
1033
- return context;
1539
+ throw error;
1034
1540
  }
1035
- #buildWith(repositories, storageFactory, standalone = [], ai) {
1541
+ /**
1542
+ * Assembles a context and cleans partial resources on failure.
1543
+ *
1544
+ * @param repositories Repositories selected for the context.
1545
+ * @param storageFactory Storage factory for the context.
1546
+ * @param standalone Generated standalone handler groups.
1547
+ * @param ai Optional AI registry.
1548
+ * @returns The assembled context.
1549
+ */
1550
+ async #buildWithResources(repositories, storageFactory, standalone, ai) {
1036
1551
  const resources = this.#subscriptionRegistry === undefined ? {} : { registry: this.#subscriptionRegistry };
1037
1552
  this.#subscriptionRegistry = undefined;
1038
1553
  const registeredRepositories = [...repositories];
@@ -1040,9 +1555,13 @@ export class BoundedContextBuilder {
1040
1555
  return this.#assembleContext(registeredRepositories, storageFactory, standalone, resources, ai);
1041
1556
  }
1042
1557
  catch (error) {
1043
- return this.#failBuild(resources, error);
1558
+ return await this.#failBuild(resources, error);
1044
1559
  }
1045
1560
  }
1561
+ /**
1562
+ * Assembles dispatchers, buses, Stand, subscription runtime, and integration hooks, registering
1563
+ * the completed context with its environment.
1564
+ */
1046
1565
  #assembleContext(repositories, storageFactory, standalone, resources, ai) {
1047
1566
  ContextParts.preflightRepositories(repositories);
1048
1567
  this.#preflightAgents(repositories, storageFactory, ai);
@@ -1059,6 +1578,10 @@ export class BoundedContextBuilder {
1059
1578
  ]);
1060
1579
  const running = this.#buildRuntime(storageFactory, buses, resources);
1061
1580
  const context = this.#createContext(repositories, storageFactory, buses, running, ai);
1581
+ resources.context = context;
1582
+ if (registerBoundedContext === undefined)
1583
+ throw new Error("Context registration is unavailable.");
1584
+ registerBoundedContext(context, repositories);
1062
1585
  ContextParts.attachIntegration(context, buses.eventBus, buses.systemSpec, ContextParts.externalEventSchemas([
1063
1586
  ...dispatchers.domain,
1064
1587
  ...(standaloneEvent === undefined ? [] : [standaloneEvent]),
@@ -1083,13 +1606,8 @@ export class BoundedContextBuilder {
1083
1606
  throw new TypeError("Agent registration requires persisted System Events.");
1084
1607
  if (!AgentHistoryStorageFactories.supports(storageFactory))
1085
1608
  throw new TypeError("Agent registration requires indexed history storage.");
1086
- if (!AgentExecutionStorageFactories.supports(storageFactory))
1087
- throw new TypeError("Agent registration requires durable execution storage.");
1088
- this.#checkAgentOutputBindings(agents);
1089
1609
  for (const repository of agents) {
1090
1610
  const config = repositoryAccess.agentConfiguration(repository);
1091
- if (config.codeRevision === undefined || config.codeRevision.trim() === "")
1092
- throw new TypeError("Agent registration requires a code revision.");
1093
1611
  if (config.ai === undefined)
1094
1612
  throw new TypeError("Agent registration requires repository capabilities.");
1095
1613
  if (config.ai.models.some((model) => !isAiModel(model)))
@@ -1097,17 +1615,8 @@ export class BoundedContextBuilder {
1097
1615
  }
1098
1616
  }
1099
1617
  /**
1100
- * Rejects Agent outcomes whose raw matching dispatcher cannot bind a saved plan.
1618
+ * Collects repository and configured event recipients into domain and System dispatcher groups.
1101
1619
  */
1102
- #checkAgentOutputBindings(agents) {
1103
- const rawCommands = new Set([...this.#commandDispatchers].flatMap((dispatcher) => dispatcher.messageSchemas().map((schema) => TypeUrls.derive(schema))));
1104
- for (const repository of agents) {
1105
- if (repositoryAccess
1106
- .producedCommandSchemas(repository)
1107
- .some((schema) => rawCommands.has(TypeUrls.derive(schema))))
1108
- throw new TypeError("Agent Command output requires a durable dispatcher binding.");
1109
- }
1110
- }
1111
1620
  #buildDispatchers(repositories) {
1112
1621
  const event = [
1113
1622
  ...ContextParts.repositoryEventDispatchers(repositories),
@@ -1121,6 +1630,9 @@ export class BoundedContextBuilder {
1121
1630
  ],
1122
1631
  };
1123
1632
  }
1633
+ /**
1634
+ * Creates command, domain Event, and System Event buses plus their stores and signal publisher.
1635
+ */
1124
1636
  #buildBuses(repositories, storageFactory, dispatchers, resources) {
1125
1637
  const commandBus = new CommandBus([
1126
1638
  ...this.#commandDispatchers,
@@ -1145,10 +1657,13 @@ export class BoundedContextBuilder {
1145
1657
  systemSpec: system.spec,
1146
1658
  };
1147
1659
  }
1660
+ /**
1661
+ * Creates the System context store, EventBus, and Stand, then registers System event dispatchers.
1662
+ */
1148
1663
  #buildSystemBuses(storageFactory, dispatchers, resources) {
1149
1664
  const systemSpec = ContextParts.createSystemSpec(this.#specSnapshot, this.#persistSystemEvents);
1150
1665
  const systemEventStore = systemSpec.storesEvents
1151
- ? new EventStore(ContextParts.createStorageMode(systemSpec), storageFactory, systemEventGroup)
1666
+ ? new EventStore(ContextParts.createStorageMode(systemSpec), storageFactory, EventStoreGroups.forContext(systemSpec.name.value, "system"))
1152
1667
  : undefined;
1153
1668
  if (systemEventStore !== undefined)
1154
1669
  resources.systemEventStore = systemEventStore;
@@ -1168,6 +1683,10 @@ export class BoundedContextBuilder {
1168
1683
  spec: systemSpec,
1169
1684
  };
1170
1685
  }
1686
+ /**
1687
+ * Matches generated standalone handlers to configured instances and registers their dispatchers
1688
+ * on the appropriate buses.
1689
+ */
1171
1690
  #installStandalone(standalone, buses) {
1172
1691
  ContextParts.assertUniqueCommandReceptors(standalone);
1173
1692
  const runtime = standalone.length === 0
@@ -1184,59 +1703,98 @@ export class BoundedContextBuilder {
1184
1703
  buses.systemEventBus.register(state);
1185
1704
  return event;
1186
1705
  }
1706
+ /**
1707
+ * Creates the context Stand, subscription registry, and runtime that observes domain and System events.
1708
+ */
1187
1709
  #buildRuntime(storageFactory, buses, resources) {
1188
- const stand = new Stand({
1189
- context: ContextParts.createStorageMode(this.#specSnapshot),
1190
- storageFactory,
1191
- });
1710
+ const stand = this.#buildStand(storageFactory);
1192
1711
  resources.stand = stand;
1193
1712
  const registry = resources.registry ??
1194
- new StorageSubscriptionRegistry(ContextParts.createSubscriptionStorageContext(this.#specSnapshot), storageFactory);
1713
+ new StorageSubscriptionRegistry(ContextParts.createSubscriptionStorageContext(this.#specSnapshot), storageFactory, subscriptionRecordGroup(this.#specSnapshot.name.value));
1195
1714
  resources.registry = registry;
1196
1715
  const runtime = new SubscriptionRuntime(stand, buses.systemStand, buses.eventBus, buses.systemEventBus, registry);
1197
1716
  resources.runtime = runtime;
1198
1717
  return { stand, registry, runtime };
1199
1718
  }
1719
+ /**
1720
+ * Builds the context Stand with a captured live membership directory.
1721
+ *
1722
+ * @param storageFactory Provider used for Entity records.
1723
+ * @returns Direct read-side Stand.
1724
+ */
1725
+ #buildStand(storageFactory) {
1726
+ const tenantIndex = this.#tenantIndex ?? ServerEnvironment.instance().tenantIndex;
1727
+ const stand = new Stand({
1728
+ context: ContextParts.createStorageMode(this.#specSnapshot),
1729
+ storageFactory,
1730
+ });
1731
+ if (this.#readFilters.size > 0)
1732
+ standAccess.installReadFilters(stand, [...this.#readFilters]);
1733
+ standAccess.setAdmission(stand, (tenantId) => TenantAdmissions.require(tenantIndex, this.#specSnapshot.name.value, this.#specSnapshot.multitenant, tenantId));
1734
+ return stand;
1735
+ }
1736
+ /**
1737
+ * Constructs the immutable BoundedContext using assembled buses, runtime parts, environment
1738
+ * delivery strategy, and tenant index.
1739
+ */
1200
1740
  #createContext(repositories, storageFactory, buses, running, ai) {
1201
- return ContextParts.createBoundedContext(this.#specSnapshot, storageFactory, repositories, this.#deliveryStrategy, buses, running, ai);
1741
+ return ContextParts.createBoundedContext(this.#specSnapshot, storageFactory, repositories, ServerEnvironment.instance().delivery.strategy, buses, running, ai, this.#tenantIndex ?? ServerEnvironment.instance().tenantIndex);
1202
1742
  }
1203
- #failBuild(resources, error) {
1743
+ /**
1744
+ * Closes partial context resources after assembly fails and aggregates cleanup errors with the original failure.
1745
+ */
1746
+ async #failBuild(resources, error) {
1204
1747
  const cleanupErrors = [];
1205
- ContextParts.attemptCleanup(() => resources.runtime?.abortClose(), cleanupErrors);
1206
- ContextParts.attemptCleanup(() => resources.publisher?.abortAssembly(), cleanupErrors);
1207
- if (resources.runtime === undefined) {
1208
- ContextParts.attemptCleanup(() => void resources.registry?.close().catch(() => undefined), cleanupErrors);
1209
- }
1210
- ContextParts.attemptCleanup(() => void resources.stand?.close().catch(() => undefined), cleanupErrors);
1211
- ContextParts.attemptCleanup(() => void resources.systemStand?.close().catch(() => undefined), cleanupErrors);
1212
- ContextParts.cleanupBuildBuses(resources, cleanupErrors);
1748
+ if (resources.context !== undefined) {
1749
+ try {
1750
+ await resources.context.close();
1751
+ }
1752
+ catch (closeError) {
1753
+ cleanupErrors.push(closeError);
1754
+ }
1755
+ }
1756
+ else {
1757
+ ContextParts.attemptCleanup(() => resources.publisher?.abortAssembly(), cleanupErrors);
1758
+ const registry = resources.runtime ?? resources.registry;
1759
+ const closing = [registry, resources.stand, resources.systemStand];
1760
+ for (const resource of closing) {
1761
+ if (resource === undefined)
1762
+ continue;
1763
+ try {
1764
+ await resource.close();
1765
+ }
1766
+ catch (closeError) {
1767
+ cleanupErrors.push(closeError);
1768
+ }
1769
+ }
1770
+ await ContextParts.cleanupBuildBuses(resources, cleanupErrors);
1771
+ }
1213
1772
  if (cleanupErrors.length > 0) {
1214
1773
  throw new AggregateError([error, ...cleanupErrors], "Bounded Context build failed during cleanup.");
1215
1774
  }
1216
1775
  throw error;
1217
1776
  }
1218
- 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) {
1219
1782
  if (entityTypes.length === 0 && this.#standaloneInstances().length === 0) {
1220
1783
  return Object.freeze({ repositories: Object.freeze([]), standalone: Object.freeze([]) });
1221
1784
  }
1222
- const root = ContextParts.requireGeneratedRegistryRoot(this.#generatedRegistryRoot);
1223
- const discovery = new GeneratedRegistryDiscovery();
1224
- const registryModule = await ContextParts.trustedGeneratedRegistryModule(root);
1225
- const registryKey = registryModule.href;
1226
- const registries = (await discovery
1227
- .load({
1228
- modules: [registryModule],
1229
- ...ContextParts.generatedRegistryCacheBust(registryKey),
1230
- })
1231
- .catch((error) => {
1232
- ContextParts.recordGeneratedRegistryFailure(registryKey);
1233
- throw error;
1234
- }));
1785
+ const selected = [
1786
+ ...entityTypes,
1787
+ ...this.#standaloneInstances().map((instance) => instance.constructor),
1788
+ ];
1789
+ const registries = [SpineMetadataInventory.select(selected)];
1235
1790
  const metadata = ContextParts.ingestGeneratedRegistries(registries);
1236
1791
  const repositories = Object.freeze(entityTypes.map((entityType) => ContextParts.createGeneratedRepository(entityType, registries, metadata, this.#generatedRepositoryOptions.get(entityType))));
1237
1792
  const standalone = Object.freeze(registries.flatMap((registry) => registry.receivers.filter((receiver) => receiver.receiverKind === "standalone")));
1238
1793
  return Object.freeze({ repositories, standalone });
1239
1794
  }
1795
+ /**
1796
+ * Returns a frozen list of configured standalone assignee, commander, and event-receiver instances.
1797
+ */
1240
1798
  #standaloneInstances() {
1241
1799
  return Object.freeze([...this.#assignees, ...this.#commanders, ...this.#eventReceivers]);
1242
1800
  }
@@ -1409,24 +1967,29 @@ const ContextParts = Object.freeze({
1409
1967
  *
1410
1968
  * @param resources Partially created resources.
1411
1969
  * @param errors Collection receiving failures.
1412
- */
1413
- cleanupBuildBuses(resources, errors) {
1414
- ContextParts.attemptCleanup(() => {
1415
- if (resources.commandBus !== undefined)
1416
- commandBusAccess.abortClose(resources.commandBus);
1417
- }, errors);
1418
- ContextParts.attemptCleanup(() => {
1419
- if (resources.systemEventBus !== undefined)
1420
- eventBusAccess.abortClose(resources.systemEventBus);
1421
- else
1422
- resources.systemEventStore?.close();
1423
- }, errors);
1424
- ContextParts.attemptCleanup(() => {
1425
- if (resources.eventBus !== undefined)
1426
- eventBusAccess.abortClose(resources.eventBus);
1427
- else
1428
- resources.eventStore?.close();
1429
- }, errors);
1970
+ * @returns Completion after all created buses and stores close.
1971
+ */
1972
+ async cleanupBuildBuses(resources, errors) {
1973
+ const closing = [];
1974
+ const { commandBus, systemEventBus, systemEventStore, eventBus, eventStore } = resources;
1975
+ if (commandBus !== undefined)
1976
+ closing.push(() => commandBus.close());
1977
+ if (systemEventBus !== undefined)
1978
+ closing.push(() => systemEventBus.close());
1979
+ else if (systemEventStore !== undefined)
1980
+ closing.push(() => systemEventStore.close());
1981
+ if (eventBus !== undefined)
1982
+ closing.push(() => eventBus.close());
1983
+ else if (eventStore !== undefined)
1984
+ closing.push(() => eventStore.close());
1985
+ for (const close of closing) {
1986
+ try {
1987
+ await close();
1988
+ }
1989
+ catch (error) {
1990
+ errors.push(error);
1991
+ }
1992
+ }
1430
1993
  },
1431
1994
  /**
1432
1995
  * Returns constituent failures from nested aggregate errors.
@@ -1476,10 +2039,11 @@ const ContextParts = Object.freeze({
1476
2039
  * @param buses Prepared domain and System buses.
1477
2040
  * @param running Prepared Stand and subscription runtime.
1478
2041
  * @param ai Effective AI registry when Agent repositories are present.
2042
+ * @param tenantIndex Effective live membership index.
1479
2043
  * @returns Constructed bounded context.
1480
2044
  */
1481
- createBoundedContext(specSnapshot, storageFactory, repositories, deliveryStrategy, buses, running, ai) {
1482
- return constructBoundedContext({
2045
+ createBoundedContext(specSnapshot, storageFactory, repositories, deliveryStrategy, buses, running, ai, tenantIndex) {
2046
+ const context = constructBoundedContext({
1483
2047
  snapshot: ContextParts.snapshotFor(specSnapshot),
1484
2048
  commandBus: buses.commandBus,
1485
2049
  eventBus: buses.eventBus,
@@ -1490,11 +2054,14 @@ const ContextParts = Object.freeze({
1490
2054
  subscriptionRuntime: running.runtime,
1491
2055
  systemSpec: buses.systemSpec,
1492
2056
  storageFactory,
2057
+ tenantIndex: tenantIndex ?? ServerEnvironment.instance().tenantIndex,
1493
2058
  repositories,
1494
2059
  deliveryStrategy,
1495
2060
  ...(ai === undefined ? {} : { ai }),
1496
2061
  token: frameworkConstructionToken,
1497
2062
  });
2063
+ standAccess.bindProducingContext(running.stand, context);
2064
+ return context;
1498
2065
  },
1499
2066
  /**
1500
2067
  * Captures public context identity from the build specification.
@@ -1533,35 +2100,6 @@ const ContextParts = Object.freeze({
1533
2100
  multitenant: false,
1534
2101
  });
1535
2102
  },
1536
- /**
1537
- * Captures the shard count and validates later shard results.
1538
- *
1539
- * @param strategy Configured delivery strategy.
1540
- * @returns Immutable delivery strategy snapshot.
1541
- */
1542
- snapshotDeliveryStrategy(strategy) {
1543
- if (!Number.isSafeInteger(strategy.shardCount) || strategy.shardCount <= 0) {
1544
- throw new Error("Delivery strategy shard count must be a positive safe integer.");
1545
- }
1546
- const shardCount = strategy.shardCount;
1547
- return Object.freeze({
1548
- shardCount,
1549
- /**
1550
- * Returns a target shard after verifying the captured shard total.
1551
- *
1552
- * @param targetId Inbox target identifier.
1553
- * @param targetType Inbox target type.
1554
- * @returns Validated shard index.
1555
- */
1556
- shardFor(targetId, targetType) {
1557
- const shard = strategy.shardFor(InboxTargets.clone(targetId), targetType);
1558
- if (shard.ofTotal !== shardCount) {
1559
- throw new Error("Delivery strategy shard total must equal its resolved shard count.");
1560
- }
1561
- return new ShardIndex(shard.index, shard.ofTotal);
1562
- },
1563
- });
1564
- },
1565
2103
  /**
1566
2104
  * Creates the storage context for read-side catch-up.
1567
2105
  *
@@ -1740,8 +2278,7 @@ const ContextParts = Object.freeze({
1740
2278
  },
1741
2279
  });
1742
2280
  const ready = broker.open();
1743
- // Synchronous build returns before readiness; retain the failure for the next
1744
- // observable operation without letting Node report an unhandled rejection.
2281
+ // The builder awaits readiness; retain a rejection handler until it observes the result.
1745
2282
  void ready.catch(() => undefined);
1746
2283
  contextIntegrations.set(context, { broker, ready });
1747
2284
  },
@@ -1878,166 +2415,9 @@ const ContextParts = Object.freeze({
1878
2415
  */
1879
2416
  requireEntityClass(entityType, operation) {
1880
2417
  if (typeof entityType !== "function") {
1881
- throw new TypeError(`${operation} requires a Repository instance. Use an entity class only with buildAsync().`);
1882
- }
1883
- },
1884
- /**
1885
- * Rejects synchronous assembly when classes need generated metadata.
1886
- *
1887
- * @param entityTypes Entity classes to assemble.
1888
- */
1889
- rejectSyncEntityAssembly(entityTypes) {
1890
- if (entityTypes.size === 0) {
1891
- return;
1892
- }
1893
- throw new Error("BoundedContextBuilder.build() cannot assemble entity classes from generated metadata. " +
1894
- "Use buildAsync().");
1895
- },
1896
- /**
1897
- * Validates that a root exists when generated metadata must be loaded.
1898
- *
1899
- * @param root Configured registry location.
1900
- * @returns Configured root; throws when absent.
1901
- */
1902
- requireGeneratedRegistryRoot(root) {
1903
- if (root !== undefined) {
1904
- return root;
1905
- }
1906
- throw new Error("BoundedContextBuilder.buildAsync() requires withGeneratedRegistryRoot(root) " +
1907
- "when assembling entity classes from generated metadata.");
1908
- },
1909
- /**
1910
- * Resolves the generated registry module and verifies it stays beneath the configured root.
1911
- *
1912
- * @param root Configured filesystem path or file URL.
1913
- * @returns Registry module file URL.
1914
- */
1915
- async trustedGeneratedRegistryModule(root) {
1916
- const trustedRoot = await ContextParts.canonicalGeneratedRegistryRoot(root);
1917
- const registryPath = resolve(trustedRoot, generatedRegistryFile);
1918
- const canonicalRegistryPath = await ContextParts.canonicalReadableRegistryPath(registryPath);
1919
- if (ContextParts.resolvesOutsideRoot(trustedRoot, canonicalRegistryPath)) {
1920
- throw new Error(`Generated handler registry module "${canonicalRegistryPath}" must resolve within ` +
1921
- `the configured generated registry root "${trustedRoot}".`);
1922
- }
1923
- return pathToFileURL(canonicalRegistryPath);
1924
- },
1925
- /**
1926
- * Resolves a generated registry root to its canonical filesystem path.
1927
- *
1928
- * @param root Configured path or file URL.
1929
- * @returns Canonical directory path.
1930
- */
1931
- async canonicalGeneratedRegistryRoot(root) {
1932
- const rootPath = ContextParts.generatedRegistryRootPath(root);
1933
- try {
1934
- return await realpath(rootPath);
1935
- }
1936
- catch (error) {
1937
- throw new Error(`Generated registry root "${rootPath}" must be an existing readable directory.`, { cause: error });
1938
- }
1939
- },
1940
- /**
1941
- * Converts a registry root path or file URL to an absolute filesystem path.
1942
- *
1943
- * @param root Configured path or file URL.
1944
- * @returns Absolute filesystem path.
1945
- */
1946
- generatedRegistryRootPath(root) {
1947
- if (root instanceof URL) {
1948
- return ContextParts.fileUrlPath(root, "Generated registry root");
1949
- }
1950
- if (ContextParts.isUrlLike(root)) {
1951
- return ContextParts.fileUrlPath(ContextParts.parseRootUrl(root), "Generated registry root");
1952
- }
1953
- return resolve(root);
1954
- },
1955
- /**
1956
- * Validates a file URL and converts it to an absolute filesystem path.
1957
- *
1958
- * @param url File URL to convert.
1959
- * @param label Label used in validation errors.
1960
- * @returns Filesystem path.
1961
- */
1962
- fileUrlPath(url, label) {
1963
- if (url.protocol !== "file:") {
1964
- throw new Error(`${label} "${url.href}" must use the file: URL scheme.`);
1965
- }
1966
- if (url.search.length > 0 || url.hash.length > 0) {
1967
- throw new Error(`${label} "${url.href}" must not include a query or hash.`);
1968
- }
1969
- return resolve(fileURLToPath(url));
1970
- },
1971
- /**
1972
- * Parses a generated registry root string as a URL.
1973
- *
1974
- * @param root Registry root string.
1975
- * @returns Parsed URL.
1976
- */
1977
- parseRootUrl(root) {
1978
- try {
1979
- return new URL(root);
1980
- }
1981
- catch (error) {
1982
- throw new Error(`Generated registry root "${root}" is not a valid URL.`, { cause: error });
1983
- }
1984
- },
1985
- /**
1986
- * Checks strings for a URL scheme, excluding Windows drive paths.
1987
- *
1988
- * @param value String to inspect.
1989
- * @returns True when the string has a URL scheme.
1990
- */
1991
- isUrlLike(value) {
1992
- return moduleSchemeRe.test(value) && !/^[A-Za-z]:[\\/]/.test(value);
1993
- },
1994
- /**
1995
- * Verifies registry-module readability and resolves its canonical path.
1996
- *
1997
- * @param registryPath Registry module filesystem path.
1998
- * @returns Canonical readable path.
1999
- */
2000
- async canonicalReadableRegistryPath(registryPath) {
2001
- try {
2002
- await access(registryPath, fsConstants.R_OK);
2003
- return await realpath(registryPath);
2004
- }
2005
- catch (error) {
2006
- throw new Error(`Generated handler registry module "${registryPath}" must exist and be readable.`, { cause: error });
2418
+ throw new TypeError(`${operation} requires a Repository instance. Use an entity class only with build().`);
2007
2419
  }
2008
2420
  },
2009
- /**
2010
- * Checks whether a canonical path escapes a canonical root.
2011
- *
2012
- * @param canonicalRoot Root directory.
2013
- * @param canonicalPath Candidate path.
2014
- * @returns True when the candidate escapes the root.
2015
- */
2016
- resolvesOutsideRoot(canonicalRoot, canonicalPath) {
2017
- const relativePath = relative(canonicalRoot, canonicalPath);
2018
- return (relativePath.startsWith("..") ||
2019
- relativePath === ".." ||
2020
- relativePath.split(sep).includes("..") ||
2021
- isAbsolute(relativePath));
2022
- },
2023
- /**
2024
- * Creates a cache-bust query after a prior registry load failure.
2025
- *
2026
- * @param registryKey Canonical registry identifier.
2027
- * @returns Retry query, or an empty object on the first attempt.
2028
- */
2029
- generatedRegistryCacheBust(registryKey) {
2030
- const attempt = generatedRegistryLoadAttempts.get(registryKey) ?? 0;
2031
- return attempt === 0 ? {} : { cacheBust: `retry-${attempt.toString()}` };
2032
- },
2033
- /**
2034
- * Records another failed load for a registry.
2035
- *
2036
- * @param registryKey Canonical registry identifier.
2037
- */
2038
- recordGeneratedRegistryFailure(registryKey) {
2039
- generatedRegistryLoadAttempts.set(registryKey, (generatedRegistryLoadAttempts.get(registryKey) ?? 0) + 1);
2040
- },
2041
2421
  /**
2042
2422
  * Loads generated handler registries into a metadata registry.
2043
2423
  *
@@ -2295,16 +2675,6 @@ const ContextParts = Object.freeze({
2295
2675
  }
2296
2676
  return Object.freeze([...schemas.values()]);
2297
2677
  },
2298
- /**
2299
- * Clears metadata for a failed context build and closes its tenant index.
2300
- *
2301
- * @param context Context whose build failed.
2302
- * @param tenantIndex Tenant index to close.
2303
- */
2304
- cleanupFailedContext(context, tenantIndex) {
2305
- ContextParts.clearContextMetadata(context);
2306
- tenantIndex.close();
2307
- },
2308
2678
  /**
2309
2679
  * Removes runtime metadata associated with a context.
2310
2680
  *
@@ -2327,68 +2697,25 @@ const ContextParts = Object.freeze({
2327
2697
  contextStorageFactories.delete(context);
2328
2698
  contextDeliveryDescriptors.delete(context);
2329
2699
  contextSubscriptionRuntimes.delete(context);
2700
+ contextInputPreparations.delete(context);
2701
+ contextPreparations.delete(context);
2330
2702
  contextIntegrations.delete(context);
2331
2703
  eventSubscribers.delete(context);
2332
2704
  systemEventPosters.delete(context);
2333
2705
  },
2334
2706
  /**
2335
- * Creates delivery operations from the context inboxes and readiness state.
2707
+ * Creates delivery operations from the context inboxes.
2336
2708
  *
2337
- * @param context Built context snapshot.
2338
- * @param storageFactory Context storage provider.
2339
- * @param tenantIndex Tenant index for startup scopes.
2340
2709
  * @param entityInbox Registered Entity inbox.
2341
2710
  * @param projections Projection inbox.
2342
- * @param readiness Delivery readiness coordinator.
2343
2711
  * @returns Delivery descriptor.
2344
2712
  */
2345
- createDeliveryDescriptor(context, storageFactory, tenantIndex, entityInbox, projections, readiness) {
2713
+ createDeliveryDescriptor(entityInbox, projections) {
2346
2714
  return Object.freeze({
2347
- storageFactory,
2348
- startupScopes: () => ContextParts.deliveryStartupScopes(tenantIndex),
2349
- storageContext: (scope) => ContextParts.deliveryStorageContext(context, scope),
2350
2715
  endpoints: () => Object.freeze([...entityInbox.endpoints(), ...projections.endpoints()]),
2351
2716
  replay: (message, tenantId) => message.label === "UPDATE_SUBSCRIBER"
2352
2717
  ? projections.replay(message, tenantId)
2353
2718
  : entityInbox.replay(message, tenantId),
2354
- onReady: (onReady) => readiness.onReady(onReady),
2355
- transition: (scopes, onReady, options) => readiness.transition(scopes, onReady, options),
2356
- });
2357
- },
2358
- /**
2359
- * Lists delivery startup scopes, one per tenant in multitenant contexts.
2360
- *
2361
- * @param tenantIndex Tenant index supplying configured scopes.
2362
- * @returns Frozen startup scope list.
2363
- */
2364
- async deliveryStartupScopes(tenantIndex) {
2365
- if (tenantIndex.tenantMode === "single-tenant") {
2366
- return Object.freeze([Object.freeze({})]);
2367
- }
2368
- return Object.freeze((await tenantIndex.all()).map((tenantId) => Object.freeze({ tenantId })));
2369
- },
2370
- /**
2371
- * Creates tenant-validated storage context for a delivery scope.
2372
- *
2373
- * @param context Built context snapshot.
2374
- * @param scope Selected startup scope.
2375
- * @returns Storage context for the selected tenant.
2376
- */
2377
- deliveryStorageContext(context, scope) {
2378
- if (context.tenantMode === "single-tenant") {
2379
- if (scope.tenantId !== undefined) {
2380
- throw new Error(`Single-tenant context "${context.name.value}" does not accept tenantId.`);
2381
- }
2382
- return Object.freeze({ name: context.name.value, multitenant: false });
2383
- }
2384
- const tenantId = scope.tenantId;
2385
- if (tenantId === undefined) {
2386
- throw new Error(`Multitenant context "${context.name.value}" requires tenantId.`);
2387
- }
2388
- return Object.freeze({
2389
- name: context.name.value,
2390
- multitenant: true,
2391
- tenantId: TenantBoundary.from(tenantId).tenantId,
2392
2719
  });
2393
2720
  },
2394
2721
  /**
@@ -2488,7 +2815,7 @@ const ContextParts = Object.freeze({
2488
2815
  registeredRepositories.set(repository, { name: registration.name });
2489
2816
  },
2490
2817
  close: () => {
2491
- repositoryAccess.clearRuntime(repository);
2818
+ repositoryAccess.clearRuntimeIdle(repository);
2492
2819
  },
2493
2820
  };
2494
2821
  },
@@ -2502,6 +2829,7 @@ const ContextParts = Object.freeze({
2502
2829
  repositoryAccess.bindRuntime(repository, {
2503
2830
  context: registration.storageContext,
2504
2831
  storageFactory: registration.storageFactory,
2832
+ onAdmit: registration.onAdmit,
2505
2833
  ...(registration.ai === undefined ? {} : { ai: registration.ai }),
2506
2834
  stand: registration.stand,
2507
2835
  signalMetadata: new SignalMetadata(),
@@ -2511,11 +2839,11 @@ const ContextParts = Object.freeze({
2511
2839
  registerEventSchema: registration.registerEventSchema,
2512
2840
  registerSystemEventSchema: registration.registerSystemEventSchema,
2513
2841
  publishAgentEvent: registration.publishAgentEvent,
2514
- prepareSavedCommand: registration.prepareSavedCommand,
2515
- publishSavedCommand: registration.publishSavedCommand,
2516
2842
  publishAgentSystemEvent: registration.publishAgentSystemEvent,
2517
- recordAcceptedSaved: registration.recordAcceptedSaved,
2518
- wakeAcceptedAgent: registration.wakeAcceptedAgent,
2843
+ agentReceipts: registration.agentReceipts,
2844
+ agentSignal: registration.agentSignal,
2845
+ reserveAgent: registration.reserveAgent,
2846
+ deferAgent: registration.deferAgent,
2519
2847
  });
2520
2848
  },
2521
2849
  /**
@@ -2637,7 +2965,7 @@ const ContextParts = Object.freeze({
2637
2965
  });
2638
2966
  }
2639
2967
  finally {
2640
- eventStore.close();
2968
+ await eventStore.close();
2641
2969
  }
2642
2970
  },
2643
2971
  /**
@@ -2787,9 +3115,15 @@ const ContextParts = Object.freeze({
2787
3115
  * @internal
2788
3116
  */
2789
3117
  export const boundedContextIntegrationAccess = Object.freeze({
3118
+ /**
3119
+ * Publishes an imported Event through the context’s import path.
3120
+ */
2790
3121
  publishImported(context, event) {
2791
3122
  return ContextParts.publishImported(context, event);
2792
3123
  },
3124
+ /**
3125
+ * Signals that standalone context integration is ready.
3126
+ */
2793
3127
  ready(context) {
2794
3128
  return ContextParts.integrationReady(context);
2795
3129
  },