@spine-event-engine/server 2.0.0-snapshot.2

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 (354) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +569 -0
  3. package/REFERENCE.md +488 -0
  4. package/dist/bus/command-bus.d.ts +61 -0
  5. package/dist/bus/command-bus.d.ts.map +1 -0
  6. package/dist/bus/command-bus.js +201 -0
  7. package/dist/bus/command-bus.js.map +1 -0
  8. package/dist/bus/command-dispatcher-registry.d.ts +33 -0
  9. package/dist/bus/command-dispatcher-registry.d.ts.map +1 -0
  10. package/dist/bus/command-dispatcher-registry.js +75 -0
  11. package/dist/bus/command-dispatcher-registry.js.map +1 -0
  12. package/dist/bus/command-dispatcher.d.ts +25 -0
  13. package/dist/bus/command-dispatcher.d.ts.map +1 -0
  14. package/dist/bus/command-dispatcher.js +15 -0
  15. package/dist/bus/command-dispatcher.js.map +1 -0
  16. package/dist/bus/command-errors.d.ts +23 -0
  17. package/dist/bus/command-errors.d.ts.map +1 -0
  18. package/dist/bus/command-errors.js +52 -0
  19. package/dist/bus/command-errors.js.map +1 -0
  20. package/dist/bus/event-bus.d.ts +113 -0
  21. package/dist/bus/event-bus.d.ts.map +1 -0
  22. package/dist/bus/event-bus.js +518 -0
  23. package/dist/bus/event-bus.js.map +1 -0
  24. package/dist/bus/event-dispatcher-origin-schemas.d.ts +17 -0
  25. package/dist/bus/event-dispatcher-origin-schemas.d.ts.map +1 -0
  26. package/dist/bus/event-dispatcher-origin-schemas.js +32 -0
  27. package/dist/bus/event-dispatcher-origin-schemas.js.map +1 -0
  28. package/dist/bus/event-dispatcher-registry.d.ts +48 -0
  29. package/dist/bus/event-dispatcher-registry.d.ts.map +1 -0
  30. package/dist/bus/event-dispatcher-registry.js +176 -0
  31. package/dist/bus/event-dispatcher-registry.js.map +1 -0
  32. package/dist/bus/event-dispatcher.d.ts +48 -0
  33. package/dist/bus/event-dispatcher.d.ts.map +1 -0
  34. package/dist/bus/event-dispatcher.js +15 -0
  35. package/dist/bus/event-dispatcher.js.map +1 -0
  36. package/dist/context/bounded-context.d.ts +606 -0
  37. package/dist/context/bounded-context.d.ts.map +1 -0
  38. package/dist/context/bounded-context.js +1780 -0
  39. package/dist/context/bounded-context.js.map +1 -0
  40. package/dist/context/entity-inbox.d.ts +63 -0
  41. package/dist/context/entity-inbox.d.ts.map +1 -0
  42. package/dist/context/entity-inbox.js +380 -0
  43. package/dist/context/entity-inbox.js.map +1 -0
  44. package/dist/context/local-inbox-handoff.d.ts +244 -0
  45. package/dist/context/local-inbox-handoff.d.ts.map +1 -0
  46. package/dist/context/local-inbox-handoff.js +334 -0
  47. package/dist/context/local-inbox-handoff.js.map +1 -0
  48. package/dist/context/projection-handoff.d.ts +46 -0
  49. package/dist/context/projection-handoff.d.ts.map +1 -0
  50. package/dist/context/projection-handoff.js +172 -0
  51. package/dist/context/projection-handoff.js.map +1 -0
  52. package/dist/context/tenant-index.d.ts +41 -0
  53. package/dist/context/tenant-index.d.ts.map +1 -0
  54. package/dist/context/tenant-index.js +92 -0
  55. package/dist/context/tenant-index.js.map +1 -0
  56. package/dist/delivery/conditional-pickup.d.ts +25 -0
  57. package/dist/delivery/conditional-pickup.d.ts.map +1 -0
  58. package/dist/delivery/conditional-pickup.js +26 -0
  59. package/dist/delivery/conditional-pickup.js.map +1 -0
  60. package/dist/delivery/delivery-builder.d.ts +216 -0
  61. package/dist/delivery/delivery-builder.d.ts.map +1 -0
  62. package/dist/delivery/delivery-builder.js +323 -0
  63. package/dist/delivery/delivery-builder.js.map +1 -0
  64. package/dist/delivery/delivery-lease.d.ts +15 -0
  65. package/dist/delivery/delivery-lease.d.ts.map +1 -0
  66. package/dist/delivery/delivery-lease.js +44 -0
  67. package/dist/delivery/delivery-lease.js.map +1 -0
  68. package/dist/delivery/delivery-loop.d.ts +113 -0
  69. package/dist/delivery/delivery-loop.d.ts.map +1 -0
  70. package/dist/delivery/delivery-loop.js +119 -0
  71. package/dist/delivery/delivery-loop.js.map +1 -0
  72. package/dist/delivery/delivery-monitor.d.ts +165 -0
  73. package/dist/delivery/delivery-monitor.d.ts.map +1 -0
  74. package/dist/delivery/delivery-monitor.js +168 -0
  75. package/dist/delivery/delivery-monitor.js.map +1 -0
  76. package/dist/delivery/delivery-ports.d.ts +141 -0
  77. package/dist/delivery/delivery-ports.d.ts.map +1 -0
  78. package/dist/delivery/delivery-ports.js +15 -0
  79. package/dist/delivery/delivery-ports.js.map +1 -0
  80. package/dist/delivery/delivery-run-control.d.ts +50 -0
  81. package/dist/delivery/delivery-run-control.d.ts.map +1 -0
  82. package/dist/delivery/delivery-run-control.js +66 -0
  83. package/dist/delivery/delivery-run-control.js.map +1 -0
  84. package/dist/delivery/delivery-run-coordinator.d.ts +218 -0
  85. package/dist/delivery/delivery-run-coordinator.d.ts.map +1 -0
  86. package/dist/delivery/delivery-run-coordinator.js +609 -0
  87. package/dist/delivery/delivery-run-coordinator.js.map +1 -0
  88. package/dist/delivery/delivery-storage-error.d.ts +13 -0
  89. package/dist/delivery/delivery-storage-error.d.ts.map +1 -0
  90. package/dist/delivery/delivery-storage-error.js +30 -0
  91. package/dist/delivery/delivery-storage-error.js.map +1 -0
  92. package/dist/delivery/delivery-supervisor.d.ts +175 -0
  93. package/dist/delivery/delivery-supervisor.d.ts.map +1 -0
  94. package/dist/delivery/delivery-supervisor.js +469 -0
  95. package/dist/delivery/delivery-supervisor.js.map +1 -0
  96. package/dist/delivery/delivery-worker.d.ts +180 -0
  97. package/dist/delivery/delivery-worker.d.ts.map +1 -0
  98. package/dist/delivery/delivery-worker.js +232 -0
  99. package/dist/delivery/delivery-worker.js.map +1 -0
  100. package/dist/delivery/delivery.d.ts +238 -0
  101. package/dist/delivery/delivery.d.ts.map +1 -0
  102. package/dist/delivery/delivery.js +400 -0
  103. package/dist/delivery/delivery.js.map +1 -0
  104. package/dist/delivery/inbox-records.d.ts +19 -0
  105. package/dist/delivery/inbox-records.d.ts.map +1 -0
  106. package/dist/delivery/inbox-records.js +291 -0
  107. package/dist/delivery/inbox-records.js.map +1 -0
  108. package/dist/delivery/inbox-storage.d.ts +81 -0
  109. package/dist/delivery/inbox-storage.d.ts.map +1 -0
  110. package/dist/delivery/inbox-storage.js +353 -0
  111. package/dist/delivery/inbox-storage.js.map +1 -0
  112. package/dist/delivery/inbox.d.ts +263 -0
  113. package/dist/delivery/inbox.d.ts.map +1 -0
  114. package/dist/delivery/inbox.js +172 -0
  115. package/dist/delivery/inbox.js.map +1 -0
  116. package/dist/delivery/parked-delivery-obligations.d.ts +182 -0
  117. package/dist/delivery/parked-delivery-obligations.d.ts.map +1 -0
  118. package/dist/delivery/parked-delivery-obligations.js +390 -0
  119. package/dist/delivery/parked-delivery-obligations.js.map +1 -0
  120. package/dist/delivery/shard-index.d.ts +41 -0
  121. package/dist/delivery/shard-index.d.ts.map +1 -0
  122. package/dist/delivery/shard-index.js +67 -0
  123. package/dist/delivery/shard-index.js.map +1 -0
  124. package/dist/delivery/sharded-work-registry.d.ts +114 -0
  125. package/dist/delivery/sharded-work-registry.d.ts.map +1 -0
  126. package/dist/delivery/sharded-work-registry.js +360 -0
  127. package/dist/delivery/sharded-work-registry.js.map +1 -0
  128. package/dist/entity/entity-metadata.d.ts +173 -0
  129. package/dist/entity/entity-metadata.d.ts.map +1 -0
  130. package/dist/entity/entity-metadata.js +187 -0
  131. package/dist/entity/entity-metadata.js.map +1 -0
  132. package/dist/entity/entity-storage-descriptor.d.ts +64 -0
  133. package/dist/entity/entity-storage-descriptor.d.ts.map +1 -0
  134. package/dist/entity/entity-storage-descriptor.js +227 -0
  135. package/dist/entity/entity-storage-descriptor.js.map +1 -0
  136. package/dist/entity/entity-transaction.d.ts +372 -0
  137. package/dist/entity/entity-transaction.d.ts.map +1 -0
  138. package/dist/entity/entity-transaction.js +413 -0
  139. package/dist/entity/entity-transaction.js.map +1 -0
  140. package/dist/entity/entity-transition-validation.d.ts +47 -0
  141. package/dist/entity/entity-transition-validation.d.ts.map +1 -0
  142. package/dist/entity/entity-transition-validation.js +327 -0
  143. package/dist/entity/entity-transition-validation.js.map +1 -0
  144. package/dist/entity/entity.d.ts +499 -0
  145. package/dist/entity/entity.d.ts.map +1 -0
  146. package/dist/entity/entity.js +829 -0
  147. package/dist/entity/entity.js.map +1 -0
  148. package/dist/entity/implicit-required-id.d.ts +15 -0
  149. package/dist/entity/implicit-required-id.d.ts.map +1 -0
  150. package/dist/entity/implicit-required-id.js +123 -0
  151. package/dist/entity/implicit-required-id.js.map +1 -0
  152. package/dist/entity/spec-scanner.d.ts +14 -0
  153. package/dist/entity/spec-scanner.d.ts.map +1 -0
  154. package/dist/entity/spec-scanner.js +37 -0
  155. package/dist/entity/spec-scanner.js.map +1 -0
  156. package/dist/handler/build-time-handler-analyzer.d.ts +150 -0
  157. package/dist/handler/build-time-handler-analyzer.d.ts.map +1 -0
  158. package/dist/handler/build-time-handler-analyzer.js +1101 -0
  159. package/dist/handler/build-time-handler-analyzer.js.map +1 -0
  160. package/dist/handler/command-registration-readiness.d.ts +99 -0
  161. package/dist/handler/command-registration-readiness.d.ts.map +1 -0
  162. package/dist/handler/command-registration-readiness.js +118 -0
  163. package/dist/handler/command-registration-readiness.js.map +1 -0
  164. package/dist/handler/event-handler-filter.d.ts +43 -0
  165. package/dist/handler/event-handler-filter.d.ts.map +1 -0
  166. package/dist/handler/event-handler-filter.js +128 -0
  167. package/dist/handler/event-handler-filter.js.map +1 -0
  168. package/dist/handler/event-registration-readiness.d.ts +196 -0
  169. package/dist/handler/event-registration-readiness.d.ts.map +1 -0
  170. package/dist/handler/event-registration-readiness.js +187 -0
  171. package/dist/handler/event-registration-readiness.js.map +1 -0
  172. package/dist/handler/external.d.ts +10 -0
  173. package/dist/handler/external.d.ts.map +1 -0
  174. package/dist/handler/external.js +15 -0
  175. package/dist/handler/external.js.map +1 -0
  176. package/dist/handler/generated-handler-registry.d.ts +154 -0
  177. package/dist/handler/generated-handler-registry.d.ts.map +1 -0
  178. package/dist/handler/generated-handler-registry.js +223 -0
  179. package/dist/handler/generated-handler-registry.js.map +1 -0
  180. package/dist/handler/generated-registry-discovery.d.ts +94 -0
  181. package/dist/handler/generated-registry-discovery.d.ts.map +1 -0
  182. package/dist/handler/generated-registry-discovery.js +228 -0
  183. package/dist/handler/generated-registry-discovery.js.map +1 -0
  184. package/dist/handler/generated-registry-writer.d.ts +58 -0
  185. package/dist/handler/generated-registry-writer.d.ts.map +1 -0
  186. package/dist/handler/generated-registry-writer.js +458 -0
  187. package/dist/handler/generated-registry-writer.js.map +1 -0
  188. package/dist/handler/handler-codegen.d.ts +48 -0
  189. package/dist/handler/handler-codegen.d.ts.map +1 -0
  190. package/dist/handler/handler-codegen.js +214 -0
  191. package/dist/handler/handler-codegen.js.map +1 -0
  192. package/dist/handler/handler-decorators.d.ts +134 -0
  193. package/dist/handler/handler-decorators.d.ts.map +1 -0
  194. package/dist/handler/handler-decorators.js +239 -0
  195. package/dist/handler/handler-decorators.js.map +1 -0
  196. package/dist/handler/handler-metadata.d.ts +510 -0
  197. package/dist/handler/handler-metadata.d.ts.map +1 -0
  198. package/dist/handler/handler-metadata.js +391 -0
  199. package/dist/handler/handler-metadata.js.map +1 -0
  200. package/dist/handler/registration-readiness-metadata.d.ts +68 -0
  201. package/dist/handler/registration-readiness-metadata.d.ts.map +1 -0
  202. package/dist/handler/registration-readiness-metadata.js +170 -0
  203. package/dist/handler/registration-readiness-metadata.js.map +1 -0
  204. package/dist/handler/rejection-source.d.ts +11 -0
  205. package/dist/handler/rejection-source.d.ts.map +1 -0
  206. package/dist/handler/rejection-source.js +25 -0
  207. package/dist/handler/rejection-source.js.map +1 -0
  208. package/dist/index.d.ts +46 -0
  209. package/dist/index.d.ts.map +1 -0
  210. package/dist/index.js +54 -0
  211. package/dist/index.js.map +1 -0
  212. package/dist/integration/external-messages.d.ts +49 -0
  213. package/dist/integration/external-messages.d.ts.map +1 -0
  214. package/dist/integration/external-messages.js +112 -0
  215. package/dist/integration/external-messages.js.map +1 -0
  216. package/dist/integration/integration-broker.d.ts +74 -0
  217. package/dist/integration/integration-broker.d.ts.map +1 -0
  218. package/dist/integration/integration-broker.js +444 -0
  219. package/dist/integration/integration-broker.js.map +1 -0
  220. package/dist/integration/third-party-context.d.ts +56 -0
  221. package/dist/integration/third-party-context.d.ts.map +1 -0
  222. package/dist/integration/third-party-context.js +130 -0
  223. package/dist/integration/third-party-context.js.map +1 -0
  224. package/dist/repository/command-errors.d.ts +25 -0
  225. package/dist/repository/command-errors.d.ts.map +1 -0
  226. package/dist/repository/command-errors.js +43 -0
  227. package/dist/repository/command-errors.js.map +1 -0
  228. package/dist/repository/command-routing.d.ts +72 -0
  229. package/dist/repository/command-routing.d.ts.map +1 -0
  230. package/dist/repository/command-routing.js +98 -0
  231. package/dist/repository/command-routing.js.map +1 -0
  232. package/dist/repository/commit-fence.d.ts +22 -0
  233. package/dist/repository/commit-fence.d.ts.map +1 -0
  234. package/dist/repository/commit-fence.js +37 -0
  235. package/dist/repository/commit-fence.js.map +1 -0
  236. package/dist/repository/event-routing.d.ts +71 -0
  237. package/dist/repository/event-routing.d.ts.map +1 -0
  238. package/dist/repository/event-routing.js +96 -0
  239. package/dist/repository/event-routing.js.map +1 -0
  240. package/dist/repository/primitive-id.d.ts +86 -0
  241. package/dist/repository/primitive-id.d.ts.map +1 -0
  242. package/dist/repository/primitive-id.js +110 -0
  243. package/dist/repository/primitive-id.js.map +1 -0
  244. package/dist/repository/repository.d.ts +670 -0
  245. package/dist/repository/repository.d.ts.map +1 -0
  246. package/dist/repository/repository.js +3298 -0
  247. package/dist/repository/repository.js.map +1 -0
  248. package/dist/repository/routing-declarations.d.ts +75 -0
  249. package/dist/repository/routing-declarations.d.ts.map +1 -0
  250. package/dist/repository/routing-declarations.js +101 -0
  251. package/dist/repository/routing-declarations.js.map +1 -0
  252. package/dist/repository/state-update-routing.d.ts +66 -0
  253. package/dist/repository/state-update-routing.d.ts.map +1 -0
  254. package/dist/repository/state-update-routing.js +91 -0
  255. package/dist/repository/state-update-routing.js.map +1 -0
  256. package/dist/runtime/runtime.d.ts +156 -0
  257. package/dist/runtime/runtime.d.ts.map +1 -0
  258. package/dist/runtime/runtime.js +215 -0
  259. package/dist/runtime/runtime.js.map +1 -0
  260. package/dist/runtime/signal-intake.d.ts +84 -0
  261. package/dist/runtime/signal-intake.d.ts.map +1 -0
  262. package/dist/runtime/signal-intake.js +94 -0
  263. package/dist/runtime/signal-intake.js.map +1 -0
  264. package/dist/runtime/signal-metadata.d.ts +242 -0
  265. package/dist/runtime/signal-metadata.d.ts.map +1 -0
  266. package/dist/runtime/signal-metadata.js +368 -0
  267. package/dist/runtime/signal-metadata.js.map +1 -0
  268. package/dist/server/browser-server.d.ts +49 -0
  269. package/dist/server/browser-server.d.ts.map +1 -0
  270. package/dist/server/browser-server.js +633 -0
  271. package/dist/server/browser-server.js.map +1 -0
  272. package/dist/server/durable-subscription-bindings.d.ts +122 -0
  273. package/dist/server/durable-subscription-bindings.d.ts.map +1 -0
  274. package/dist/server/durable-subscription-bindings.js +524 -0
  275. package/dist/server/durable-subscription-bindings.js.map +1 -0
  276. package/dist/server/environment-attachment.d.ts +356 -0
  277. package/dist/server/environment-attachment.d.ts.map +1 -0
  278. package/dist/server/environment-attachment.js +2061 -0
  279. package/dist/server/environment-attachment.js.map +1 -0
  280. package/dist/server/environment-delivery-records.d.ts +77 -0
  281. package/dist/server/environment-delivery-records.d.ts.map +1 -0
  282. package/dist/server/environment-delivery-records.js +282 -0
  283. package/dist/server/environment-delivery-records.js.map +1 -0
  284. package/dist/server/environment-delivery-worker.d.ts +161 -0
  285. package/dist/server/environment-delivery-worker.d.ts.map +1 -0
  286. package/dist/server/environment-delivery-worker.js +664 -0
  287. package/dist/server/environment-delivery-worker.js.map +1 -0
  288. package/dist/server/environment.d.ts +43 -0
  289. package/dist/server/environment.d.ts.map +1 -0
  290. package/dist/server/environment.js +90 -0
  291. package/dist/server/environment.js.map +1 -0
  292. package/dist/server/managed-child-subscription.d.ts +11 -0
  293. package/dist/server/managed-child-subscription.d.ts.map +1 -0
  294. package/dist/server/managed-child-subscription.js +44 -0
  295. package/dist/server/managed-child-subscription.js.map +1 -0
  296. package/dist/server/managed-server-application.d.ts +324 -0
  297. package/dist/server/managed-server-application.d.ts.map +1 -0
  298. package/dist/server/managed-server-application.js +937 -0
  299. package/dist/server/managed-server-application.js.map +1 -0
  300. package/dist/server/node-coordinator.d.ts +191 -0
  301. package/dist/server/node-coordinator.d.ts.map +1 -0
  302. package/dist/server/node-coordinator.js +552 -0
  303. package/dist/server/node-coordinator.js.map +1 -0
  304. package/dist/server/process-server-coordinator.d.ts +15 -0
  305. package/dist/server/process-server-coordinator.d.ts.map +1 -0
  306. package/dist/server/process-server-coordinator.js +112 -0
  307. package/dist/server/process-server-coordinator.js.map +1 -0
  308. package/dist/server/reserved-spine-rpc-paths.json +8 -0
  309. package/dist/server/retryable-close.d.ts +28 -0
  310. package/dist/server/retryable-close.d.ts.map +1 -0
  311. package/dist/server/retryable-close.js +129 -0
  312. package/dist/server/retryable-close.js.map +1 -0
  313. package/dist/server/server-environment.d.ts +180 -0
  314. package/dist/server/server-environment.d.ts.map +1 -0
  315. package/dist/server/server-environment.js +418 -0
  316. package/dist/server/server-environment.js.map +1 -0
  317. package/dist/server/server-log.d.ts +18 -0
  318. package/dist/server/server-log.d.ts.map +1 -0
  319. package/dist/server/server-log.js +96 -0
  320. package/dist/server/server-log.js.map +1 -0
  321. package/dist/server/server.d.ts +389 -0
  322. package/dist/server/server.d.ts.map +1 -0
  323. package/dist/server/server.js +752 -0
  324. package/dist/server/server.js.map +1 -0
  325. package/dist/services/spine-services.d.ts +88 -0
  326. package/dist/services/spine-services.d.ts.map +1 -0
  327. package/dist/services/spine-services.js +1586 -0
  328. package/dist/services/spine-services.js.map +1 -0
  329. package/dist/stand/stand.d.ts +294 -0
  330. package/dist/stand/stand.d.ts.map +1 -0
  331. package/dist/stand/stand.js +680 -0
  332. package/dist/stand/stand.js.map +1 -0
  333. package/dist/stand/subscription-observer.d.ts +53 -0
  334. package/dist/stand/subscription-observer.d.ts.map +1 -0
  335. package/dist/stand/subscription-observer.js +444 -0
  336. package/dist/stand/subscription-observer.js.map +1 -0
  337. package/dist/stand/subscription-records.d.ts +12 -0
  338. package/dist/stand/subscription-records.d.ts.map +1 -0
  339. package/dist/stand/subscription-records.js +99 -0
  340. package/dist/stand/subscription-records.js.map +1 -0
  341. package/dist/stand/subscription-registry.d.ts +285 -0
  342. package/dist/stand/subscription-registry.d.ts.map +1 -0
  343. package/dist/stand/subscription-registry.js +403 -0
  344. package/dist/stand/subscription-registry.js.map +1 -0
  345. package/dist/stand/subscription-runtime.d.ts +102 -0
  346. package/dist/stand/subscription-runtime.d.ts.map +1 -0
  347. package/dist/stand/subscription-runtime.js +380 -0
  348. package/dist/stand/subscription-runtime.js.map +1 -0
  349. package/dist/testing/index.d.ts +14 -0
  350. package/dist/testing/index.d.ts.map +1 -0
  351. package/dist/testing/index.js +34 -0
  352. package/dist/testing/index.js.map +1 -0
  353. package/dist/tsconfig.tsbuildinfo +1 -0
  354. package/package.json +65 -0
@@ -0,0 +1,1780 @@
1
+ /*
2
+ * Copyright 2026, CodeMatters. All rights reserved.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5
+ * in compliance with the License. You may obtain a copy of the License at
6
+ *
7
+ * https://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
10
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11
+ * or implied. See the License for the specific language governing permissions and limitations under
12
+ * the License.
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
+ import { clone, create, getOption, hasOption } from "@bufbuild/protobuf";
19
+ import { TypeUrls } from "@spine-event-engine/core";
20
+ import { EventSchema, BoundedContextNameSchema, TenantIdSchema, } from "@spine-event-engine/proto";
21
+ import { SPI_type, internal_all, internal_type } from "@spine-event-engine/proto";
22
+ import { ColumnTypes, EventStore, InMemoryStorageFactory, RecordColumn, TenantBoundary, } from "@spine-event-engine/storage";
23
+ import { CommandBus, commandBusAccess } from "../bus/command-bus.js";
24
+ import { EventBus, eventBusAccess, } from "../bus/event-bus.js";
25
+ import { DeliveryReadiness, } from "./local-inbox-handoff.js";
26
+ import { LocalEntityInbox } from "./entity-inbox.js";
27
+ import { LocalProjectionInbox } from "./projection-handoff.js";
28
+ import { TenantIndexes } from "./tenant-index.js";
29
+ import { Repository, repositoryAccess, } from "../repository/repository.js";
30
+ import { GeneratedRegistryDiscovery } from "../handler/generated-registry-discovery.js";
31
+ import { HandlerRegistryIngestor, } from "../handler/generated-handler-registry.js";
32
+ import { HandlerMetadataRegistry, } from "../handler/handler-metadata.js";
33
+ import { SignalMetadata } from "../runtime/signal-metadata.js";
34
+ import { Stand } from "../stand/stand.js";
35
+ import { SubscriptionRuntime, subscriptionRuntimeAccess } from "../stand/subscription-runtime.js";
36
+ import { StorageSubscriptionRegistry, } from "../stand/subscription-registry.js";
37
+ import { UniformAcrossAllShards } from "../delivery/delivery-builder.js";
38
+ import { InboxTargets } from "../delivery/inbox.js";
39
+ import { ShardIndex } from "../delivery/shard-index.js";
40
+ import { emitServerError } from "../server/server-log.js";
41
+ import { IntegrationBroker } from "../integration/integration-broker.js";
42
+ import { ServerEnvironment } from "../server/server-environment.js";
43
+ /**
44
+ * Error thrown when a bounded context name cannot be accepted.
45
+ */
46
+ export class BoundedContextNameError extends Error {
47
+ // prettier-ignore
48
+ /**
49
+ * Rejected raw value.
50
+ */
51
+ value;
52
+ /**
53
+ * Creates a deterministic bounded-context name validation error.
54
+ *
55
+ * @param value Contains the rejected name value.
56
+ */
57
+ constructor(value) {
58
+ super('A Bounded Context name cannot be empty, blank, or start with "__spine/".');
59
+ this.name = "BoundedContextNameError";
60
+ this.value = value;
61
+ Object.setPrototypeOf(this, new.target.prototype);
62
+ }
63
+ }
64
+ const frameworkConstructionToken = Object.freeze({
65
+ frameworkConstructionToken: true,
66
+ });
67
+ const dispatchFailureLimit = 10;
68
+ const dispatchErrorMessageLimit = 500;
69
+ const dispatchErrorStackLimit = 2_000;
70
+ const generatedRegistryFile = "generated/handler/generated-handler-registry.js";
71
+ const moduleSchemeRe = /^[A-Za-z][A-Za-z\d+.-]*:/;
72
+ const internalStoragePrefix = "__spine/";
73
+ const generatedRegistryLoadAttempts = new Map();
74
+ const eventSubscribers = new WeakMap();
75
+ const contextSystemPairings = new WeakMap();
76
+ const contextTenantIndexes = new WeakMap();
77
+ const contextStorageFactories = new WeakMap();
78
+ const contextDeliveryDescriptors = new WeakMap();
79
+ const contextSubscriptionRuntimes = new WeakMap();
80
+ const contextLoggers = new WeakMap();
81
+ const contextEventBuses = new WeakMap();
82
+ const closingContexts = new WeakSet();
83
+ const contextDispatchFailureRecorders = new WeakMap();
84
+ const contextIntegrations = new WeakMap();
85
+ const systemEventPosters = new WeakMap();
86
+ const builderBuilds = new WeakMap();
87
+ let constructBoundedContext;
88
+ let constructBoundedContextBuilder;
89
+ let constructContextSpec;
90
+ /**
91
+ * Represents a built bounded context and its command, event, repository, and read-side resources.
92
+ */
93
+ export class BoundedContext {
94
+ #snapshot;
95
+ #commandBus;
96
+ #eventBus;
97
+ #systemEventBus;
98
+ #commandEndpoint;
99
+ #eventEndpoint;
100
+ #entityInbox;
101
+ #projectionInbox;
102
+ #deliveryStrategy;
103
+ #registeredRepositories = [];
104
+ #storedEventDispatchFailures = [];
105
+ #repositoryViews = new Set();
106
+ #storageFactory;
107
+ #stand;
108
+ #systemStand;
109
+ #subscriptionRuntime;
110
+ #closed;
111
+ /**
112
+ * Registers the framework-only construction hook for this module.
113
+ */
114
+ static {
115
+ constructBoundedContext = (snapshot, commandBus, eventBus, systemEventBus, stand, systemStand, runtime, systemSpec, storageFactory, repositories, deliveryStrategy, token) => new BoundedContext(snapshot, commandBus, eventBus, systemEventBus, stand, systemStand, runtime, systemSpec, storageFactory, repositories, deliveryStrategy, token);
116
+ }
117
+ /**
118
+ * Creates a framework-owned bounded context.
119
+ *
120
+ * @param snapshot Contains the immutable context metadata.
121
+ * @param commandBus Dispatches commands accepted by this context.
122
+ * @param eventBus Dispatches events accepted by this context.
123
+ * @param systemEventBus Dispatches framework-only System events.
124
+ * @param stand Stores read-side state for this context.
125
+ * @param systemStand Stores read-side state for the paired System Context.
126
+ * @param subscriptionRuntime Coordinates pair-owned subscription delivery.
127
+ * @param systemSpec Contains paired System Context metadata.
128
+ * @param storageFactory Creates context storage.
129
+ * @param repositories Lists repositories to register.
130
+ * @param deliveryStrategy Selects immutable Entity Inbox shards.
131
+ * @param token Proves framework-controlled construction.
132
+ */
133
+ constructor(snapshot, commandBus, eventBus, systemEventBus, stand, systemStand, subscriptionRuntime, systemSpec, storageFactory, repositories, deliveryStrategy, token) {
134
+ ContextParts.requireFrameworkConstructionToken(token, "BoundedContext instances are framework-owned.");
135
+ this.#snapshot = ContextParts.cloneContextSnapshot(snapshot);
136
+ this.#commandBus = commandBus;
137
+ this.#eventBus = eventBus;
138
+ this.#systemEventBus = systemEventBus;
139
+ contextEventBuses.set(this, [eventBus, systemEventBus]);
140
+ this.#stand = stand;
141
+ this.#systemStand = systemStand;
142
+ this.#subscriptionRuntime = subscriptionRuntime;
143
+ this.#storageFactory = storageFactory;
144
+ this.#deliveryStrategy = ContextParts.snapshotDeliveryStrategy(deliveryStrategy);
145
+ this.#commandEndpoint = Object.freeze({
146
+ acceptedCommandTypes: () => this.#commandBus.acceptedCommandTypes(),
147
+ post: (command) => this.#commandBus.post(command),
148
+ });
149
+ this.#eventEndpoint = Object.freeze({
150
+ acceptedEventTypes: () => ContextParts.exposedEventTypeUrls(this.#eventBus),
151
+ post: (event) => ContextParts.postContextEvent(this, event),
152
+ });
153
+ contextDispatchFailureRecorders.set(this, (event, error) => {
154
+ this.#recordDispatchFailure(event, error);
155
+ });
156
+ const deliveryReadiness = new DeliveryReadiness();
157
+ const tenantIndex = TenantIndexes.create({
158
+ contextName: this.#snapshot.name.value,
159
+ tenantMode: this.#snapshot.tenantMode,
160
+ storageFactory,
161
+ });
162
+ const keepTenant = (tenantId) => tenantIndex.keep(tenantId);
163
+ this.#entityInbox = new LocalEntityInbox(this.#snapshot.name.value, deliveryReadiness, keepTenant, this.#deliveryStrategy);
164
+ this.#projectionInbox = new LocalProjectionInbox(this.#snapshot.name.value, deliveryReadiness, keepTenant);
165
+ eventSubscribers.set(this, (typeUrl, subscriber) => eventBusAccess.subscribe(this.#eventBus, typeUrl, subscriber));
166
+ systemEventPosters.set(this, (event) => this.#systemEventBus.post(event));
167
+ contextSystemPairings.set(this, ContextParts.createSystemPairing(this.#snapshot, systemSpec));
168
+ contextTenantIndexes.set(this, tenantIndex);
169
+ contextStorageFactories.set(this, storageFactory);
170
+ contextSubscriptionRuntimes.set(this, subscriptionRuntime);
171
+ contextDeliveryDescriptors.set(this, ContextParts.createDeliveryDescriptor(this.#snapshot, storageFactory, tenantIndex, this.#entityInbox, this.#projectionInbox, deliveryReadiness));
172
+ try {
173
+ this.#registerRepositories(repositories);
174
+ this.#subscriptionRuntime.start();
175
+ }
176
+ catch (error) {
177
+ try {
178
+ ContextParts.cleanupFailedContext(this, tenantIndex);
179
+ }
180
+ catch (cleanupError) {
181
+ throw new AggregateError([error, cleanupError], "Bounded Context build failed, and tenant index cleanup also failed.");
182
+ }
183
+ throw error;
184
+ }
185
+ Object.freeze(this);
186
+ }
187
+ #registerRepositories(repositories) {
188
+ const preparedRepositories = this.#prepareRepositories(repositories);
189
+ try {
190
+ for (const preparedRepository of preparedRepositories) {
191
+ ContextParts.rejectRegisteredRepository(preparedRepository.repository);
192
+ }
193
+ for (const preparedRepository of preparedRepositories) {
194
+ this.#stand.register(preparedRepository.snapshot.stateSchema, {
195
+ columns: ContextParts.repositoryColumns(preparedRepository.snapshot),
196
+ });
197
+ preparedRepository.commit();
198
+ this.#registeredRepositories.push(preparedRepository.snapshot);
199
+ if (preparedRepository.entityInboxTarget !== undefined) {
200
+ this.#entityInbox.register(preparedRepository.entityInboxTarget);
201
+ }
202
+ if (preparedRepository.projectionInboxTarget !== undefined) {
203
+ this.#projectionInbox.register(preparedRepository.projectionInboxTarget);
204
+ }
205
+ this.#repositoryViews.add(preparedRepository.repository);
206
+ }
207
+ }
208
+ catch (error) {
209
+ this.#failRegistration(error, preparedRepositories);
210
+ }
211
+ }
212
+ #prepareRepositories(repositories) {
213
+ const registration = {
214
+ name: ContextParts.cloneName(this.#snapshot.name),
215
+ storageContext: ContextParts.createStorageMode(this.#snapshot.spec),
216
+ storageFactory: this.#storageFactory,
217
+ stand: this.#stand,
218
+ entityInbox: this.#entityInbox,
219
+ projectionInbox: this.#projectionInbox,
220
+ dispatchStored: (event) => eventBusAccess.postStored(this.#eventBus, event),
221
+ dispatchStoredFollowUp: (event) => eventBusAccess.postStoredFollowUp(this.#eventBus, event),
222
+ postEventFollowUp: (event) => eventBusAccess.postFollowUp(this.#eventBus, event),
223
+ registerEventSchema: (schema) => {
224
+ eventBusAccess.registerSchemas(this.#eventBus, [schema]);
225
+ },
226
+ registerSystemEventSchema: (schema) => {
227
+ eventBusAccess.registerSchemas(this.#systemEventBus, [schema]);
228
+ },
229
+ postSystemFollowUp: (event) => eventBusAccess.postFollowUp(this.#systemEventBus, event),
230
+ onPostCommand: (command) => commandBusAccess.postInternal(this.#commandBus, command),
231
+ recordDispatchFailure: (event, error) => {
232
+ this.#recordDispatchFailure(event, error);
233
+ },
234
+ };
235
+ const preparedRepositories = [];
236
+ try {
237
+ for (const repository of repositories) {
238
+ preparedRepositories.push(ContextParts.prepareRepositoryForContext(repository, registration));
239
+ }
240
+ }
241
+ catch (error) {
242
+ this.#failRegistration(error, preparedRepositories);
243
+ }
244
+ return preparedRepositories;
245
+ }
246
+ #failRegistration(error, preparedRepositories) {
247
+ const closeErrors = ContextParts.closePreparedRepositories(preparedRepositories);
248
+ if (closeErrors.length > 0) {
249
+ throw new AggregateError([error, ...closeErrors], "Repository registration failed, and prepared repository storage cleanup also failed.");
250
+ }
251
+ throw error;
252
+ }
253
+ /**
254
+ * Creates a builder for a context without tenant isolation.
255
+ *
256
+ * @param name Names the bounded context.
257
+ * @returns Returns a builder initialized with the supplied name.
258
+ */
259
+ static singleTenant(name) {
260
+ return ContextParts.createBoundedContextBuilder(ContextParts.createSpecSnapshot(name, false));
261
+ }
262
+ /**
263
+ * Creates a builder for a tenant-isolated context.
264
+ *
265
+ * @param name Names the bounded context.
266
+ * @returns Returns a builder initialized with the supplied name.
267
+ */
268
+ static multitenant(name) {
269
+ return ContextParts.createBoundedContextBuilder(ContextParts.createSpecSnapshot(name, true));
270
+ }
271
+ /**
272
+ * Returns the bounded context name.
273
+ *
274
+ * @returns Returns the immutable context name.
275
+ */
276
+ get name() {
277
+ return this.#snapshot.name;
278
+ }
279
+ /**
280
+ * Returns the context's tenant isolation mode.
281
+ *
282
+ * @returns Returns the configured tenant mode.
283
+ */
284
+ get tenantMode() {
285
+ return this.#snapshot.tenantMode;
286
+ }
287
+ /**
288
+ * Returns whether this context isolates data by tenant.
289
+ *
290
+ * @returns Returns true when the context is multitenant.
291
+ */
292
+ get isMultitenant() {
293
+ return this.#snapshot.tenantMode === "multitenant";
294
+ }
295
+ /**
296
+ * Returns a copy-safe specification used to build this context.
297
+ *
298
+ * @returns Returns the context specification.
299
+ */
300
+ get spec() {
301
+ return ContextParts.createContextSpec(this.#snapshot.spec);
302
+ }
303
+ /**
304
+ * Returns a copy-safe immutable metadata snapshot.
305
+ *
306
+ * @returns Returns the context metadata snapshot.
307
+ */
308
+ get snapshot() {
309
+ return ContextParts.cloneContextSnapshot(this.#snapshot);
310
+ }
311
+ /**
312
+ * Returns the command endpoint owned by this context.
313
+ *
314
+ * @returns Returns the context command endpoint.
315
+ */
316
+ commandBus() {
317
+ return this.#commandEndpoint;
318
+ }
319
+ /**
320
+ * Returns the event endpoint owned by this context.
321
+ *
322
+ * @returns Returns the context event endpoint.
323
+ */
324
+ eventBus() {
325
+ return this.#eventEndpoint;
326
+ }
327
+ /**
328
+ * Returns the context-owned read-side Stand.
329
+ *
330
+ * @returns Returns the read-side state store.
331
+ */
332
+ stand() {
333
+ return this.#stand;
334
+ }
335
+ /**
336
+ * Lists copy-safe views of repositories registered with this context.
337
+ *
338
+ * @returns Returns immutable repository views.
339
+ */
340
+ registeredRepositories() {
341
+ return this.#registeredRepositories.map((snapshot) => ContextParts.createRepositoryView(snapshot));
342
+ }
343
+ /**
344
+ * Returns copy-safe diagnostics for asynchronous event follow-up failures.
345
+ *
346
+ * Entries can describe already-stored event dispatch or an independent
347
+ * follow-up post that failed before storage.
348
+ *
349
+ * @returns Returns immutable failure diagnostics.
350
+ */
351
+ storedEventDispatchFailures() {
352
+ return this.#storedEventDispatchFailures.map(ContextParts.cloneDispatchFailure);
353
+ }
354
+ /**
355
+ * Clears and locally replays every registered Projection from already-stored events.
356
+ *
357
+ * Despite its legacy name, this method is not Projection catch-up. It is a
358
+ * process-local maintenance helper and provides no Projection targeting,
359
+ * historical starting point, durable operation identity, progress,
360
+ * historical/live coordination, restart, resumption, or multi-node work.
361
+ *
362
+ * Supported boundary:
363
+ * - projection subscribers only;
364
+ * - already-stored events only;
365
+ * - clear then replay, with no event re-append;
366
+ * - single-tenant contexts reject `tenantId`;
367
+ * - multitenant contexts require the exact non-blank `tenantId`;
368
+ * - process-local sequential execution on the same EventBus runtime queue as
369
+ * live event intake and stored redispatch.
370
+ *
371
+ * Unsupported boundary:
372
+ * - Delivery jobs, schedulers, inbox lifecycle, retries, and transport
373
+ * topology;
374
+ * - durable live-traffic catch-up orchestration across processes.
375
+ *
376
+ * @param options Selects the tenant slice to rebuild.
377
+ * @returns Resolves to replay and clear counts for the selected slice.
378
+ */
379
+ async catchUpReadSide(options = {}) {
380
+ return eventBusAccess.runExclusive(this.#eventBus, () => this.#catchUpReadSideOnce(options));
381
+ }
382
+ async #catchUpReadSideOnce(options) {
383
+ const storageContext = ContextParts.catchUpStorageContext(this.#snapshot.spec, options);
384
+ const tenantOptions = ContextParts.catchUpStandOptions(storageContext);
385
+ const projections = ContextParts.projectionDispatchers(this.#repositoryViews);
386
+ const clearTargets = ContextParts.projectionStateClearTargets(projections);
387
+ const clearedStateTypes = [];
388
+ let clearedEntityCount = 0;
389
+ let replayedEventCount = 0;
390
+ for (const target of clearTargets) {
391
+ clearedEntityCount += await this.#stand.clear(target.schema, tenantOptions);
392
+ clearedStateTypes.push(target.typeUrl);
393
+ }
394
+ const events = await ContextParts.readStoredEvents(storageContext, this.#storageFactory);
395
+ for (const event of events) {
396
+ try {
397
+ ContextParts.validateReplayTenant(storageContext, event);
398
+ replayedEventCount += await ContextParts.dispatchStoredProjectionEvent(projections, event, true);
399
+ }
400
+ catch (error) {
401
+ throw ContextParts.catchUpReplayError(event, error);
402
+ }
403
+ }
404
+ return Object.freeze({
405
+ replayedEventCount,
406
+ clearedEntityCount,
407
+ clearedStateTypes: Object.freeze(clearedStateTypes),
408
+ });
409
+ }
410
+ /**
411
+ * Closes context-owned buses, Stand, and repository storage/runtime bindings.
412
+ *
413
+ * Close is idempotent and returns the same close outcome on repeated calls.
414
+ * The context attempts every owned close hook; when any hook fails, the
415
+ * returned promise rejects with an `AggregateError` after the remaining hooks
416
+ * have also been attempted.
417
+ *
418
+ * @returns A promise that settles after all owned resources close.
419
+ */
420
+ close() {
421
+ closingContexts.add(this);
422
+ eventBusAccess.beginClose(this.#eventBus);
423
+ this.#closed ??= this.#closeOnce();
424
+ return this.#closed;
425
+ }
426
+ async #closeOnce() {
427
+ const errors = [];
428
+ await ContextParts.closeContextPart(() => ContextParts.closeIntegration(this), errors);
429
+ commandBusAccess.beginClose(this.#commandBus);
430
+ eventBusAccess.beginClose(this.#eventBus);
431
+ await ContextParts.closeContextPart(() => ContextParts.drainContextBuses(this.#commandBus, this.#eventBus), errors);
432
+ await ContextParts.closeContextPart(() => commandBusAccess.finishClose(this.#commandBus), errors);
433
+ await ContextParts.closeContextPart(() => eventBusAccess.finishClose(this.#eventBus), errors);
434
+ this.#subscriptionRuntime.beginClose();
435
+ eventBusAccess.beginClose(this.#systemEventBus);
436
+ await ContextParts.closeContextPart(() => eventBusAccess.drain(this.#systemEventBus), errors);
437
+ await ContextParts.closeContextPart(() => this.#subscriptionRuntime.drainClose(), errors);
438
+ await ContextParts.closeContextPart(() => eventBusAccess.finishClose(this.#systemEventBus), errors);
439
+ await ContextParts.closeContextPart(() => this.#stand.close(), errors);
440
+ await ContextParts.closeContextPart(() => this.#systemStand.close(), errors);
441
+ await ContextParts.closeContextPart(() => this.#subscriptionRuntime.finishClose(), errors);
442
+ await ContextParts.closeContextPart(() => {
443
+ ContextParts.requireTenantIndex(this).close();
444
+ }, errors);
445
+ for (const repository of this.#repositoryViews) {
446
+ await ContextParts.closeContextPart(() => {
447
+ repositoryAccess.clearRuntime(repository);
448
+ registeredRepositories.delete(repository);
449
+ }, errors);
450
+ }
451
+ if (errors.length > 0) {
452
+ this.#closed = undefined;
453
+ throw new AggregateError(ContextParts.flattenErrors(errors), "BoundedContext close failed.");
454
+ }
455
+ ContextParts.clearContextMetadata(this);
456
+ }
457
+ #recordDispatchFailure(event, error) {
458
+ this.#storedEventDispatchFailures.push(Object.freeze({
459
+ event: clone(EventSchema, event),
460
+ error: ContextParts.snapshotDispatchError(error),
461
+ }));
462
+ if (this.#storedEventDispatchFailures.length > dispatchFailureLimit) {
463
+ this.#storedEventDispatchFailures.splice(0, this.#storedEventDispatchFailures.length - dispatchFailureLimit);
464
+ }
465
+ const logger = contextLoggers.get(this);
466
+ const eventType = event.message?.typeUrl;
467
+ if (logger !== undefined && eventType !== undefined && eventType.length > 0) {
468
+ emitServerError(logger, "Repository follow-up dispatch failed.", {
469
+ eventType,
470
+ operation: "repository.follow_up",
471
+ reasonCode: "dispatch_failed",
472
+ });
473
+ }
474
+ }
475
+ }
476
+ /**
477
+ * Exposes framework-only operations for built contexts and their builders.
478
+ */
479
+ export const boundedContextAccess = Object.freeze({
480
+ installLogger(context, logger) {
481
+ if (!contextStorageFactories.has(context)) {
482
+ throw new TypeError("Context logger requires a built BoundedContext instance.");
483
+ }
484
+ contextLoggers.set(context, logger);
485
+ const buses = contextEventBuses.get(context);
486
+ if (buses === undefined) {
487
+ throw new TypeError("Context logger requires a built BoundedContext instance.");
488
+ }
489
+ eventBusAccess.installLogger(buses[0], logger);
490
+ eventBusAccess.installLogger(buses[1], logger);
491
+ const runtime = contextSubscriptionRuntimes.get(context);
492
+ if (runtime === undefined) {
493
+ throw new TypeError("Context logger requires a built BoundedContext instance.");
494
+ }
495
+ subscriptionRuntimeAccess.installLogger(runtime, logger);
496
+ },
497
+ loggerFor(context) {
498
+ const logger = contextLoggers.get(context);
499
+ if (logger === undefined) {
500
+ throw new TypeError("Context logger requires a built BoundedContext instance.");
501
+ }
502
+ return logger;
503
+ },
504
+ recordDispatchFailure(context, event, error) {
505
+ const record = contextDispatchFailureRecorders.get(context);
506
+ if (record === undefined) {
507
+ throw new TypeError("Dispatch failure recording requires a built BoundedContext instance.");
508
+ }
509
+ record(event, error);
510
+ },
511
+ isBuilder(value) {
512
+ return (typeof value === "object" &&
513
+ value !== null &&
514
+ builderBuilds.has(value));
515
+ },
516
+ build(builder, defaultStorageFactory) {
517
+ const build = builderBuilds.get(builder);
518
+ if (build === undefined) {
519
+ throw new TypeError("Builder access requires a BoundedContextBuilder instance.");
520
+ }
521
+ return build(defaultStorageFactory);
522
+ },
523
+ subscribeToEvent(context, typeUrl, subscriber) {
524
+ const subscribe = eventSubscribers.get(context);
525
+ if (subscribe === undefined) {
526
+ throw new TypeError("Event subscription requires a built BoundedContext instance.");
527
+ }
528
+ return subscribe(typeUrl, subscriber);
529
+ },
530
+ postSystemEvent(context, event) {
531
+ const post = systemEventPosters.get(context);
532
+ if (post === undefined) {
533
+ throw new TypeError("System event posting requires a built BoundedContext instance.");
534
+ }
535
+ return post(event);
536
+ },
537
+ systemPairing(context) {
538
+ return ContextParts.cloneSystemPairing(ContextParts.requireSystemPairing(context));
539
+ },
540
+ tenantIndex(context) {
541
+ return ContextParts.requireTenantIndex(context);
542
+ },
543
+ storageFactory(context) {
544
+ const storageFactory = contextStorageFactories.get(context);
545
+ if (storageFactory === undefined) {
546
+ throw new TypeError("Storage access requires a built BoundedContext instance.");
547
+ }
548
+ return storageFactory;
549
+ },
550
+ subscriptionRegistry(context) {
551
+ const runtime = contextSubscriptionRuntimes.get(context);
552
+ if (runtime === undefined) {
553
+ throw new TypeError("Subscription registry access requires a built BoundedContext instance.");
554
+ }
555
+ return runtime.registry();
556
+ },
557
+ consumeSubscription(context, id, onUpdate) {
558
+ const runtime = contextSubscriptionRuntimes.get(context);
559
+ if (runtime === undefined) {
560
+ throw new TypeError("Subscription consumption requires a built BoundedContext instance.");
561
+ }
562
+ return runtime.consume(id, onUpdate);
563
+ },
564
+ delivery(context) {
565
+ const descriptor = contextDeliveryDescriptors.get(context);
566
+ if (descriptor === undefined) {
567
+ throw new TypeError("Delivery access requires a built BoundedContext instance.");
568
+ }
569
+ return descriptor;
570
+ },
571
+ });
572
+ /**
573
+ * Assembles a {@link BoundedContext} from repositories and dispatchers.
574
+ */
575
+ export class BoundedContextBuilder {
576
+ #specSnapshot;
577
+ #commandDispatchers = new Set();
578
+ #eventDispatchers = new Set();
579
+ #repositories = new Set();
580
+ #entityTypes = new Set();
581
+ #generatedRepositoryOptions = new Map();
582
+ #deliveryStrategy = UniformAcrossAllShards.singleShard();
583
+ #storageFactory;
584
+ #subscriptionRegistry;
585
+ #persistSystemEvents = false;
586
+ #generatedRegistryRoot;
587
+ /**
588
+ * Registers the framework-only construction hook for this module.
589
+ */
590
+ static {
591
+ constructBoundedContextBuilder = (snapshot, token) => new BoundedContextBuilder(snapshot, token);
592
+ }
593
+ /**
594
+ * Creates a framework-owned context builder.
595
+ *
596
+ * @param specSnapshot Contains the initial context specification.
597
+ * @param token Proves framework-controlled construction.
598
+ */
599
+ constructor(specSnapshot, token) {
600
+ ContextParts.requireFrameworkConstructionToken(token, "BoundedContextBuilder instances are framework-owned.");
601
+ this.#specSnapshot = ContextParts.cloneSpecSnapshot(specSnapshot);
602
+ builderBuilds.set(this, (defaultStorageFactory) => this.#buildAsyncWith(defaultStorageFactory));
603
+ Object.freeze(this);
604
+ }
605
+ /**
606
+ * Returns the name configured for the context to build.
607
+ *
608
+ * @returns Returns the immutable context name.
609
+ */
610
+ get name() {
611
+ return this.#specSnapshot.name;
612
+ }
613
+ /**
614
+ * Returns a copy-safe specification configured for the context.
615
+ *
616
+ * @returns Returns the context specification.
617
+ */
618
+ get spec() {
619
+ return ContextParts.createContextSpec(this.#specSnapshot);
620
+ }
621
+ /**
622
+ * Returns the tenant isolation mode configured for the context.
623
+ *
624
+ * @returns Returns the configured tenant mode.
625
+ */
626
+ get tenantMode() {
627
+ return ContextParts.toTenantMode(this.#specSnapshot.multitenant);
628
+ }
629
+ /**
630
+ * Returns whether this builder will create a tenant-isolated context.
631
+ *
632
+ * @returns Returns true when the built context will be multitenant.
633
+ */
634
+ isMultitenant() {
635
+ return this.#specSnapshot.multitenant;
636
+ }
637
+ /**
638
+ * Adds an explicitly assembled repository or an Entity class.
639
+ *
640
+ * @param entry The repository or Entity class to register.
641
+ * @param options Optional settings used only when an Entity class is supplied.
642
+ * @returns This builder for further configuration.
643
+ */
644
+ add(entry, options) {
645
+ if (repositoryAccess.hasInstance(entry)) {
646
+ if (options !== undefined) {
647
+ throw new TypeError("Explicit Repository instances do not accept generated options.");
648
+ }
649
+ this.#repositories.add(entry);
650
+ return this;
651
+ }
652
+ ContextParts.requireEntityClass(entry, "BoundedContextBuilder.add(repository)");
653
+ this.#entityTypes.add(entry);
654
+ if (options !== undefined) {
655
+ this.#generatedRepositoryOptions.set(entry, Object.freeze({ ...options }));
656
+ }
657
+ return this;
658
+ }
659
+ /**
660
+ * Removes a repository from the context registration list.
661
+ *
662
+ * @param repository Identifies the repository to remove.
663
+ * @returns Returns this builder for further configuration.
664
+ */
665
+ remove(repository) {
666
+ ContextParts.requireRepositoryInstance(repository, "BoundedContextBuilder.remove(repository)");
667
+ this.#repositories.delete(repository);
668
+ return this;
669
+ }
670
+ /**
671
+ * Adds a command dispatcher to the context being built.
672
+ *
673
+ * @param dispatcher Dispatches commands accepted by this context.
674
+ * @returns Returns this builder for further configuration.
675
+ */
676
+ addCommandDispatcher(dispatcher) {
677
+ this.#commandDispatchers.add(dispatcher);
678
+ return this;
679
+ }
680
+ /**
681
+ * Removes a command dispatcher from the context being built.
682
+ *
683
+ * @param dispatcher Identifies the dispatcher to remove.
684
+ * @returns Returns this builder for further configuration.
685
+ */
686
+ removeCommandDispatcher(dispatcher) {
687
+ this.#commandDispatchers.delete(dispatcher);
688
+ return this;
689
+ }
690
+ /**
691
+ * Adds an event dispatcher to the context being built.
692
+ *
693
+ * @param dispatcher Dispatches events accepted by this context.
694
+ * @returns Returns this builder for further configuration.
695
+ */
696
+ addEventDispatcher(dispatcher) {
697
+ this.#eventDispatchers.add(dispatcher);
698
+ return this;
699
+ }
700
+ /**
701
+ * Removes an event dispatcher from the context being built.
702
+ *
703
+ * @param dispatcher Identifies the dispatcher to remove.
704
+ * @returns Returns this builder for further configuration.
705
+ */
706
+ removeEventDispatcher(dispatcher) {
707
+ this.#eventDispatchers.delete(dispatcher);
708
+ return this;
709
+ }
710
+ /**
711
+ * Sets a storage factory for event, repository-state, and Stand storage.
712
+ *
713
+ * @param storageFactory Creates the context's persistent storage.
714
+ * @returns Returns this builder for further configuration.
715
+ */
716
+ withStorageFactory(storageFactory) {
717
+ this.#storageFactory = storageFactory;
718
+ return this;
719
+ }
720
+ /**
721
+ * Persists internal system events in the paired System Context storage.
722
+ *
723
+ * System events are forgotten by default. Enabling this option does not put
724
+ * them into the domain EventStore.
725
+ *
726
+ * @returns Returns this builder for further configuration.
727
+ */
728
+ persistSystemEvents() {
729
+ this.#persistSystemEvents = true;
730
+ return this;
731
+ }
732
+ /**
733
+ * Sets a complete custom registry and transfers it to the first build attempt.
734
+ *
735
+ * The built context closes the registry. A failed first build also begins its
736
+ * closure, so callers must not reuse it.
737
+ *
738
+ * @param registry Stores this context's Stand subscription definitions.
739
+ * @returns Returns this builder for further configuration.
740
+ */
741
+ withSubscriptionRegistry(registry) {
742
+ this.#subscriptionRegistry = registry;
743
+ return this;
744
+ }
745
+ /**
746
+ * Sets the target-to-shard strategy for the context-owned Entity Inbox.
747
+ *
748
+ * @param strategy Selects the durable shard for Aggregate and Process Manager targets.
749
+ * @returns Returns this builder for further configuration.
750
+ */
751
+ withDeliveryStrategy(strategy) {
752
+ this.#deliveryStrategy = ContextParts.snapshotDeliveryStrategy(strategy);
753
+ return this;
754
+ }
755
+ /**
756
+ * Sets a trusted compiled application root for generated handler metadata.
757
+ *
758
+ * @param root Names the compiled package or application root.
759
+ * @returns Returns this builder for further configuration.
760
+ */
761
+ withGeneratedRegistryRoot(root) {
762
+ this.#generatedRegistryRoot = root;
763
+ return this;
764
+ }
765
+ /**
766
+ * Builds a context from explicitly added repositories and dispatchers.
767
+ *
768
+ * @returns Returns the built context.
769
+ */
770
+ build() {
771
+ ContextParts.rejectSyncEntityAssembly(this.#entityTypes);
772
+ return this.#buildWith([...this.#repositories], this.#storageFactory ?? new InMemoryStorageFactory());
773
+ }
774
+ /**
775
+ * Builds a context after loading generated metadata for added entity classes.
776
+ *
777
+ * @returns Resolves to the built context.
778
+ */
779
+ async buildAsync() {
780
+ return this.#buildAsyncWith();
781
+ }
782
+ async #buildAsyncWith(defaultStorageFactory) {
783
+ const repositories = [
784
+ ...this.#repositories,
785
+ ...(await this.#loadGeneratedRepositories([...this.#entityTypes])),
786
+ ];
787
+ const context = this.#buildWith(repositories, this.#storageFactory ?? defaultStorageFactory ?? new InMemoryStorageFactory());
788
+ try {
789
+ await ContextParts.integrationReady(context);
790
+ }
791
+ catch (error) {
792
+ try {
793
+ await context.close();
794
+ }
795
+ catch (closeError) {
796
+ throw new AggregateError([error, closeError], "BoundedContext broker open failed during cleanup.");
797
+ }
798
+ throw error;
799
+ }
800
+ return context;
801
+ }
802
+ #buildWith(repositories, storageFactory) {
803
+ let registry = this.#subscriptionRegistry;
804
+ this.#subscriptionRegistry = undefined;
805
+ const registeredRepositories = [...repositories];
806
+ let eventStore;
807
+ let systemEventStore;
808
+ let eventBus;
809
+ let systemEventBus;
810
+ let stand;
811
+ let systemStand;
812
+ let runtime;
813
+ try {
814
+ ContextParts.preflightRepositories(registeredRepositories);
815
+ const eventDispatchers = [
816
+ ...ContextParts.repositoryEventDispatchers(registeredRepositories),
817
+ ...this.#eventDispatchers,
818
+ ];
819
+ const repositorySystemEventDispatchers = ContextParts.repositorySystemEventDispatchers(registeredRepositories);
820
+ const domainEventDispatchers = ContextParts.domainEventDispatchers(eventDispatchers);
821
+ const systemEventDispatchers = [
822
+ ...ContextParts.systemEventDispatchers(eventDispatchers),
823
+ ...repositorySystemEventDispatchers,
824
+ ];
825
+ const commandBus = new CommandBus([
826
+ ...this.#commandDispatchers,
827
+ ...ContextParts.repositoryCommandDispatchers(registeredRepositories),
828
+ ]);
829
+ const systemSpec = ContextParts.createSystemSpec(this.#specSnapshot, this.#persistSystemEvents);
830
+ systemEventStore = systemSpec.storesEvents
831
+ ? new EventStore(ContextParts.createStorageMode(systemSpec), storageFactory)
832
+ : undefined;
833
+ systemEventBus = eventBusAccess.createSystemBus(systemEventStore);
834
+ for (const dispatcher of systemEventDispatchers)
835
+ systemEventBus.register(dispatcher);
836
+ systemStand = new Stand({
837
+ context: ContextParts.createStorageMode(systemSpec),
838
+ storageFactory,
839
+ });
840
+ eventStore = this.createEventStore(storageFactory);
841
+ eventBus = new EventBus(eventStore);
842
+ for (const dispatcher of domainEventDispatchers)
843
+ eventBus.register(dispatcher);
844
+ eventBusAccess.registerSchemas(eventBus, ContextParts.repositoryProducedEventSchemas(registeredRepositories));
845
+ stand = new Stand({
846
+ context: ContextParts.createStorageMode(this.#specSnapshot),
847
+ storageFactory,
848
+ });
849
+ registry ??= new StorageSubscriptionRegistry(ContextParts.createSubscriptionStorageContext(this.#specSnapshot), storageFactory);
850
+ runtime = new SubscriptionRuntime(stand, systemStand, eventBus, systemEventBus, registry);
851
+ const context = ContextParts.createBoundedContext(this.#specSnapshot, commandBus, eventBus, systemEventBus, stand, systemStand, runtime, systemSpec, storageFactory, registeredRepositories, this.#deliveryStrategy);
852
+ ContextParts.attachIntegration(context, eventBus, systemSpec, ContextParts.externalEventSchemas(domainEventDispatchers));
853
+ return context;
854
+ }
855
+ catch (error) {
856
+ const cleanupErrors = [];
857
+ ContextParts.attemptCleanup(() => runtime?.abortClose(), cleanupErrors);
858
+ if (runtime === undefined) {
859
+ ContextParts.attemptCleanup(() => void registry?.close().catch(() => undefined), cleanupErrors);
860
+ }
861
+ ContextParts.attemptCleanup(() => void stand?.close().catch(() => undefined), cleanupErrors);
862
+ ContextParts.attemptCleanup(() => void systemStand?.close().catch(() => undefined), cleanupErrors);
863
+ ContextParts.attemptCleanup(() => {
864
+ if (systemEventBus !== undefined)
865
+ eventBusAccess.abortClose(systemEventBus);
866
+ else if (systemEventStore !== undefined)
867
+ systemEventStore.close();
868
+ }, cleanupErrors);
869
+ ContextParts.attemptCleanup(() => {
870
+ if (eventBus !== undefined)
871
+ eventBusAccess.abortClose(eventBus);
872
+ else if (eventStore !== undefined)
873
+ eventStore.close();
874
+ }, cleanupErrors);
875
+ if (cleanupErrors.length > 0) {
876
+ throw new AggregateError([error, ...cleanupErrors], "Bounded Context build failed during cleanup.");
877
+ }
878
+ throw error;
879
+ }
880
+ }
881
+ async #loadGeneratedRepositories(entityTypes) {
882
+ if (entityTypes.length === 0) {
883
+ return Object.freeze([]);
884
+ }
885
+ const root = ContextParts.requireGeneratedRegistryRoot(this.#generatedRegistryRoot);
886
+ const discovery = new GeneratedRegistryDiscovery();
887
+ const registryModule = await ContextParts.trustedGeneratedRegistryModule(root);
888
+ const registryKey = registryModule.href;
889
+ const registries = (await discovery
890
+ .load({
891
+ modules: [registryModule],
892
+ ...ContextParts.generatedRegistryCacheBust(registryKey),
893
+ })
894
+ .catch((error) => {
895
+ ContextParts.recordGeneratedRegistryFailure(registryKey);
896
+ throw error;
897
+ }));
898
+ const metadata = ContextParts.ingestGeneratedRegistries(registries);
899
+ return Object.freeze(entityTypes.map((entityType) => ContextParts.createGeneratedRepository(entityType, registries, metadata, this.#generatedRepositoryOptions.get(entityType))));
900
+ }
901
+ createEventStore(storageFactory) {
902
+ return new EventStore(ContextParts.createStorageMode(this.#specSnapshot), storageFactory);
903
+ }
904
+ }
905
+ /**
906
+ * Represents the immutable specification used by a context builder.
907
+ */
908
+ export class ContextSpec {
909
+ #snapshot;
910
+ /**
911
+ * Registers the framework-only construction hook for this module.
912
+ */
913
+ static {
914
+ constructContextSpec = (snapshot, token) => new ContextSpec(snapshot, token);
915
+ }
916
+ /**
917
+ * Creates a framework-owned context specification.
918
+ *
919
+ * @param snapshot Contains immutable specification values.
920
+ * @param token Proves framework-controlled construction.
921
+ */
922
+ constructor(snapshot, token) {
923
+ ContextParts.requireFrameworkConstructionToken(token, "ContextSpec instances are framework-owned.");
924
+ this.#snapshot = ContextParts.cloneSpecSnapshot(snapshot);
925
+ Object.freeze(this);
926
+ }
927
+ /**
928
+ * Returns the bounded context name.
929
+ *
930
+ * @returns Returns the immutable context name.
931
+ */
932
+ get name() {
933
+ return this.#snapshot.name;
934
+ }
935
+ /**
936
+ * Returns whether the context requires tenant isolation.
937
+ *
938
+ * @returns Returns true when tenant isolation is required.
939
+ */
940
+ get multitenant() {
941
+ return this.#snapshot.multitenant;
942
+ }
943
+ /**
944
+ * Returns the tenant mode derived from the multitenant setting.
945
+ *
946
+ * @returns Returns the derived tenant mode.
947
+ */
948
+ get tenantMode() {
949
+ return ContextParts.toTenantMode(this.#snapshot.multitenant);
950
+ }
951
+ /**
952
+ * Returns whether the context specification stores its domain event log.
953
+ *
954
+ * @returns Returns true when the context stores events.
955
+ */
956
+ get storesEvents() {
957
+ return this.#snapshot.storesEvents;
958
+ }
959
+ /**
960
+ * Returns a copy-safe immutable snapshot of this specification.
961
+ *
962
+ * @returns Returns the specification snapshot.
963
+ */
964
+ get snapshot() {
965
+ return ContextParts.cloneSpecSnapshot(this.#snapshot);
966
+ }
967
+ }
968
+ const registeredRepositories = new WeakMap();
969
+ class CatchUpReplayError extends Error {
970
+ code = "READ_SIDE_CATCH_UP_REPLAY_FAILED";
971
+ eventId;
972
+ detail;
973
+ constructor(eventId, detail) {
974
+ super(`Read-side catch-up failed for stored event "${eventId}".`);
975
+ this.name = "ReadCatchUpReplayError";
976
+ this.eventId = eventId;
977
+ this.detail = detail;
978
+ Object.setPrototypeOf(this, new.target.prototype);
979
+ }
980
+ }
981
+ /**
982
+ * Assembles private bounded-context lifecycle and replay details.
983
+ */
984
+ const ContextParts = Object.freeze({
985
+ attemptCleanup(onCleanup, errors) {
986
+ try {
987
+ onCleanup();
988
+ }
989
+ catch (error) {
990
+ errors.push(error);
991
+ }
992
+ },
993
+ flattenErrors(errors) {
994
+ return errors.flatMap((error) => error instanceof AggregateError ? ContextParts.flattenErrors(error.errors) : [error]);
995
+ },
996
+ requireFrameworkConstructionToken(token, message) {
997
+ if (token !== frameworkConstructionToken) {
998
+ throw new TypeError(message);
999
+ }
1000
+ },
1001
+ createContextSpec(specSnapshot) {
1002
+ return constructContextSpec(specSnapshot, frameworkConstructionToken);
1003
+ },
1004
+ createBoundedContextBuilder(specSnapshot) {
1005
+ return constructBoundedContextBuilder(specSnapshot, frameworkConstructionToken);
1006
+ },
1007
+ createBoundedContext(specSnapshot, commandBus, eventBus, systemEventBus, stand, systemStand, runtime, systemSpec, storageFactory, repositories, deliveryStrategy) {
1008
+ return constructBoundedContext({
1009
+ name: specSnapshot.name,
1010
+ tenantMode: ContextParts.toTenantMode(specSnapshot.multitenant),
1011
+ spec: specSnapshot,
1012
+ }, commandBus, eventBus, systemEventBus, stand, systemStand, runtime, systemSpec, storageFactory, repositories, deliveryStrategy, frameworkConstructionToken);
1013
+ },
1014
+ createStorageMode(specSnapshot) {
1015
+ return Object.freeze({
1016
+ name: specSnapshot.name.value,
1017
+ multitenant: specSnapshot.multitenant,
1018
+ });
1019
+ },
1020
+ createSubscriptionStorageContext(specSnapshot) {
1021
+ return Object.freeze({
1022
+ name: `${specSnapshot.name.value}:subscriptions`,
1023
+ multitenant: false,
1024
+ });
1025
+ },
1026
+ snapshotDeliveryStrategy(strategy) {
1027
+ if (!Number.isSafeInteger(strategy.shardCount) || strategy.shardCount <= 0) {
1028
+ throw new Error("Delivery strategy shard count must be a positive safe integer.");
1029
+ }
1030
+ const shardCount = strategy.shardCount;
1031
+ return Object.freeze({
1032
+ shardCount,
1033
+ shardFor(targetId, targetType) {
1034
+ const shard = strategy.shardFor(InboxTargets.clone(targetId), targetType);
1035
+ if (shard.ofTotal !== shardCount) {
1036
+ throw new Error("Delivery strategy shard total must equal its resolved shard count.");
1037
+ }
1038
+ return new ShardIndex(shard.index, shard.ofTotal);
1039
+ },
1040
+ });
1041
+ },
1042
+ catchUpStorageContext(specSnapshot, options) {
1043
+ if (!specSnapshot.multitenant) {
1044
+ if (options.tenantId !== undefined) {
1045
+ throw new Error(`Single-tenant read-side catch-up for "${specSnapshot.name.value}" does not accept tenantId.`);
1046
+ }
1047
+ return Object.freeze({ name: specSnapshot.name.value, multitenant: false });
1048
+ }
1049
+ const tenantId = options.tenantId;
1050
+ if (tenantId === undefined) {
1051
+ throw new Error(`Multitenant read-side catch-up for "${specSnapshot.name.value}" requires tenantId.`);
1052
+ }
1053
+ return Object.freeze({
1054
+ name: specSnapshot.name.value,
1055
+ multitenant: true,
1056
+ tenantId: TenantBoundary.from(tenantId).tenantId,
1057
+ });
1058
+ },
1059
+ createBoundedContextName(value) {
1060
+ if (typeof value !== "string" ||
1061
+ value.trim().length === 0 ||
1062
+ value.startsWith(internalStoragePrefix)) {
1063
+ throw new BoundedContextNameError(value);
1064
+ }
1065
+ return Object.freeze({ value });
1066
+ },
1067
+ createSpecSnapshot(name, multitenant) {
1068
+ return Object.freeze({
1069
+ name: ContextParts.createBoundedContextName(name),
1070
+ multitenant,
1071
+ storesEvents: true,
1072
+ });
1073
+ },
1074
+ cloneName(name) {
1075
+ return ContextParts.createBoundedContextName(name.value);
1076
+ },
1077
+ cloneSpecSnapshot(spec) {
1078
+ return Object.freeze({
1079
+ name: ContextParts.cloneName(spec.name),
1080
+ multitenant: spec.multitenant,
1081
+ storesEvents: spec.storesEvents,
1082
+ });
1083
+ },
1084
+ cloneContextSnapshot(snapshot) {
1085
+ return Object.freeze({
1086
+ name: ContextParts.cloneName(snapshot.name),
1087
+ tenantMode: snapshot.tenantMode,
1088
+ spec: ContextParts.cloneSpecSnapshot(snapshot.spec),
1089
+ });
1090
+ },
1091
+ createSystemSpec(domainSpec, storesEvents) {
1092
+ return Object.freeze({
1093
+ name: ContextParts.createBoundedContextName(`${domainSpec.name.value}_System`),
1094
+ multitenant: domainSpec.multitenant,
1095
+ storesEvents,
1096
+ });
1097
+ },
1098
+ createSystemPairing(snapshot, systemSpec) {
1099
+ return Object.freeze({
1100
+ domain: ContextParts.cloneContextSnapshot(snapshot),
1101
+ system: ContextParts.cloneSpecSnapshot(systemSpec),
1102
+ });
1103
+ },
1104
+ cloneSystemPairing(pairing) {
1105
+ return Object.freeze({
1106
+ domain: ContextParts.cloneContextSnapshot(pairing.domain),
1107
+ system: ContextParts.cloneSpecSnapshot(pairing.system),
1108
+ });
1109
+ },
1110
+ exposedEventTypeUrls(eventBus) {
1111
+ return Object.freeze(eventBusAccess
1112
+ .eventSchemas(eventBus)
1113
+ .filter((schema) => !ContextParts.isInternalEventSchema(schema))
1114
+ .map((schema) => TypeUrls.derive(schema)));
1115
+ },
1116
+ externalEventSchemas(dispatchers) {
1117
+ const schemas = new Map();
1118
+ for (const dispatcher of dispatchers) {
1119
+ for (const schema of dispatcher.externalEventSchemas?.() ?? []) {
1120
+ schemas.set(TypeUrls.derive(schema), schema);
1121
+ }
1122
+ }
1123
+ return Object.freeze([...schemas.values()]);
1124
+ },
1125
+ attachIntegration(context, eventBus, systemSpec, externalEventSchemas) {
1126
+ const broker = new IntegrationBroker({
1127
+ contextName: create(BoundedContextNameSchema, { value: context.name.value }),
1128
+ pairedContextName: create(BoundedContextNameSchema, { value: systemSpec.name.value }),
1129
+ transportFactory: ServerEnvironment.instance().integrationChannelFactory,
1130
+ eventBus,
1131
+ externalEventSchemas,
1132
+ postImported: async (event) => {
1133
+ const imported = clone(EventSchema, event);
1134
+ if (imported.context === undefined)
1135
+ throw new Error("Imported event requires context.");
1136
+ ContextParts.validateImportedTenant(context, imported);
1137
+ imported.context.external = true;
1138
+ await eventBus.post(imported);
1139
+ },
1140
+ });
1141
+ const ready = broker.open();
1142
+ // Synchronous build returns before readiness; retain the failure for the next
1143
+ // observable operation without letting Node report an unhandled rejection.
1144
+ void ready.catch(() => undefined);
1145
+ contextIntegrations.set(context, { broker, ready });
1146
+ },
1147
+ postContextEvent(context, event) {
1148
+ const buses = contextEventBuses.get(context);
1149
+ if (buses === undefined)
1150
+ return Promise.reject(new Error("Context EventBus is unavailable."));
1151
+ if (closingContexts.has(context))
1152
+ return Promise.reject(new Error("server runtime is closed."));
1153
+ return (contextIntegrations.get(context)?.ready ?? Promise.resolve()).then(() => {
1154
+ ContextParts.validateImportedTenant(context, event);
1155
+ return buses[0].post(event);
1156
+ });
1157
+ },
1158
+ closeIntegration(context) {
1159
+ return contextIntegrations.get(context)?.broker.close() ?? Promise.resolve();
1160
+ },
1161
+ publishImported(context, event) {
1162
+ const integration = contextIntegrations.get(context);
1163
+ if (integration === undefined)
1164
+ return Promise.reject(new Error("Context integration is unavailable."));
1165
+ return integration.ready.then(() => integration.broker.publishImported(event));
1166
+ },
1167
+ validateImportedTenant(context, event) {
1168
+ const tenantId = ContextParts.readReplayTenant(event);
1169
+ if (!context.isMultitenant) {
1170
+ if (tenantId !== undefined) {
1171
+ throw new Error(`Single-tenant context "${context.name.value}" does not accept tenantId.`);
1172
+ }
1173
+ return;
1174
+ }
1175
+ if (tenantId === undefined) {
1176
+ throw new Error(`Multitenant context "${context.name.value}" requires tenantId.`);
1177
+ }
1178
+ TenantBoundary.from(tenantId);
1179
+ },
1180
+ integrationReady(context) {
1181
+ return contextIntegrations.get(context)?.ready ?? Promise.resolve();
1182
+ },
1183
+ isInternalEventSchema(schema) {
1184
+ return ((hasOption(schema, internal_type) && getOption(schema, internal_type)) ||
1185
+ (hasOption(schema, SPI_type) && getOption(schema, SPI_type)) ||
1186
+ (hasOption(schema.file, internal_all) && getOption(schema.file, internal_all)));
1187
+ },
1188
+ toTenantMode(multitenant) {
1189
+ return multitenant ? "multitenant" : "single-tenant";
1190
+ },
1191
+ requireSystemPairing(context) {
1192
+ const pairing = contextSystemPairings.get(context);
1193
+ if (pairing === undefined) {
1194
+ throw new TypeError("System pairing requires a built BoundedContext instance.");
1195
+ }
1196
+ return pairing;
1197
+ },
1198
+ requireTenantIndex(context) {
1199
+ const tenantIndex = contextTenantIndexes.get(context);
1200
+ if (tenantIndex === undefined) {
1201
+ throw new TypeError("Tenant index requires a built BoundedContext instance.");
1202
+ }
1203
+ return tenantIndex;
1204
+ },
1205
+ requireRepositoryInstance(repository, operation) {
1206
+ if (!repositoryAccess.hasInstance(repository)) {
1207
+ throw new TypeError(`${operation} requires a Repository instance.`);
1208
+ }
1209
+ },
1210
+ requireEntityClass(entityType, operation) {
1211
+ if (typeof entityType !== "function") {
1212
+ throw new TypeError(`${operation} requires a Repository instance. Use an entity class only with buildAsync().`);
1213
+ }
1214
+ },
1215
+ rejectSyncEntityAssembly(entityTypes) {
1216
+ if (entityTypes.size === 0) {
1217
+ return;
1218
+ }
1219
+ throw new Error("BoundedContextBuilder.build() cannot assemble entity classes from generated metadata. " +
1220
+ "Use buildAsync().");
1221
+ },
1222
+ requireGeneratedRegistryRoot(root) {
1223
+ if (root !== undefined) {
1224
+ return root;
1225
+ }
1226
+ throw new Error("BoundedContextBuilder.buildAsync() requires withGeneratedRegistryRoot(root) " +
1227
+ "when assembling entity classes from generated metadata.");
1228
+ },
1229
+ async trustedGeneratedRegistryModule(root) {
1230
+ const trustedRoot = await ContextParts.canonicalGeneratedRegistryRoot(root);
1231
+ const registryPath = resolve(trustedRoot, generatedRegistryFile);
1232
+ const canonicalRegistryPath = await ContextParts.canonicalReadableRegistryPath(registryPath);
1233
+ if (ContextParts.resolvesOutsideRoot(trustedRoot, canonicalRegistryPath)) {
1234
+ throw new Error(`Generated handler registry module "${canonicalRegistryPath}" must resolve within ` +
1235
+ `the configured generated registry root "${trustedRoot}".`);
1236
+ }
1237
+ return pathToFileURL(canonicalRegistryPath);
1238
+ },
1239
+ async canonicalGeneratedRegistryRoot(root) {
1240
+ const rootPath = ContextParts.generatedRegistryRootPath(root);
1241
+ try {
1242
+ return await realpath(rootPath);
1243
+ }
1244
+ catch (error) {
1245
+ throw new Error(`Generated registry root "${rootPath}" must be an existing readable directory.`, { cause: error });
1246
+ }
1247
+ },
1248
+ generatedRegistryRootPath(root) {
1249
+ if (root instanceof URL) {
1250
+ return ContextParts.fileUrlPath(root, "Generated registry root");
1251
+ }
1252
+ if (ContextParts.isUrlLike(root)) {
1253
+ return ContextParts.fileUrlPath(ContextParts.parseRootUrl(root), "Generated registry root");
1254
+ }
1255
+ return resolve(root);
1256
+ },
1257
+ fileUrlPath(url, label) {
1258
+ if (url.protocol !== "file:") {
1259
+ throw new Error(`${label} "${url.href}" must use the file: URL scheme.`);
1260
+ }
1261
+ if (url.search.length > 0 || url.hash.length > 0) {
1262
+ throw new Error(`${label} "${url.href}" must not include a query or hash.`);
1263
+ }
1264
+ return resolve(fileURLToPath(url));
1265
+ },
1266
+ parseRootUrl(root) {
1267
+ try {
1268
+ return new URL(root);
1269
+ }
1270
+ catch (error) {
1271
+ throw new Error(`Generated registry root "${root}" is not a valid URL.`, { cause: error });
1272
+ }
1273
+ },
1274
+ isUrlLike(value) {
1275
+ return moduleSchemeRe.test(value) && !/^[A-Za-z]:[\\/]/.test(value);
1276
+ },
1277
+ async canonicalReadableRegistryPath(registryPath) {
1278
+ try {
1279
+ await access(registryPath, fsConstants.R_OK);
1280
+ return await realpath(registryPath);
1281
+ }
1282
+ catch (error) {
1283
+ throw new Error(`Generated handler registry module "${registryPath}" must exist and be readable.`, { cause: error });
1284
+ }
1285
+ },
1286
+ resolvesOutsideRoot(canonicalRoot, canonicalPath) {
1287
+ const relativePath = relative(canonicalRoot, canonicalPath);
1288
+ return (relativePath.startsWith("..") ||
1289
+ relativePath === ".." ||
1290
+ relativePath.split(sep).includes("..") ||
1291
+ isAbsolute(relativePath));
1292
+ },
1293
+ generatedRegistryCacheBust(registryKey) {
1294
+ const attempt = generatedRegistryLoadAttempts.get(registryKey) ?? 0;
1295
+ return attempt === 0 ? {} : { cacheBust: `retry-${attempt.toString()}` };
1296
+ },
1297
+ recordGeneratedRegistryFailure(registryKey) {
1298
+ generatedRegistryLoadAttempts.set(registryKey, (generatedRegistryLoadAttempts.get(registryKey) ?? 0) + 1);
1299
+ },
1300
+ ingestGeneratedRegistries(registries) {
1301
+ const registry = new HandlerMetadataRegistry();
1302
+ const ingestor = new HandlerRegistryIngestor();
1303
+ for (const generated of registries) {
1304
+ ingestor.register(generated, registry);
1305
+ }
1306
+ return registry;
1307
+ },
1308
+ createGeneratedRepository(entityType, registries, metadata, options) {
1309
+ const generated = ContextParts.findGeneratedEntity(entityType, registries);
1310
+ const handlers = ContextParts.findGeneratedHandlers(entityType, generated, metadata);
1311
+ const repositoryOptions = Object.assign({
1312
+ entityType,
1313
+ schema: generated.stateSchema,
1314
+ handlers,
1315
+ events: ContextParts.aggregateAssignedEvents(generated),
1316
+ }, options);
1317
+ return new Repository(repositoryOptions);
1318
+ },
1319
+ findGeneratedEntity(entityType, registries) {
1320
+ for (const registry of registries) {
1321
+ const generated = registry.entities.find((entity) => entity.entityType === entityType);
1322
+ if (generated !== undefined) {
1323
+ return generated;
1324
+ }
1325
+ }
1326
+ throw new Error(`Generated handler registry is missing metadata for ${entityType.name}.`);
1327
+ },
1328
+ findGeneratedHandlers(entityType, generated, metadata) {
1329
+ const matches = metadata.findByState(generated.stateSchema.typeName);
1330
+ const handlers = matches.find((candidate) => candidate.entityType === entityType);
1331
+ if (handlers === undefined) {
1332
+ throw new Error(`Generated handler registry is missing metadata for ${entityType.name}.`);
1333
+ }
1334
+ return handlers;
1335
+ },
1336
+ aggregateAssignedEvents(generated) {
1337
+ return ContextParts.uniqueSchemas(generated.handlers.flatMap((handler) => handler.kind === "command-assignment" || handler.kind === "event-reaction"
1338
+ ? handler.emittedSchemas
1339
+ : []));
1340
+ },
1341
+ uniqueSchemas(schemas) {
1342
+ const byTypeName = new Map();
1343
+ for (const schema of schemas) {
1344
+ byTypeName.set(schema.typeName, schema);
1345
+ }
1346
+ return Object.freeze([...byTypeName.values()]);
1347
+ },
1348
+ preflightRepositories(repositories) {
1349
+ const entityTypes = new Set();
1350
+ const stateTypeNames = new Set();
1351
+ for (const repository of repositories) {
1352
+ ContextParts.requireRepositoryInstance(repository, "BoundedContextBuilder.add(repository)");
1353
+ const snapshot = ContextParts.repositorySnapshot(repository);
1354
+ const registration = registeredRepositories.get(repository);
1355
+ if (registration !== undefined) {
1356
+ throw new Error(`Repository for "${snapshot.stateFullTypeName}" is already registered with Bounded Context ` +
1357
+ `"${registration.name.value}".`);
1358
+ }
1359
+ if (entityTypes.has(snapshot.entityType)) {
1360
+ throw new Error(`Repository entity type "${snapshot.entityType.name}" is already registered.`);
1361
+ }
1362
+ entityTypes.add(snapshot.entityType);
1363
+ if (stateTypeNames.has(snapshot.stateFullTypeName)) {
1364
+ throw new Error(`Repository state type "${snapshot.stateFullTypeName}" is already registered.`);
1365
+ }
1366
+ stateTypeNames.add(snapshot.stateFullTypeName);
1367
+ }
1368
+ ContextParts.rejectStateCycles(repositories);
1369
+ },
1370
+ rejectStateCycles(repositories) {
1371
+ const dependencies = new Map();
1372
+ for (const repository of repositories) {
1373
+ const stateType = ContextParts.repositorySnapshot(repository).stateFullTypeName;
1374
+ dependencies.set(stateType, repositoryAccess.stateSubscriptionTypes(repository));
1375
+ }
1376
+ const visited = new Set();
1377
+ const visiting = new Set();
1378
+ const path = [];
1379
+ const visit = (stateType) => {
1380
+ if (visited.has(stateType))
1381
+ return;
1382
+ if (visiting.has(stateType)) {
1383
+ const start = path.indexOf(stateType);
1384
+ const cycle = [...path.slice(start), stateType];
1385
+ throw new Error(`Projection state subscriptions form a feedback cycle: ${cycle.join(" -> ")}.`);
1386
+ }
1387
+ visiting.add(stateType);
1388
+ path.push(stateType);
1389
+ for (const dependency of dependencies.get(stateType) ?? []) {
1390
+ if (dependencies.has(dependency))
1391
+ visit(dependency);
1392
+ }
1393
+ path.pop();
1394
+ visiting.delete(stateType);
1395
+ visited.add(stateType);
1396
+ };
1397
+ for (const stateType of dependencies.keys())
1398
+ visit(stateType);
1399
+ },
1400
+ repositoryCommandDispatchers(repositories) {
1401
+ return repositories.flatMap((repository) => {
1402
+ const dispatcher = repositoryAccess.commandDispatcher(repository);
1403
+ return dispatcher === undefined ? [] : [dispatcher];
1404
+ });
1405
+ },
1406
+ repositoryEventDispatchers(repositories) {
1407
+ return repositories.flatMap((repository) => {
1408
+ const dispatcher = repositoryAccess.eventDispatcher(repository);
1409
+ return dispatcher === undefined ? [] : [dispatcher];
1410
+ });
1411
+ },
1412
+ repositorySystemEventDispatchers(repositories) {
1413
+ return repositories.flatMap((repository) => {
1414
+ const dispatcher = repositoryAccess.systemEventDispatcher(repository);
1415
+ return dispatcher === undefined ? [] : [dispatcher];
1416
+ });
1417
+ },
1418
+ domainEventDispatchers(dispatchers) {
1419
+ return Object.freeze(dispatchers.filter((dispatcher) => !ContextParts.isSystemEventDispatcher(dispatcher)));
1420
+ },
1421
+ systemEventDispatchers(dispatchers) {
1422
+ return Object.freeze(dispatchers.filter((dispatcher) => ContextParts.isSystemEventDispatcher(dispatcher)));
1423
+ },
1424
+ isSystemEventDispatcher(dispatcher) {
1425
+ const schemas = [...dispatcher.messageSchemas()];
1426
+ const systemSchemas = schemas.filter((schema) => schema.typeName.startsWith("spine.system."));
1427
+ if (systemSchemas.length > 0 && systemSchemas.length !== schemas.length) {
1428
+ throw new Error("An EventDispatcher cannot mix domain and system event schemas.");
1429
+ }
1430
+ return systemSchemas.length > 0;
1431
+ },
1432
+ repositoryProducedEventSchemas(repositories) {
1433
+ const schemas = new Map();
1434
+ for (const repository of repositories) {
1435
+ for (const schema of repositoryAccess.producedEventSchemas(repository)) {
1436
+ schemas.set(TypeUrls.derive(schema), schema);
1437
+ }
1438
+ }
1439
+ return Object.freeze([...schemas.values()]);
1440
+ },
1441
+ cleanupFailedContext(context, tenantIndex) {
1442
+ ContextParts.clearContextMetadata(context);
1443
+ tenantIndex.close();
1444
+ },
1445
+ clearContextMetadata(context) {
1446
+ const buses = contextEventBuses.get(context);
1447
+ if (buses !== undefined) {
1448
+ eventBusAccess.clearLogger(buses[0]);
1449
+ eventBusAccess.clearLogger(buses[1]);
1450
+ }
1451
+ const runtime = contextSubscriptionRuntimes.get(context);
1452
+ if (runtime !== undefined)
1453
+ subscriptionRuntimeAccess.clearLogger(runtime);
1454
+ contextDispatchFailureRecorders.delete(context);
1455
+ contextLoggers.delete(context);
1456
+ contextSystemPairings.delete(context);
1457
+ contextTenantIndexes.delete(context);
1458
+ contextStorageFactories.delete(context);
1459
+ contextDeliveryDescriptors.delete(context);
1460
+ contextSubscriptionRuntimes.delete(context);
1461
+ contextIntegrations.delete(context);
1462
+ eventSubscribers.delete(context);
1463
+ systemEventPosters.delete(context);
1464
+ },
1465
+ createDeliveryDescriptor(context, storageFactory, tenantIndex, entityInbox, projections, readiness) {
1466
+ return Object.freeze({
1467
+ storageFactory,
1468
+ async startupScopes() {
1469
+ if (tenantIndex.tenantMode === "single-tenant") {
1470
+ return Object.freeze([Object.freeze({})]);
1471
+ }
1472
+ return Object.freeze((await tenantIndex.all()).map((tenantId) => Object.freeze({ tenantId })));
1473
+ },
1474
+ storageContext(scope) {
1475
+ if (context.tenantMode === "single-tenant") {
1476
+ if (scope.tenantId !== undefined) {
1477
+ throw new Error(`Single-tenant context "${context.name.value}" does not accept tenantId.`);
1478
+ }
1479
+ return Object.freeze({ name: context.name.value, multitenant: false });
1480
+ }
1481
+ const tenantId = scope.tenantId;
1482
+ if (tenantId === undefined) {
1483
+ throw new Error(`Multitenant context "${context.name.value}" requires tenantId.`);
1484
+ }
1485
+ return Object.freeze({
1486
+ name: context.name.value,
1487
+ multitenant: true,
1488
+ tenantId: TenantBoundary.from(tenantId).tenantId,
1489
+ });
1490
+ },
1491
+ endpoints() {
1492
+ return Object.freeze([...entityInbox.endpoints(), ...projections.endpoints()]);
1493
+ },
1494
+ replay(message, tenantId) {
1495
+ return message.label === "UPDATE_SUBSCRIBER"
1496
+ ? projections.replay(message, tenantId)
1497
+ : entityInbox.replay(message, tenantId);
1498
+ },
1499
+ onReady(onReady) {
1500
+ return readiness.onReady(onReady);
1501
+ },
1502
+ transition(scopes, onReady, options) {
1503
+ return readiness.transition(scopes, onReady, options);
1504
+ },
1505
+ });
1506
+ },
1507
+ closePreparedRepositories(preparedRepositories) {
1508
+ const errors = [];
1509
+ for (const preparedRepository of preparedRepositories) {
1510
+ try {
1511
+ preparedRepository.close();
1512
+ }
1513
+ catch (error) {
1514
+ errors.push(error);
1515
+ }
1516
+ }
1517
+ return errors;
1518
+ },
1519
+ async closeContextPart(close, errors) {
1520
+ try {
1521
+ await close();
1522
+ }
1523
+ catch (error) {
1524
+ ContextParts.collectCloseError(error, errors);
1525
+ }
1526
+ },
1527
+ async drainContextBuses(commandBus, eventBus) {
1528
+ let observedCommandWork = -1;
1529
+ let observedEventWork = -1;
1530
+ do {
1531
+ observedCommandWork = commandBusAccess.acceptedWorkCount(commandBus);
1532
+ observedEventWork = eventBusAccess.acceptedWorkCount(eventBus);
1533
+ await commandBusAccess.drain(commandBus);
1534
+ await eventBusAccess.drain(eventBus);
1535
+ } while (commandBusAccess.acceptedWorkCount(commandBus) !== observedCommandWork ||
1536
+ eventBusAccess.acceptedWorkCount(eventBus) !== observedEventWork);
1537
+ },
1538
+ collectCloseError(error, errors) {
1539
+ if (error instanceof AggregateError) {
1540
+ const causes = error.errors;
1541
+ for (const cause of causes) {
1542
+ errors.push(cause);
1543
+ }
1544
+ return;
1545
+ }
1546
+ errors.push(error);
1547
+ },
1548
+ prepareRepositoryForContext(repository, registration) {
1549
+ ContextParts.requireRepositoryInstance(repository, "BoundedContext repository registration");
1550
+ const snapshot = ContextParts.repositorySnapshot(repository);
1551
+ ContextParts.rejectRegisteredRepository(repository);
1552
+ repositoryAccess.bindRuntime(repository, {
1553
+ context: registration.storageContext,
1554
+ storageFactory: registration.storageFactory,
1555
+ stand: registration.stand,
1556
+ signalMetadata: new SignalMetadata(),
1557
+ entityInbox: registration.entityInbox,
1558
+ projectionInbox: registration.projectionInbox,
1559
+ dispatchStored: registration.dispatchStored,
1560
+ dispatchStoredFollowUp: registration.dispatchStoredFollowUp,
1561
+ postEventFollowUp: registration.postEventFollowUp,
1562
+ registerEventSchema: registration.registerEventSchema,
1563
+ registerSystemEventSchema: registration.registerSystemEventSchema,
1564
+ postSystemFollowUp: registration.postSystemFollowUp,
1565
+ onPostCommand: registration.onPostCommand,
1566
+ recordDispatchFailure: registration.recordDispatchFailure,
1567
+ });
1568
+ const entityInboxTarget = repositoryAccess.entityInboxTarget(repository);
1569
+ const projectionInboxTarget = repositoryAccess.projectionInboxTarget(repository);
1570
+ return {
1571
+ repository,
1572
+ snapshot,
1573
+ ...(entityInboxTarget === undefined ? {} : { entityInboxTarget }),
1574
+ ...(projectionInboxTarget === undefined ? {} : { projectionInboxTarget }),
1575
+ commit: () => {
1576
+ registeredRepositories.set(repository, { name: registration.name });
1577
+ },
1578
+ close: () => {
1579
+ repositoryAccess.clearRuntime(repository);
1580
+ },
1581
+ };
1582
+ },
1583
+ repositorySnapshot(repository) {
1584
+ const snapshot = repositoryAccess.snapshot(repository);
1585
+ return Object.freeze({
1586
+ entityType: snapshot.entityType,
1587
+ entityFamily: snapshot.entityFamily,
1588
+ stateSchema: snapshot.stateSchema,
1589
+ metadata: snapshot.metadata,
1590
+ stateFullTypeName: snapshot.stateFullTypeName,
1591
+ idField: snapshot.idField,
1592
+ snapshot,
1593
+ });
1594
+ },
1595
+ rejectRegisteredRepository(repository) {
1596
+ const snapshot = ContextParts.repositorySnapshot(repository);
1597
+ const registration = registeredRepositories.get(repository);
1598
+ if (registration !== undefined) {
1599
+ throw new Error(`Repository for "${snapshot.stateFullTypeName}" is already registered with Bounded Context ` +
1600
+ `"${registration.name.value}".`);
1601
+ }
1602
+ },
1603
+ repositoryColumns(snapshot) {
1604
+ return snapshot.metadata.columns.map((field) => new RecordColumn(field.name, ColumnTypes.fromField(field.descriptor), (record) => ContextParts.readRecordField(record, field.localName)));
1605
+ },
1606
+ createRepositoryView(snapshot) {
1607
+ return Object.freeze({
1608
+ entityType: snapshot.entityType,
1609
+ entityFamily: snapshot.entityFamily,
1610
+ stateSchema: snapshot.stateSchema,
1611
+ metadata: snapshot.metadata,
1612
+ stateFullTypeName: snapshot.stateFullTypeName,
1613
+ idField: snapshot.idField,
1614
+ snapshot: snapshot.snapshot,
1615
+ });
1616
+ },
1617
+ cloneDispatchFailure(failure) {
1618
+ return Object.freeze({
1619
+ event: clone(EventSchema, failure.event),
1620
+ error: ContextParts.cloneDispatchError(failure.error),
1621
+ });
1622
+ },
1623
+ snapshotDispatchError(error) {
1624
+ if (error instanceof Error) {
1625
+ const snapshot = {
1626
+ name: ContextParts.boundedErrorString(error.name, dispatchErrorMessageLimit) || "Error",
1627
+ message: ContextParts.boundedErrorString(error.message, dispatchErrorMessageLimit),
1628
+ ...(typeof error.stack === "string"
1629
+ ? { stack: ContextParts.boundedErrorString(error.stack, dispatchErrorStackLimit) }
1630
+ : {}),
1631
+ };
1632
+ return Object.freeze(snapshot);
1633
+ }
1634
+ return Object.freeze({
1635
+ name: "NonErrorThrow",
1636
+ message: ContextParts.boundedErrorString(String(error), dispatchErrorMessageLimit),
1637
+ });
1638
+ },
1639
+ cloneDispatchError(error) {
1640
+ return Object.freeze({
1641
+ name: error.name,
1642
+ message: error.message,
1643
+ ...(error.stack === undefined ? {} : { stack: error.stack }),
1644
+ });
1645
+ },
1646
+ projectionDispatchers(repositories) {
1647
+ const projections = [];
1648
+ for (const repository of repositories) {
1649
+ const snapshot = repositoryAccess.snapshot(repository);
1650
+ const dispatcher = repositoryAccess.eventDispatcher(repository);
1651
+ if (snapshot.entityFamily !== "projection" || dispatcher === undefined) {
1652
+ continue;
1653
+ }
1654
+ projections.push(Object.freeze({
1655
+ repository,
1656
+ dispatcher,
1657
+ eventTypeUrls: new Set(dispatcher.messageSchemas().map((schema) => TypeUrls.derive(schema))),
1658
+ schema: snapshot.stateSchema,
1659
+ typeUrl: TypeUrls.derive(snapshot.stateSchema),
1660
+ }));
1661
+ }
1662
+ return Object.freeze(projections);
1663
+ },
1664
+ projectionStateClearTargets(projections) {
1665
+ const unique = new Map();
1666
+ for (const projection of projections) {
1667
+ if (!unique.has(projection.typeUrl)) {
1668
+ unique.set(projection.typeUrl, Object.freeze({
1669
+ schema: projection.schema,
1670
+ typeUrl: projection.typeUrl,
1671
+ }));
1672
+ }
1673
+ }
1674
+ return Object.freeze([...unique.values()]);
1675
+ },
1676
+ async readStoredEvents(context, storageFactory) {
1677
+ const eventStore = new EventStore(context, storageFactory);
1678
+ try {
1679
+ return await eventStore.read({
1680
+ sort: [
1681
+ { field: "timestamp", direction: "asc" },
1682
+ { field: "context.producerId.value", direction: "asc" },
1683
+ { field: "context.version.number", direction: "asc" },
1684
+ { field: "id.value", direction: "asc" },
1685
+ ],
1686
+ });
1687
+ }
1688
+ finally {
1689
+ eventStore.close();
1690
+ }
1691
+ },
1692
+ catchUpStandOptions(context) {
1693
+ if (!context.multitenant) {
1694
+ return {};
1695
+ }
1696
+ return Object.freeze({ tenantId: clone(TenantIdSchema, context.tenantId) });
1697
+ },
1698
+ validateReplayTenant(context, event) {
1699
+ if (!context.multitenant) {
1700
+ return;
1701
+ }
1702
+ const expectedTenantId = context.tenantId;
1703
+ const envelopeTenantId = ContextParts.readReplayTenant(event);
1704
+ if (envelopeTenantId === undefined) {
1705
+ throw new Error("Read-side catch-up requires stored event envelope tenant.");
1706
+ }
1707
+ if (TenantBoundary.from(envelopeTenantId).key !== TenantBoundary.from(expectedTenantId).key) {
1708
+ throw new Error("Read-side catch-up stored event envelope tenant does not match.");
1709
+ }
1710
+ },
1711
+ readReplayTenant(event) {
1712
+ switch (event.context?.origin.case) {
1713
+ case "importContext":
1714
+ return ContextParts.tenantIdValue(event.context.origin.value.tenantId);
1715
+ case "pastMessage":
1716
+ return ContextParts.tenantIdValue(event.context.origin.value.actorContext?.tenantId);
1717
+ default:
1718
+ return undefined;
1719
+ }
1720
+ },
1721
+ tenantIdValue(tenantId) {
1722
+ return tenantId === undefined ? undefined : clone(TenantIdSchema, tenantId);
1723
+ },
1724
+ async dispatchStoredProjectionEvent(projections, event, rebuild) {
1725
+ const typeUrl = event.message?.typeUrl;
1726
+ if (typeUrl === undefined || typeUrl === "") {
1727
+ throw new Error("Read-side catch-up requires stored event.message.typeUrl.");
1728
+ }
1729
+ const matching = projections.filter((projection) => projection.eventTypeUrls.has(typeUrl));
1730
+ for (const projection of matching) {
1731
+ await projection.dispatcher.accept?.(clone(EventSchema, event));
1732
+ }
1733
+ for (const projection of matching) {
1734
+ await repositoryAccess.dispatchProjectionDirect(projection.repository, clone(EventSchema, event), rebuild);
1735
+ }
1736
+ return matching.length > 0 ? 1 : 0;
1737
+ },
1738
+ catchUpReplayError(event, cause) {
1739
+ return new CatchUpReplayError(event.id?.value ?? "(missing)", ContextParts.catchUpReplayDetail(cause));
1740
+ },
1741
+ boundedErrorString(value, limit) {
1742
+ return value.length <= limit ? value : `${value.slice(0, limit - 3)}...`;
1743
+ },
1744
+ catchUpReplayDetail(error) {
1745
+ if (error instanceof Error) {
1746
+ return Object.freeze({
1747
+ name: ContextParts.boundedErrorString(error.name, dispatchErrorMessageLimit) || "Error",
1748
+ message: ContextParts.boundedErrorString(error.message, dispatchErrorMessageLimit),
1749
+ });
1750
+ }
1751
+ return Object.freeze({
1752
+ name: "NonErrorThrow",
1753
+ message: ContextParts.boundedErrorString(String(error), dispatchErrorMessageLimit),
1754
+ });
1755
+ },
1756
+ readRecordId(record, snapshot) {
1757
+ const value = ContextParts.readRecordField(record, snapshot.idField.localName);
1758
+ if (value === undefined || value === null) {
1759
+ throw new Error(`Repository state "${snapshot.stateFullTypeName}" requires ID field "${snapshot.idField.name}".`);
1760
+ }
1761
+ return value;
1762
+ },
1763
+ readRecordField(record, localName) {
1764
+ return record[localName];
1765
+ },
1766
+ });
1767
+ /**
1768
+ * Exposes broker-only operations for the owning integration package.
1769
+ *
1770
+ * @internal
1771
+ */
1772
+ export const boundedContextIntegrationAccess = Object.freeze({
1773
+ publishImported(context, event) {
1774
+ return ContextParts.publishImported(context, event);
1775
+ },
1776
+ ready(context) {
1777
+ return ContextParts.integrationReady(context);
1778
+ },
1779
+ });
1780
+ //# sourceMappingURL=bounded-context.js.map