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

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 (378) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +209 -0
  3. package/REFERENCE.md +760 -0
  4. package/dist/browser/browser-server.d.ts +95 -0
  5. package/dist/browser/browser-server.d.ts.map +1 -0
  6. package/dist/browser/browser-server.js +719 -0
  7. package/dist/browser/browser-server.js.map +1 -0
  8. package/dist/browser/durable-subscription-bindings.d.ts +122 -0
  9. package/dist/browser/durable-subscription-bindings.d.ts.map +1 -0
  10. package/dist/browser/durable-subscription-bindings.js +524 -0
  11. package/dist/browser/durable-subscription-bindings.js.map +1 -0
  12. package/dist/browser/index.d.ts +12 -0
  13. package/dist/browser/index.d.ts.map +1 -0
  14. package/dist/browser/index.js +24 -0
  15. package/dist/browser/index.js.map +1 -0
  16. package/dist/browser/options.d.ts +183 -0
  17. package/dist/browser/options.d.ts.map +1 -0
  18. package/dist/browser/options.js +15 -0
  19. package/dist/browser/options.js.map +1 -0
  20. package/dist/bus/command-bus.d.ts +63 -0
  21. package/dist/bus/command-bus.d.ts.map +1 -0
  22. package/dist/bus/command-bus.js +236 -0
  23. package/dist/bus/command-bus.js.map +1 -0
  24. package/dist/bus/command-dispatcher-registry.d.ts +33 -0
  25. package/dist/bus/command-dispatcher-registry.d.ts.map +1 -0
  26. package/dist/bus/command-dispatcher-registry.js +75 -0
  27. package/dist/bus/command-dispatcher-registry.js.map +1 -0
  28. package/dist/bus/command-dispatcher.d.ts +25 -0
  29. package/dist/bus/command-dispatcher.d.ts.map +1 -0
  30. package/dist/bus/command-dispatcher.js +15 -0
  31. package/dist/bus/command-dispatcher.js.map +1 -0
  32. package/dist/bus/command-errors.d.ts +23 -0
  33. package/dist/bus/command-errors.d.ts.map +1 -0
  34. package/dist/bus/command-errors.js +52 -0
  35. package/dist/bus/command-errors.js.map +1 -0
  36. package/dist/bus/event-bus.d.ts +113 -0
  37. package/dist/bus/event-bus.d.ts.map +1 -0
  38. package/dist/bus/event-bus.js +518 -0
  39. package/dist/bus/event-bus.js.map +1 -0
  40. package/dist/bus/event-dispatcher-origin-schemas.d.ts +17 -0
  41. package/dist/bus/event-dispatcher-origin-schemas.d.ts.map +1 -0
  42. package/dist/bus/event-dispatcher-origin-schemas.js +32 -0
  43. package/dist/bus/event-dispatcher-origin-schemas.js.map +1 -0
  44. package/dist/bus/event-dispatcher-registry.d.ts +48 -0
  45. package/dist/bus/event-dispatcher-registry.d.ts.map +1 -0
  46. package/dist/bus/event-dispatcher-registry.js +176 -0
  47. package/dist/bus/event-dispatcher-registry.js.map +1 -0
  48. package/dist/bus/event-dispatcher.d.ts +48 -0
  49. package/dist/bus/event-dispatcher.d.ts.map +1 -0
  50. package/dist/bus/event-dispatcher.js +15 -0
  51. package/dist/bus/event-dispatcher.js.map +1 -0
  52. package/dist/context/bounded-context.d.ts +592 -0
  53. package/dist/context/bounded-context.d.ts.map +1 -0
  54. package/dist/context/bounded-context.js +1897 -0
  55. package/dist/context/bounded-context.js.map +1 -0
  56. package/dist/context/entity-inbox.d.ts +63 -0
  57. package/dist/context/entity-inbox.d.ts.map +1 -0
  58. package/dist/context/entity-inbox.js +387 -0
  59. package/dist/context/entity-inbox.js.map +1 -0
  60. package/dist/context/local-inbox-handoff.d.ts +248 -0
  61. package/dist/context/local-inbox-handoff.d.ts.map +1 -0
  62. package/dist/context/local-inbox-handoff.js +340 -0
  63. package/dist/context/local-inbox-handoff.js.map +1 -0
  64. package/dist/context/projection-handoff.d.ts +46 -0
  65. package/dist/context/projection-handoff.d.ts.map +1 -0
  66. package/dist/context/projection-handoff.js +177 -0
  67. package/dist/context/projection-handoff.js.map +1 -0
  68. package/dist/context/tenant-index.d.ts +41 -0
  69. package/dist/context/tenant-index.d.ts.map +1 -0
  70. package/dist/context/tenant-index.js +93 -0
  71. package/dist/context/tenant-index.js.map +1 -0
  72. package/dist/delivery/conditional-pickup.d.ts +25 -0
  73. package/dist/delivery/conditional-pickup.d.ts.map +1 -0
  74. package/dist/delivery/conditional-pickup.js +26 -0
  75. package/dist/delivery/conditional-pickup.js.map +1 -0
  76. package/dist/delivery/delivery-builder.d.ts +216 -0
  77. package/dist/delivery/delivery-builder.d.ts.map +1 -0
  78. package/dist/delivery/delivery-builder.js +323 -0
  79. package/dist/delivery/delivery-builder.js.map +1 -0
  80. package/dist/delivery/delivery-lease.d.ts +15 -0
  81. package/dist/delivery/delivery-lease.d.ts.map +1 -0
  82. package/dist/delivery/delivery-lease.js +44 -0
  83. package/dist/delivery/delivery-lease.js.map +1 -0
  84. package/dist/delivery/delivery-loop.d.ts +113 -0
  85. package/dist/delivery/delivery-loop.d.ts.map +1 -0
  86. package/dist/delivery/delivery-loop.js +119 -0
  87. package/dist/delivery/delivery-loop.js.map +1 -0
  88. package/dist/delivery/delivery-monitor.d.ts +165 -0
  89. package/dist/delivery/delivery-monitor.d.ts.map +1 -0
  90. package/dist/delivery/delivery-monitor.js +168 -0
  91. package/dist/delivery/delivery-monitor.js.map +1 -0
  92. package/dist/delivery/delivery-ports.d.ts +151 -0
  93. package/dist/delivery/delivery-ports.d.ts.map +1 -0
  94. package/dist/delivery/delivery-ports.js +15 -0
  95. package/dist/delivery/delivery-ports.js.map +1 -0
  96. package/dist/delivery/delivery-run-control.d.ts +50 -0
  97. package/dist/delivery/delivery-run-control.d.ts.map +1 -0
  98. package/dist/delivery/delivery-run-control.js +66 -0
  99. package/dist/delivery/delivery-run-control.js.map +1 -0
  100. package/dist/delivery/delivery-run-coordinator.d.ts +218 -0
  101. package/dist/delivery/delivery-run-coordinator.d.ts.map +1 -0
  102. package/dist/delivery/delivery-run-coordinator.js +609 -0
  103. package/dist/delivery/delivery-run-coordinator.js.map +1 -0
  104. package/dist/delivery/delivery-storage-error.d.ts +13 -0
  105. package/dist/delivery/delivery-storage-error.d.ts.map +1 -0
  106. package/dist/delivery/delivery-storage-error.js +30 -0
  107. package/dist/delivery/delivery-storage-error.js.map +1 -0
  108. package/dist/delivery/delivery-supervisor.d.ts +175 -0
  109. package/dist/delivery/delivery-supervisor.d.ts.map +1 -0
  110. package/dist/delivery/delivery-supervisor.js +469 -0
  111. package/dist/delivery/delivery-supervisor.js.map +1 -0
  112. package/dist/delivery/delivery-worker.d.ts +180 -0
  113. package/dist/delivery/delivery-worker.d.ts.map +1 -0
  114. package/dist/delivery/delivery-worker.js +232 -0
  115. package/dist/delivery/delivery-worker.js.map +1 -0
  116. package/dist/delivery/delivery.d.ts +236 -0
  117. package/dist/delivery/delivery.d.ts.map +1 -0
  118. package/dist/delivery/delivery.js +539 -0
  119. package/dist/delivery/delivery.js.map +1 -0
  120. package/dist/delivery/inbox-records.d.ts +19 -0
  121. package/dist/delivery/inbox-records.d.ts.map +1 -0
  122. package/dist/delivery/inbox-records.js +291 -0
  123. package/dist/delivery/inbox-records.js.map +1 -0
  124. package/dist/delivery/inbox-storage.d.ts +83 -0
  125. package/dist/delivery/inbox-storage.d.ts.map +1 -0
  126. package/dist/delivery/inbox-storage.js +336 -0
  127. package/dist/delivery/inbox-storage.js.map +1 -0
  128. package/dist/delivery/inbox.d.ts +272 -0
  129. package/dist/delivery/inbox.d.ts.map +1 -0
  130. package/dist/delivery/inbox.js +183 -0
  131. package/dist/delivery/inbox.js.map +1 -0
  132. package/dist/delivery/parked-delivery-obligations.d.ts +182 -0
  133. package/dist/delivery/parked-delivery-obligations.d.ts.map +1 -0
  134. package/dist/delivery/parked-delivery-obligations.js +390 -0
  135. package/dist/delivery/parked-delivery-obligations.js.map +1 -0
  136. package/dist/delivery/shard-index.d.ts +41 -0
  137. package/dist/delivery/shard-index.d.ts.map +1 -0
  138. package/dist/delivery/shard-index.js +67 -0
  139. package/dist/delivery/shard-index.js.map +1 -0
  140. package/dist/delivery/sharded-work-registry.d.ts +114 -0
  141. package/dist/delivery/sharded-work-registry.d.ts.map +1 -0
  142. package/dist/delivery/sharded-work-registry.js +361 -0
  143. package/dist/delivery/sharded-work-registry.js.map +1 -0
  144. package/dist/entity/entity-metadata.d.ts +173 -0
  145. package/dist/entity/entity-metadata.d.ts.map +1 -0
  146. package/dist/entity/entity-metadata.js +187 -0
  147. package/dist/entity/entity-metadata.js.map +1 -0
  148. package/dist/entity/entity-storage-descriptor.d.ts +64 -0
  149. package/dist/entity/entity-storage-descriptor.d.ts.map +1 -0
  150. package/dist/entity/entity-storage-descriptor.js +227 -0
  151. package/dist/entity/entity-storage-descriptor.js.map +1 -0
  152. package/dist/entity/entity-transaction.d.ts +372 -0
  153. package/dist/entity/entity-transaction.d.ts.map +1 -0
  154. package/dist/entity/entity-transaction.js +413 -0
  155. package/dist/entity/entity-transaction.js.map +1 -0
  156. package/dist/entity/entity-transition-validation.d.ts +47 -0
  157. package/dist/entity/entity-transition-validation.d.ts.map +1 -0
  158. package/dist/entity/entity-transition-validation.js +327 -0
  159. package/dist/entity/entity-transition-validation.js.map +1 -0
  160. package/dist/entity/entity.d.ts +605 -0
  161. package/dist/entity/entity.d.ts.map +1 -0
  162. package/dist/entity/entity.js +981 -0
  163. package/dist/entity/entity.js.map +1 -0
  164. package/dist/entity/implicit-required-id.d.ts +15 -0
  165. package/dist/entity/implicit-required-id.d.ts.map +1 -0
  166. package/dist/entity/implicit-required-id.js +123 -0
  167. package/dist/entity/implicit-required-id.js.map +1 -0
  168. package/dist/entity/spec-scanner.d.ts +14 -0
  169. package/dist/entity/spec-scanner.d.ts.map +1 -0
  170. package/dist/entity/spec-scanner.js +37 -0
  171. package/dist/entity/spec-scanner.js.map +1 -0
  172. package/dist/handler/command-registration-readiness.d.ts +99 -0
  173. package/dist/handler/command-registration-readiness.d.ts.map +1 -0
  174. package/dist/handler/command-registration-readiness.js +120 -0
  175. package/dist/handler/command-registration-readiness.js.map +1 -0
  176. package/dist/handler/declared-rejections.d.ts +11 -0
  177. package/dist/handler/declared-rejections.d.ts.map +1 -0
  178. package/dist/handler/declared-rejections.js +34 -0
  179. package/dist/handler/declared-rejections.js.map +1 -0
  180. package/dist/handler/event-handler-filter.d.ts +43 -0
  181. package/dist/handler/event-handler-filter.d.ts.map +1 -0
  182. package/dist/handler/event-handler-filter.js +128 -0
  183. package/dist/handler/event-handler-filter.js.map +1 -0
  184. package/dist/handler/event-registration-readiness.d.ts +196 -0
  185. package/dist/handler/event-registration-readiness.d.ts.map +1 -0
  186. package/dist/handler/event-registration-readiness.js +187 -0
  187. package/dist/handler/event-registration-readiness.js.map +1 -0
  188. package/dist/handler/external.d.ts +10 -0
  189. package/dist/handler/external.d.ts.map +1 -0
  190. package/dist/handler/external.js +15 -0
  191. package/dist/handler/external.js.map +1 -0
  192. package/dist/handler/generated-handler-registry.d.ts +185 -0
  193. package/dist/handler/generated-handler-registry.d.ts.map +1 -0
  194. package/dist/handler/generated-handler-registry.js +439 -0
  195. package/dist/handler/generated-handler-registry.js.map +1 -0
  196. package/dist/handler/generated-registry-discovery.d.ts +94 -0
  197. package/dist/handler/generated-registry-discovery.d.ts.map +1 -0
  198. package/dist/handler/generated-registry-discovery.js +226 -0
  199. package/dist/handler/generated-registry-discovery.js.map +1 -0
  200. package/dist/handler/handler-decorators.d.ts +151 -0
  201. package/dist/handler/handler-decorators.d.ts.map +1 -0
  202. package/dist/handler/handler-decorators.js +275 -0
  203. package/dist/handler/handler-decorators.js.map +1 -0
  204. package/dist/handler/handler-metadata.d.ts +553 -0
  205. package/dist/handler/handler-metadata.d.ts.map +1 -0
  206. package/dist/handler/handler-metadata.js +437 -0
  207. package/dist/handler/handler-metadata.js.map +1 -0
  208. package/dist/handler/registration-readiness-metadata.d.ts +68 -0
  209. package/dist/handler/registration-readiness-metadata.d.ts.map +1 -0
  210. package/dist/handler/registration-readiness-metadata.js +171 -0
  211. package/dist/handler/registration-readiness-metadata.js.map +1 -0
  212. package/dist/handler/rejection-source.d.ts +11 -0
  213. package/dist/handler/rejection-source.d.ts.map +1 -0
  214. package/dist/handler/rejection-source.js +25 -0
  215. package/dist/handler/rejection-source.js.map +1 -0
  216. package/dist/handler/standalone.d.ts +25 -0
  217. package/dist/handler/standalone.d.ts.map +1 -0
  218. package/dist/handler/standalone.js +38 -0
  219. package/dist/handler/standalone.js.map +1 -0
  220. package/dist/index.d.ts +46 -0
  221. package/dist/index.d.ts.map +1 -0
  222. package/dist/index.js +54 -0
  223. package/dist/index.js.map +1 -0
  224. package/dist/integration/external-messages.d.ts +49 -0
  225. package/dist/integration/external-messages.d.ts.map +1 -0
  226. package/dist/integration/external-messages.js +112 -0
  227. package/dist/integration/external-messages.js.map +1 -0
  228. package/dist/integration/integration-broker.d.ts +74 -0
  229. package/dist/integration/integration-broker.d.ts.map +1 -0
  230. package/dist/integration/integration-broker.js +444 -0
  231. package/dist/integration/integration-broker.js.map +1 -0
  232. package/dist/integration/third-party-context.d.ts +56 -0
  233. package/dist/integration/third-party-context.d.ts.map +1 -0
  234. package/dist/integration/third-party-context.js +130 -0
  235. package/dist/integration/third-party-context.js.map +1 -0
  236. package/dist/repository/command-errors.d.ts +25 -0
  237. package/dist/repository/command-errors.d.ts.map +1 -0
  238. package/dist/repository/command-errors.js +43 -0
  239. package/dist/repository/command-errors.js.map +1 -0
  240. package/dist/repository/command-routing.d.ts +72 -0
  241. package/dist/repository/command-routing.d.ts.map +1 -0
  242. package/dist/repository/command-routing.js +98 -0
  243. package/dist/repository/command-routing.js.map +1 -0
  244. package/dist/repository/commit-fence.d.ts +22 -0
  245. package/dist/repository/commit-fence.d.ts.map +1 -0
  246. package/dist/repository/commit-fence.js +37 -0
  247. package/dist/repository/commit-fence.js.map +1 -0
  248. package/dist/repository/event-routing.d.ts +71 -0
  249. package/dist/repository/event-routing.d.ts.map +1 -0
  250. package/dist/repository/event-routing.js +96 -0
  251. package/dist/repository/event-routing.js.map +1 -0
  252. package/dist/repository/primitive-id.d.ts +75 -0
  253. package/dist/repository/primitive-id.d.ts.map +1 -0
  254. package/dist/repository/primitive-id.js +85 -0
  255. package/dist/repository/primitive-id.js.map +1 -0
  256. package/dist/repository/repository.d.ts +667 -0
  257. package/dist/repository/repository.d.ts.map +1 -0
  258. package/dist/repository/repository.js +3510 -0
  259. package/dist/repository/repository.js.map +1 -0
  260. package/dist/repository/routing-declarations.d.ts +75 -0
  261. package/dist/repository/routing-declarations.d.ts.map +1 -0
  262. package/dist/repository/routing-declarations.js +101 -0
  263. package/dist/repository/routing-declarations.js.map +1 -0
  264. package/dist/repository/state-update-routing.d.ts +66 -0
  265. package/dist/repository/state-update-routing.d.ts.map +1 -0
  266. package/dist/repository/state-update-routing.js +91 -0
  267. package/dist/repository/state-update-routing.js.map +1 -0
  268. package/dist/runtime/runtime.d.ts +156 -0
  269. package/dist/runtime/runtime.d.ts.map +1 -0
  270. package/dist/runtime/runtime.js +215 -0
  271. package/dist/runtime/runtime.js.map +1 -0
  272. package/dist/runtime/signal-intake.d.ts +84 -0
  273. package/dist/runtime/signal-intake.d.ts.map +1 -0
  274. package/dist/runtime/signal-intake.js +94 -0
  275. package/dist/runtime/signal-intake.js.map +1 -0
  276. package/dist/runtime/signal-metadata.d.ts +217 -0
  277. package/dist/runtime/signal-metadata.d.ts.map +1 -0
  278. package/dist/runtime/signal-metadata.js +333 -0
  279. package/dist/runtime/signal-metadata.js.map +1 -0
  280. package/dist/runtime/signal-publisher.d.ts +144 -0
  281. package/dist/runtime/signal-publisher.d.ts.map +1 -0
  282. package/dist/runtime/signal-publisher.js +248 -0
  283. package/dist/runtime/signal-publisher.js.map +1 -0
  284. package/dist/runtime/standalone-handler-runtime.d.ts +114 -0
  285. package/dist/runtime/standalone-handler-runtime.d.ts.map +1 -0
  286. package/dist/runtime/standalone-handler-runtime.js +307 -0
  287. package/dist/runtime/standalone-handler-runtime.js.map +1 -0
  288. package/dist/server/environment-attachment.d.ts +356 -0
  289. package/dist/server/environment-attachment.d.ts.map +1 -0
  290. package/dist/server/environment-attachment.js +2062 -0
  291. package/dist/server/environment-attachment.js.map +1 -0
  292. package/dist/server/environment-delivery-records.d.ts +77 -0
  293. package/dist/server/environment-delivery-records.d.ts.map +1 -0
  294. package/dist/server/environment-delivery-records.js +282 -0
  295. package/dist/server/environment-delivery-records.js.map +1 -0
  296. package/dist/server/environment-delivery-worker.d.ts +161 -0
  297. package/dist/server/environment-delivery-worker.d.ts.map +1 -0
  298. package/dist/server/environment-delivery-worker.js +673 -0
  299. package/dist/server/environment-delivery-worker.js.map +1 -0
  300. package/dist/server/environment.d.ts +43 -0
  301. package/dist/server/environment.d.ts.map +1 -0
  302. package/dist/server/environment.js +90 -0
  303. package/dist/server/environment.js.map +1 -0
  304. package/dist/server/managed-child-subscription.d.ts +11 -0
  305. package/dist/server/managed-child-subscription.d.ts.map +1 -0
  306. package/dist/server/managed-child-subscription.js +44 -0
  307. package/dist/server/managed-child-subscription.js.map +1 -0
  308. package/dist/server/managed-server-application.d.ts +327 -0
  309. package/dist/server/managed-server-application.d.ts.map +1 -0
  310. package/dist/server/managed-server-application.js +937 -0
  311. package/dist/server/managed-server-application.js.map +1 -0
  312. package/dist/server/node-coordinator.d.ts +191 -0
  313. package/dist/server/node-coordinator.d.ts.map +1 -0
  314. package/dist/server/node-coordinator.js +552 -0
  315. package/dist/server/node-coordinator.js.map +1 -0
  316. package/dist/server/process-server-coordinator.d.ts +15 -0
  317. package/dist/server/process-server-coordinator.d.ts.map +1 -0
  318. package/dist/server/process-server-coordinator.js +112 -0
  319. package/dist/server/process-server-coordinator.js.map +1 -0
  320. package/dist/server/reserved-spine-rpc-paths.json +8 -0
  321. package/dist/server/retryable-close.d.ts +28 -0
  322. package/dist/server/retryable-close.d.ts.map +1 -0
  323. package/dist/server/retryable-close.js +129 -0
  324. package/dist/server/retryable-close.js.map +1 -0
  325. package/dist/server/server-environment.d.ts +180 -0
  326. package/dist/server/server-environment.d.ts.map +1 -0
  327. package/dist/server/server-environment.js +418 -0
  328. package/dist/server/server-environment.js.map +1 -0
  329. package/dist/server/server-log.d.ts +18 -0
  330. package/dist/server/server-log.d.ts.map +1 -0
  331. package/dist/server/server-log.js +100 -0
  332. package/dist/server/server-log.js.map +1 -0
  333. package/dist/server/server.d.ts +221 -0
  334. package/dist/server/server.d.ts.map +1 -0
  335. package/dist/server/server.js +704 -0
  336. package/dist/server/server.js.map +1 -0
  337. package/dist/services/query-reader.d.ts +19 -0
  338. package/dist/services/query-reader.d.ts.map +1 -0
  339. package/dist/services/query-reader.js +43 -0
  340. package/dist/services/query-reader.js.map +1 -0
  341. package/dist/services/spine-services.d.ts +88 -0
  342. package/dist/services/spine-services.d.ts.map +1 -0
  343. package/dist/services/spine-services.js +1582 -0
  344. package/dist/services/spine-services.js.map +1 -0
  345. package/dist/spi/delivery.d.ts +5 -0
  346. package/dist/spi/delivery.d.ts.map +1 -0
  347. package/dist/spi/delivery.js +18 -0
  348. package/dist/spi/delivery.js.map +1 -0
  349. package/dist/spi/handler-registry.d.ts +8 -0
  350. package/dist/spi/handler-registry.d.ts.map +1 -0
  351. package/dist/spi/handler-registry.js +15 -0
  352. package/dist/spi/handler-registry.js.map +1 -0
  353. package/dist/stand/stand.d.ts +294 -0
  354. package/dist/stand/stand.d.ts.map +1 -0
  355. package/dist/stand/stand.js +681 -0
  356. package/dist/stand/stand.js.map +1 -0
  357. package/dist/stand/subscription-observer.d.ts +53 -0
  358. package/dist/stand/subscription-observer.d.ts.map +1 -0
  359. package/dist/stand/subscription-observer.js +444 -0
  360. package/dist/stand/subscription-observer.js.map +1 -0
  361. package/dist/stand/subscription-records.d.ts +12 -0
  362. package/dist/stand/subscription-records.d.ts.map +1 -0
  363. package/dist/stand/subscription-records.js +99 -0
  364. package/dist/stand/subscription-records.js.map +1 -0
  365. package/dist/stand/subscription-registry.d.ts +285 -0
  366. package/dist/stand/subscription-registry.d.ts.map +1 -0
  367. package/dist/stand/subscription-registry.js +403 -0
  368. package/dist/stand/subscription-registry.js.map +1 -0
  369. package/dist/stand/subscription-runtime.d.ts +102 -0
  370. package/dist/stand/subscription-runtime.d.ts.map +1 -0
  371. package/dist/stand/subscription-runtime.js +380 -0
  372. package/dist/stand/subscription-runtime.js.map +1 -0
  373. package/dist/testing/index.d.ts +36 -0
  374. package/dist/testing/index.d.ts.map +1 -0
  375. package/dist/testing/index.js +54 -0
  376. package/dist/testing/index.js.map +1 -0
  377. package/dist/tsconfig.tsbuildinfo +1 -0
  378. package/package.json +76 -0
@@ -0,0 +1,3510 @@
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 { clone, create, ScalarType, toBinary } from "@bufbuild/protobuf";
15
+ import { AnySchema, Int32ValueSchema, Int64ValueSchema, StringValueSchema, TimestampSchema, } from "@bufbuild/protobuf/wkt";
16
+ import { ValidationException, RejectionThrowable, Validate, TypeUrls, AnyMessages, Identifiers, StringifierRegistry, } from "@spine-event-engine/core";
17
+ import { CommandContextSchema, ActorContextSchema, CommandIdSchema, CommandSchema, EventContextSchema, EventIdSchema, EventSchema, MessageIdSchema, TenantIdSchema, RejectionEventContextSchema, VersionSchema, EntityOption_Kind, } from "@spine-event-engine/proto";
18
+ import * as EntityLog from "@spine-event-engine/proto/generated/spine/system/server/entity_log_events_pb.js";
19
+ import { EntityTypeNameSchema } from "@spine-event-engine/proto/generated/spine/system/server/entity_type_pb.js";
20
+ import {} from "@spine-event-engine/storage";
21
+ import { TenantBoundary } from "@spine-event-engine/storage/provider";
22
+ import { EntityCommitStorageFactories } from "@spine-event-engine/storage/provider";
23
+ import { CommandValidationError } from "../bus/command-errors.js";
24
+ import { SignalPublisher } from "../runtime/signal-publisher.js";
25
+ import { CommandRoutingInternals, } from "./command-routing.js";
26
+ import { EventRoutingInternals } from "./event-routing.js";
27
+ import { StateUpdateRoutingInternals, } from "./state-update-routing.js";
28
+ import { RoutingDeclarations } from "./routing-declarations.js";
29
+ import { EventDispatcherOriginSchemas } from "../bus/event-dispatcher-origin-schemas.js";
30
+ import { Delivery } from "../delivery/delivery.js";
31
+ import { commitFenced } from "./commit-fence.js";
32
+ import { InboxTargets } from "../delivery/inbox.js";
33
+ import { ShardIndex } from "../delivery/shard-index.js";
34
+ import { Aggregate, ProcessManager, Projection, entityHistoryAccess, processManagerQueryAccess, transactionalEntityAccess, } from "../entity/entity.js";
35
+ import { describeEntityMetadata, attachEntitySchema, } from "../entity/entity-metadata.js";
36
+ import { EntityIds, EntityRecords, entityStorageDescriptor, } from "../entity/entity-storage-descriptor.js";
37
+ import { SpecScanner } from "../entity/spec-scanner.js";
38
+ import { CommandRegistrationReadiness, } from "../handler/command-registration-readiness.js";
39
+ import { EventRegistrationReadiness, } from "../handler/event-registration-readiness.js";
40
+ import { EventHandlerFilters, } from "../handler/event-handler-filter.js";
41
+ import { SignalMetadata } from "../runtime/signal-metadata.js";
42
+ import { QueryReader } from "../services/query-reader.js";
43
+ import { HandlerMetadataRegistry, HandlerMetadataValues, } from "../handler/handler-metadata.js";
44
+ import { DeclaredRejections } from "../handler/declared-rejections.js";
45
+ import { standAccess } from "../stand/stand.js";
46
+ import { TransitionValidationError } from "./command-errors.js";
47
+ import { MessageIds, PrimitiveIds } from "./primitive-id.js";
48
+ import { ImplicitRequiredIds } from "../entity/implicit-required-id.js";
49
+ /**
50
+ * Describes an error raised when repository identity metadata cannot be constructed.
51
+ */
52
+ export class RepositoryIdentityError extends Error {
53
+ // prettier-ignore
54
+ /**
55
+ * Stable code for callers/tests that need structured failure handling.
56
+ */
57
+ code;
58
+ /**
59
+ * Creates a repository identity error.
60
+ *
61
+ * @param code The stable reason for the failed identity.
62
+ * @param message The diagnostic message.
63
+ */
64
+ constructor(code, message) {
65
+ super(message);
66
+ this.name = "RepositoryIdentityError";
67
+ this.code = code;
68
+ Object.setPrototypeOf(this, new.target.prototype);
69
+ }
70
+ }
71
+ /**
72
+ * Repository identity and context-owned storage registration over one entity constructor and state schema.
73
+ *
74
+ * The class records the ownership facts bounded-context registration needs for
75
+ * duplicate and conflict checks. Context assembly uses this metadata to attach
76
+ * a repository to one built context and open state record storage. With
77
+ * authentic explicit aggregate handler metadata, the built context can also
78
+ * execute aggregate commands through repository-owned assignees and live
79
+ * direct transactional handlers, persist the latest aggregate state plus a diagnostic event journal,
80
+ * and hand already-stored events to the event bus. Aggregate and process-manager command
81
+ * execution require `command.id` before routing or mutation so produced events
82
+ * can carry a contract-valid command origin. With authentic projection
83
+ * subscriber metadata, built contexts can also execute projection subscribers
84
+ * and write changed projection state through the context-owned `Stand`. With
85
+ * authentic process-manager metadata, built contexts can execute command
86
+ * assignees, event reactors, and event-commanding handlers, storing changed
87
+ * process-manager state through tenant-scoped Stand records with numeric
88
+ * versions. The `events` option declares generated event schemas emitted by
89
+ * aggregate and process-manager producer handlers. The repository surface still
90
+ * does not expose direct entity lookup/storage APIs, inbox/delivery management,
91
+ * caches, lifecycle monitors, or transport startup.
92
+ *
93
+ * @typeParam EntityType - A single concrete aggregate, projection, or process-manager constructor
94
+ * with one concrete generated state schema. Broad constructor, constructor-union, broad-schema, and
95
+ * schema-union bindings intentionally fail the public type constraint.
96
+ */
97
+ export class Repository {
98
+ #entityType;
99
+ #entityFamily;
100
+ #metadata;
101
+ #routing;
102
+ /**
103
+ * Creates repository identity for one entity family and state schema pair.
104
+ *
105
+ * @param options The entity constructor, state schema, and optional routing configuration.
106
+ */
107
+ constructor(options) {
108
+ if (!RepositoryIdentity.isRepositoryOptionsObject(options)) {
109
+ throw new RepositoryIdentityError("UNSUPPORTED_ENTITY_TYPE", "Repository options must be a non-null object with an entity type class constructor " +
110
+ "extending Aggregate, Projection, or ProcessManager.");
111
+ }
112
+ const entityType = RepositoryIdentity.readEntityTypeOption(options);
113
+ const entityTypeDisplayName = RepositoryIdentity.entityTypeName(entityType);
114
+ if (typeof entityType !== "function" || !RepositoryIdentity.isClassConstructor(entityType)) {
115
+ throw new RepositoryIdentityError("UNSUPPORTED_ENTITY_TYPE", `Repository entity type "${entityTypeDisplayName}" must be a class constructor ` +
116
+ "extending Aggregate, Projection, or ProcessManager.");
117
+ }
118
+ const entityFamily = RepositoryIdentity.resolveRepositoryEntityFamily(entityType);
119
+ if (entityFamily === undefined) {
120
+ throw new RepositoryIdentityError("UNSUPPORTED_ENTITY_TYPE", `Repository entity type "${entityTypeDisplayName}" must extend Aggregate, Projection, or ProcessManager.`);
121
+ }
122
+ const schema = RepositoryIdentity.readRepositorySchemaOption(options, entityTypeDisplayName, entityFamily);
123
+ const metadata = RepositoryIdentity.describeRepositoryEntityMetadata(entityTypeDisplayName, entityFamily, schema);
124
+ if (metadata.kind !== entityFamily) {
125
+ throw new RepositoryIdentityError("ENTITY_SCHEMA_KIND_MISMATCH", `Repository entity type "${entityTypeDisplayName}" does not match ` +
126
+ "the supplied state schema.");
127
+ }
128
+ if (options.stateUpdateRouting !== undefined && entityFamily !== "projection") {
129
+ throw new RepositoryIdentityError("UNSUPPORTED_ENTITY_TYPE", "State-update routing is supported only by Projection repositories.");
130
+ }
131
+ attachEntitySchema(entityType, schema);
132
+ this.#entityType = entityType;
133
+ this.#entityFamily = entityFamily;
134
+ this.#metadata = metadata;
135
+ this.#routing = RepositoryRoutes.createRepositoryRouting(this.#entityType, this.#entityFamily, this.#metadata, options.handlers, options.events ?? [], CommandRoutingInternals.snapshot(options.commandRouting), EventRoutingInternals.snapshot(options.eventRouting), StateUpdateRoutingInternals.snapshot(options.stateUpdateRouting), new StringifierRegistry(options.stringifierRegistry));
136
+ repositoryRoutings.set(this, this.#routing);
137
+ repositoryProducedEventSchemas.set(this, Object.freeze([...this.#routing.producedEventSchemas]));
138
+ repositoryHistoryConfigurations.set(this, RepositoryStorage.readHistoryConfiguration(options, this.#entityFamily));
139
+ repositorySnapshots.set(this, RepositoryIdentity.createRepositorySnapshot(this.#entityType, this.#entityFamily, this.#metadata));
140
+ repositoryDispatchers.set(this, RepositoryDispatch.createRepositoryDispatchers(this, this.#routing));
141
+ const entityInboxTarget = RepositoryDispatch.createEntityInboxTarget(this, this.#routing);
142
+ const projectionInboxTarget = RepositoryDispatch.createProjectionInboxTarget(this, this.#routing);
143
+ if (entityInboxTarget !== undefined) {
144
+ repositoryEntityInboxTargets.set(this, entityInboxTarget);
145
+ }
146
+ if (projectionInboxTarget !== undefined) {
147
+ repositoryProjectionInboxTargets.set(this, projectionInboxTarget);
148
+ repositoryProjectionDirect.set(this, RepositoryDispatch.createProjectionDirectDispatch(this, this.#routing));
149
+ }
150
+ }
151
+ /**
152
+ * Returns the entity constructor owned by this repository identity.
153
+ *
154
+ * @returns The owned entity constructor.
155
+ */
156
+ get entityType() {
157
+ return this.#entityType;
158
+ }
159
+ /**
160
+ * Returns the entity family inferred from its constructor.
161
+ *
162
+ * @returns The aggregate, projection, or process-manager family.
163
+ */
164
+ get entityFamily() {
165
+ return this.#entityFamily;
166
+ }
167
+ /**
168
+ * Returns the generated schema for this repository's entity state.
169
+ *
170
+ * @returns The owned state schema.
171
+ */
172
+ get stateSchema() {
173
+ return this.#metadata.schema;
174
+ }
175
+ /**
176
+ * Sets whether future state-history rows are appended for this repository.
177
+ *
178
+ * This JVM-parity switch is for controlled administration/testing, not
179
+ * routine request-time behavior. It never deletes or reconstructs history.
180
+ *
181
+ * @param enabled Whether later successful stores append state-history rows.
182
+ */
183
+ setStateHistoryEnabled(enabled) {
184
+ if (typeof enabled !== "boolean") {
185
+ throw new TypeError("Repository state-history switch requires a boolean.");
186
+ }
187
+ const configuration = RepositoryStorage.historyConfiguration(this);
188
+ if (configuration.stateHistory === enabled)
189
+ return;
190
+ configuration.stateHistory = enabled;
191
+ const handles = repositoryEntityHandles.get(this);
192
+ if (handles !== undefined) {
193
+ for (const handle of handles.values())
194
+ handle.close();
195
+ handles.clear();
196
+ }
197
+ }
198
+ /**
199
+ * Returns descriptor-derived metadata for the owned entity state.
200
+ *
201
+ * @returns A metadata view for the owned state.
202
+ */
203
+ get metadata() {
204
+ return this.#metadata;
205
+ }
206
+ /**
207
+ * Returns the fully qualified Protobuf name of the owned entity state.
208
+ *
209
+ * @returns The state message type name.
210
+ */
211
+ get stateFullTypeName() {
212
+ return this.#metadata.fullTypeName;
213
+ }
214
+ /**
215
+ * Returns a copy of the canonical entity ID field metadata.
216
+ *
217
+ * @returns The descriptor field used as the entity ID.
218
+ */
219
+ get idField() {
220
+ return RepositoryIdentity.cloneFieldMetadata(this.#metadata.idField);
221
+ }
222
+ /**
223
+ * Returns a copy-safe identity snapshot for builder duplicate and conflict checks.
224
+ *
225
+ * @returns An immutable snapshot of the repository identity.
226
+ */
227
+ get snapshot() {
228
+ return RepositoryIdentity.cloneRepositorySnapshot(RepositoryIdentity.createRepositorySnapshot(this.#entityType, this.#entityFamily, this.#metadata));
229
+ }
230
+ /**
231
+ * Routes a command to one entity ID without invoking a handler.
232
+ *
233
+ * @param command The command envelope to route.
234
+ * @returns The calculated command route.
235
+ */
236
+ routeCommand(command) {
237
+ return this.#routing.routeCommand(command);
238
+ }
239
+ /**
240
+ * Routes an Event without invoking a handler.
241
+ *
242
+ * The default route uses a readable producer whose typed ID is compatible
243
+ * with the Entity ID. A valid producer of another type falls back to the
244
+ * Event's declaration-first field. A producer that claims the compatible type
245
+ * but cannot be decoded is rejected. Custom routes may select zero, one, or
246
+ * many targets.
247
+ *
248
+ * @param event The event envelope to route.
249
+ * @returns The calculated event route.
250
+ */
251
+ routeEvent(event) {
252
+ return this.#routing.routeEvent(event);
253
+ }
254
+ }
255
+ const repositorySnapshots = new WeakMap();
256
+ const repositoryRoutings = new WeakMap();
257
+ const repositoryProducedEventSchemas = new WeakMap();
258
+ const repositoryDispatchers = new WeakMap();
259
+ const repositoryEntityInboxTargets = new WeakMap();
260
+ const repositoryProjectionInboxTargets = new WeakMap();
261
+ const repositoryProjectionDirect = new WeakMap();
262
+ const repositoryRuntimes = new WeakMap();
263
+ const repositoryEntityHandles = new WeakMap();
264
+ const entityStateHistoryCaches = new WeakMap();
265
+ const repositoryHistoryConfigurations = new WeakMap();
266
+ const repositoryDispatchGuards = new WeakMap();
267
+ Object.freeze(Repository);
268
+ /**
269
+ * Exposes framework-only repository access for bounded-context assembly.
270
+ *
271
+ * @internal
272
+ */
273
+ export const repositoryAccess = Object.freeze({
274
+ hasInstance(repository) {
275
+ return repositorySnapshots.has(repository);
276
+ },
277
+ snapshot(repository) {
278
+ const snapshot = repositorySnapshots.get(repository);
279
+ if (snapshot === undefined) {
280
+ throw new TypeError("Repository snapshot requires a Repository instance.");
281
+ }
282
+ return RepositoryIdentity.cloneRepositorySnapshot(snapshot);
283
+ },
284
+ producedEventSchemas(repository) {
285
+ const schemas = repositoryProducedEventSchemas.get(repository);
286
+ if (schemas === undefined) {
287
+ throw new TypeError("Produced event schemas require a Repository instance.");
288
+ }
289
+ return schemas;
290
+ },
291
+ commandDispatcher(repository) {
292
+ return repositoryDispatchers.get(repository)?.command;
293
+ },
294
+ eventDispatcher(repository) {
295
+ return repositoryDispatchers.get(repository)?.event;
296
+ },
297
+ systemEventDispatcher(repository) {
298
+ return repositoryDispatchers.get(repository)?.systemEvent;
299
+ },
300
+ stateSubscriptionTypes(repository) {
301
+ const routing = repositoryRoutings.get(repository);
302
+ if (routing === undefined) {
303
+ throw new TypeError("State subscriptions require a Repository instance.");
304
+ }
305
+ return Object.freeze(routing.stateSchemas.map((schema) => schema.typeName));
306
+ },
307
+ routeStateUpdate(repository, event) {
308
+ const routing = repositoryRoutings.get(repository);
309
+ if (routing === undefined) {
310
+ throw new TypeError("State-update routing requires a Repository instance.");
311
+ }
312
+ return routing.routeStateUpdate(event);
313
+ },
314
+ entityInboxTarget(repository) {
315
+ return repositoryEntityInboxTargets.get(repository);
316
+ },
317
+ projectionInboxTarget(repository) {
318
+ return repositoryProjectionInboxTargets.get(repository);
319
+ },
320
+ dispatchProjectionDirect(repository, event, rebuild) {
321
+ const dispatch = repositoryProjectionDirect.get(repository);
322
+ if (dispatch === undefined) {
323
+ throw new TypeError("Direct projection dispatch requires a projection Repository instance.");
324
+ }
325
+ return dispatch(event, rebuild);
326
+ },
327
+ bindRuntime(repository, runtime) {
328
+ repositoryRuntimes.set(repository, Object.freeze(runtime));
329
+ },
330
+ clearRuntime(repository) {
331
+ repositoryRuntimes.delete(repository);
332
+ repositoryDispatchGuards.delete(repository);
333
+ const handles = repositoryEntityHandles.get(repository);
334
+ if (handles !== undefined) {
335
+ for (const handle of handles.values())
336
+ handle.close();
337
+ handles.clear();
338
+ }
339
+ },
340
+ });
341
+ const inboxDedupMs = 30_000;
342
+ class AggregateExecutionSupport {
343
+ #repository;
344
+ #runtime;
345
+ #storageContext;
346
+ constructor(repository, runtime, storageContext) {
347
+ this.#repository = repository;
348
+ this.#runtime = runtime;
349
+ this.#storageContext = storageContext;
350
+ }
351
+ async loadAggregate(entityId) {
352
+ const storageInput = RepositoryStorage.entityStorageInput(this.#repository, this.#storageContext);
353
+ const storage = RepositoryStorage.openRepositoryEntityStorage(this.#repository, this.#runtime.storageFactory, storageInput);
354
+ const current = await standAccess.readCurrent(this.#runtime.stand, this.#repository.stateSchema, entityId, this.#storageContext.tenantId === undefined
355
+ ? {}
356
+ : { tenantId: this.#storageContext.tenantId });
357
+ const entity = this.#instantiateAggregate(entityId, current);
358
+ RepositoryHistoryInternals.bindEntityHistory(entity, storage, entityId, this.#repository.stateSchema);
359
+ return Object.freeze({
360
+ commits: storage.commits,
361
+ current: current === undefined
362
+ ? undefined
363
+ : EntityRecords.pack(this.#repository.stateSchema, entityId, current.state, current.versionMessage, { archived: current.archived, deleted: current.deleted }),
364
+ entity,
365
+ oldState: current === undefined
366
+ ? undefined
367
+ : clone(this.#repository.stateSchema, current.state),
368
+ states: storage.states,
369
+ events: storage.events,
370
+ version: current?.version ?? 0n,
371
+ storageInput,
372
+ });
373
+ }
374
+ normalizeProducedSignals(produced) {
375
+ if (produced === undefined) {
376
+ return Object.freeze([]);
377
+ }
378
+ if (Array.isArray(produced)) {
379
+ return Object.freeze(Array.from(produced));
380
+ }
381
+ return Object.freeze([produced]);
382
+ }
383
+ /**
384
+ * Store the current aggregate record before its diagnostic and delivery journals.
385
+ */
386
+ async persistAggregateUpdate(loaded, entityId, version, events) {
387
+ const lifecycle = RepositoryEntities.repositoryLifecycle(loaded.entity);
388
+ const state = RepositoryEntities.repositoryState(loaded.entity);
389
+ const deferred = await standAccess.deferUpdate(this.#runtime.stand, this.#repository.stateSchema, state, RepositoryStand.standUpdateOptions(this.#storageContext.tenantId, create(VersionSchema, { number: RepositorySignals.eventVersionNumber(version) }), lifecycle));
390
+ try {
391
+ const createdAt = events[events.length - 1]?.context?.timestamp ?? create(TimestampSchema);
392
+ const outcome = await loaded.commits.commit({
393
+ context: this.#storageContext,
394
+ entity: loaded.storageInput,
395
+ entityId,
396
+ ...(loaded.current === undefined ? {} : { expected: loaded.current }),
397
+ next: EntityRecords.pack(this.#repository.stateSchema, entityId, state, version, lifecycle),
398
+ ...(RepositoryStorage.historyConfiguration(this.#repository).stateHistory
399
+ ? {
400
+ states: [
401
+ EntityRecords.pack(this.#repository.stateSchema, entityId, state, create(VersionSchema, {
402
+ number: RepositorySignals.eventVersionNumber(version),
403
+ timestamp: createdAt,
404
+ }), lifecycle),
405
+ ],
406
+ }
407
+ : {}),
408
+ diagnostics: events.map((event) => clone(EventSchema, event)),
409
+ events,
410
+ });
411
+ if (outcome !== "committed") {
412
+ deferred.cancel();
413
+ throw new Error("Concurrent Aggregate state commit conflict.");
414
+ }
415
+ entityStateHistoryCaches.get(loaded.entity)?.clear();
416
+ }
417
+ catch (error) {
418
+ deferred.cancel();
419
+ throw error;
420
+ }
421
+ try {
422
+ deferred.notify();
423
+ }
424
+ catch (error) {
425
+ const event = events[events.length - 1];
426
+ if (event !== undefined)
427
+ this.#runtime.publisher.reportFailure("event", event, error);
428
+ }
429
+ return true;
430
+ }
431
+ async appendDiagnosticEvent(loaded, entityId, event) {
432
+ await loaded.events.append(clone(EventSchema, event));
433
+ }
434
+ /**
435
+ * Complete persistence before scheduling best-effort stored-event dispatch.
436
+ */
437
+ async persistAggregateAndDispatch(loaded, entityId, version, events, dispatch, onPersisted) {
438
+ const committed = await this.persistAggregateUpdate(loaded, entityId, version, events);
439
+ if (!committed)
440
+ return () => Promise.resolve();
441
+ await onPersisted();
442
+ return async () => {
443
+ await Promise.all(events.map(async (event) => {
444
+ try {
445
+ await dispatch(event);
446
+ }
447
+ catch (error) {
448
+ this.#runtime.publisher.reportFailure("event", event, error);
449
+ }
450
+ }));
451
+ };
452
+ }
453
+ #instantiateAggregate(entityId, current) {
454
+ const entityType = this.#repository.entityType;
455
+ const options = {
456
+ id: entityId,
457
+ schema: this.#repository.stateSchema,
458
+ state: current?.state ?? this.#defaultState(entityId),
459
+ version: current?.version ?? 0n,
460
+ };
461
+ if (current !== undefined) {
462
+ options.lifecycle = { archived: current.archived, deleted: current.deleted };
463
+ }
464
+ return new entityType(options);
465
+ }
466
+ #defaultState(entityId) {
467
+ return create(this.#repository.stateSchema, {
468
+ [this.#repository.idField.localName]: entityId,
469
+ });
470
+ }
471
+ }
472
+ class AggregateCommandExecution {
473
+ #repository;
474
+ #routing;
475
+ #runtime;
476
+ #command;
477
+ #support;
478
+ constructor(repository, routing, runtime, command) {
479
+ this.#repository = repository;
480
+ this.#routing = routing;
481
+ this.#runtime = runtime;
482
+ this.#command = command;
483
+ this.#support = new AggregateExecutionSupport(repository, runtime, RepositoryTenants.storageContextForCommand(this.#runtime.context, this.#command));
484
+ }
485
+ async run(replayedRoute) {
486
+ void RepositorySignals.requireCommandId(this.#command);
487
+ const intake = this.#readIntake(replayedRoute);
488
+ if (intake === undefined)
489
+ return undefined;
490
+ return this.#runAssignee(intake);
491
+ }
492
+ #readIntake(replayedRoute) {
493
+ const commandMessage = EntityInvocation.requireSignalMessage(this.#command.message, "command");
494
+ const commandSchema = RepositoryRoutes.schemaForTypeUrl(this.#routing.commandSchemas, commandMessage.typeUrl, "command");
495
+ const message = EntityInvocation.unpackRequired(commandMessage, commandSchema, "command");
496
+ const route = replayedRoute ?? this.#repository.routeCommand(this.#command);
497
+ const assignee = this.#routing.commandReadiness?.findCommandAssignee(route.messageFullTypeName);
498
+ return assignee === undefined ? undefined : Object.freeze({ message, route, assignee });
499
+ }
500
+ async #runAssignee(intake) {
501
+ const loaded = await this.#support.loadAggregate(intake.route.entityId);
502
+ this.#publishCommandDispatch(intake.route.entityId);
503
+ const commandContext = EntityInvocation.commandHandlerContext(this.#command);
504
+ let produced;
505
+ try {
506
+ produced = await this.#invokeAssignee(loaded.entity, intake.assignee.handler.methodName, intake.message, intake.assignee.handler.parameterCount, commandContext);
507
+ }
508
+ catch (error) {
509
+ if (!RejectionThrowable.is(error)) {
510
+ throw error;
511
+ }
512
+ RepositorySignals.requireDeclaredRejection(intake.assignee.handler, error);
513
+ return RepositorySignals.postRejectionEvent(this.#runtime, this.#repository, this.#command, intake.route.entityId, error);
514
+ }
515
+ const events = this.#requiredEvents(produced, intake.route.entityId, loaded.version);
516
+ return this.#persistAssigneeResult(loaded, intake.route.entityId, events);
517
+ }
518
+ #publishCommandDispatch(entityId) {
519
+ HandlerDispatchPublisher.command(this.#runtime, this.#repository, this.#command, entityId);
520
+ }
521
+ #requiredEvents(produced, entityId, version) {
522
+ const events = this.#bindProducedEvents(this.#support.normalizeProducedSignals(produced), entityId, version, true);
523
+ if (events.length === 0)
524
+ throw new Error("Repository aggregate command handlers must return at least one event.");
525
+ return events;
526
+ }
527
+ #persistAssigneeResult(loaded, entityId, events) {
528
+ const committedVersion = loaded.version + 1n;
529
+ return this.#support.persistAggregateAndDispatch(loaded, entityId, committedVersion, events, (event) => this.#runtime.publisher.publishCommittedEvent(event), () => {
530
+ if (!RepositoryEntities.repositoryChanged(loaded.entity))
531
+ return;
532
+ EntityStateChangePublisher.command(this.#runtime, this.#repository, this.#command, entityId, loaded.oldState, loaded.current === undefined
533
+ ? undefined
534
+ : {
535
+ archived: loaded.current.lifecycleFlags?.archived ?? false,
536
+ deleted: loaded.current.lifecycleFlags?.deleted ?? false,
537
+ }, RepositoryEntities.repositoryState(loaded.entity), RepositoryEntities.repositoryLifecycle(loaded.entity), RepositorySignals.eventVersionNumber(committedVersion));
538
+ });
539
+ }
540
+ async #invokeAssignee(entity, methodName, message, parameterCount, context) {
541
+ transactionalEntityAccess.start(entity);
542
+ try {
543
+ const produced = await EntityInvocation.invokeEntityMethod(entity, methodName, message, parameterCount, context);
544
+ const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
545
+ if (commit.status === "rejected") {
546
+ throw new TransitionValidationError(commit.validation.error);
547
+ }
548
+ return produced;
549
+ }
550
+ catch (error) {
551
+ transactionalEntityAccess.rollback(entity);
552
+ throw error;
553
+ }
554
+ }
555
+ #bindProducedEvents(produced, entityId, lastVersion, allowEnvelopes) {
556
+ const dispatchVersion = lastVersion;
557
+ return Object.freeze(produced.map((signal) => this.#bindProducedEvent(signal, entityId, dispatchVersion, allowEnvelopes)));
558
+ }
559
+ #bindProducedEvent(signal, entityId, version, allowEnvelopes) {
560
+ const metadata = this.#runtime.signalMetadata.eventFromCommand(this.#command, {
561
+ version: RepositorySignals.eventVersionNumber(version),
562
+ });
563
+ const bound = allowEnvelopes && EntityInvocation.isEventEnvelope(signal)
564
+ ? clone(EventSchema, signal)
565
+ : this.#packDomainEvent(signal, metadata);
566
+ bound.context = RepositorySignals.eventContextWithProducer(metadata.context, this.#repository, entityId);
567
+ return bound;
568
+ }
569
+ #packDomainEvent(message, metadata) {
570
+ const typeName = EntityInvocation.messageTypeName(message);
571
+ const schema = this.#routing.producedEventSchemas.find((candidate) => candidate.typeName === typeName);
572
+ if (schema === undefined) {
573
+ throw new Error(`Repository aggregate execution cannot pack event message "${typeName}".`);
574
+ }
575
+ return create(EventSchema, {
576
+ id: metadata.id,
577
+ message: AnyMessages.pack(schema, message),
578
+ context: metadata.context,
579
+ });
580
+ }
581
+ }
582
+ class AggregateEventExecution {
583
+ #repository;
584
+ #routing;
585
+ #runtime;
586
+ #event;
587
+ #support;
588
+ constructor(repository, routing, runtime, event) {
589
+ this.#repository = repository;
590
+ this.#routing = routing;
591
+ this.#runtime = runtime;
592
+ this.#event = event;
593
+ this.#support = new AggregateExecutionSupport(repository, runtime, RepositoryTenants.storageContextForEvent(this.#runtime.context, this.#event));
594
+ }
595
+ async runTarget(entityId, acceptedRoute) {
596
+ const intake = this.#readIntake(acceptedRoute);
597
+ if (intake.reactors.length === 0) {
598
+ return;
599
+ }
600
+ await this.#executeEntity(entityId, intake);
601
+ }
602
+ #readIntake(acceptedRoute) {
603
+ const eventMessage = EntityInvocation.requireSignalMessage(this.#event.message, "event");
604
+ const eventSchema = RepositoryRoutes.schemaForTypeUrl(this.#routing.eventSchemas, eventMessage.typeUrl, "event");
605
+ const message = EntityInvocation.unpackRequired(eventMessage, eventSchema, "event");
606
+ const route = acceptedRoute;
607
+ const reactors = this.#routing
608
+ .eventReactors(route.messageFullTypeName, message, this.#event.context?.external === true)
609
+ .filter((reactor) => RepositoryHandlers.handlerEmittedSchemas(reactor.handler).length > 0);
610
+ return Object.freeze({
611
+ message,
612
+ route,
613
+ reactors: Object.freeze([...reactors]),
614
+ });
615
+ }
616
+ async #executeEntity(entityId, intake) {
617
+ const loaded = await this.#support.loadAggregate(entityId);
618
+ const produced = await this.#invokeHandlers(entityId, loaded, intake);
619
+ await this.#persistProducedEvents(loaded, entityId, produced);
620
+ await this.#support.appendDiagnosticEvent(loaded, entityId, DispatchGuards.guardedJournalEvent(this.#repository, this.#event, entityId));
621
+ return undefined;
622
+ }
623
+ async #persistProducedEvents(loaded, entityId, produced) {
624
+ if (produced.length === 0)
625
+ return;
626
+ const dispatch = await this.#support.persistAggregateAndDispatch(loaded, entityId, loaded.version + 1n, produced, (event) => this.#runtime.publisher.publishReactorEvent(event), () => {
627
+ this.#publishStateChange(loaded, entityId);
628
+ });
629
+ void dispatch();
630
+ }
631
+ #publishStateChange(loaded, entityId) {
632
+ if (!RepositoryEntities.repositoryChanged(loaded.entity))
633
+ return;
634
+ EntityStateChangePublisher.event(this.#runtime, this.#repository, this.#event, entityId, loaded.oldState, this.#lifecycleSnapshot(loaded), RepositoryEntities.repositoryState(loaded.entity), RepositoryEntities.repositoryLifecycle(loaded.entity), RepositorySignals.eventVersionNumber(loaded.version + 1n));
635
+ }
636
+ #lifecycleSnapshot(loaded) {
637
+ return loaded.current === undefined
638
+ ? undefined
639
+ : {
640
+ archived: loaded.current.lifecycleFlags?.archived ?? false,
641
+ deleted: loaded.current.lifecycleFlags?.deleted ?? false,
642
+ };
643
+ }
644
+ async #invokeHandlers(entityId, loaded, intake) {
645
+ const eventContext = EntityInvocation.eventHandlerContext(this.#event);
646
+ const events = [];
647
+ transactionalEntityAccess.start(loaded.entity);
648
+ try {
649
+ await this.#invokeReactors(entityId, loaded, intake, eventContext, events);
650
+ await this.#commitEntity(loaded.entity);
651
+ return Object.freeze(events);
652
+ }
653
+ catch (error) {
654
+ transactionalEntityAccess.rollback(loaded.entity);
655
+ throw error;
656
+ }
657
+ }
658
+ async #invokeReactors(entityId, loaded, intake, eventContext, events) {
659
+ if (intake.reactors.length > 0)
660
+ HandlerDispatchPublisher.reactor(this.#runtime, this.#repository, this.#event, entityId);
661
+ for (const reactor of intake.reactors) {
662
+ const produced = await EntityInvocation.invokeEntityMethod(loaded.entity, reactor.handler.methodName, intake.message, reactor.handler.parameterCount, eventContext);
663
+ events.push(...this.#bindProducedEvents(this.#support.normalizeProducedSignals(produced), entityId, loaded.version));
664
+ }
665
+ }
666
+ async #commitEntity(entity) {
667
+ const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
668
+ if (commit.status === "rejected")
669
+ throw new TransitionValidationError(commit.validation.error);
670
+ }
671
+ #bindProducedEvents(produced, entityId, lastVersion) {
672
+ const version = lastVersion;
673
+ return Object.freeze(produced.map((signal) => this.#bindProducedEvent(signal, entityId, version)));
674
+ }
675
+ #bindProducedEvent(signal, entityId, version) {
676
+ const typeName = EntityInvocation.messageTypeName(signal);
677
+ const schema = this.#routing.producedEventSchemas.find((candidate) => candidate.typeName === typeName);
678
+ if (schema === undefined) {
679
+ throw new Error(`Repository aggregate execution cannot pack event message "${typeName}".`);
680
+ }
681
+ const metadata = this.#runtime.signalMetadata.eventFromEvent(this.#event, {
682
+ version: RepositorySignals.eventVersionNumber(version),
683
+ });
684
+ return create(EventSchema, {
685
+ id: metadata.id,
686
+ message: AnyMessages.pack(schema, signal),
687
+ context: RepositorySignals.eventContextWithProducer(metadata.context, this.#repository, entityId),
688
+ });
689
+ }
690
+ }
691
+ class ProjectionEventExecution {
692
+ #repository;
693
+ #routing;
694
+ #runtime;
695
+ #event;
696
+ #rebuild;
697
+ constructor(repository, routing, runtime, event, rebuild = false) {
698
+ this.#repository = repository;
699
+ this.#routing = routing;
700
+ this.#runtime = runtime;
701
+ this.#event = event;
702
+ this.#rebuild = rebuild;
703
+ }
704
+ static async runStateTarget(repository, routing, runtime, event, entityId, route, subscribers) {
705
+ const execution = new ProjectionEventExecution(repository, routing, runtime, event);
706
+ await execution.#executeStateTarget(entityId, route.state, subscribers);
707
+ }
708
+ async run(acceptedRoute) {
709
+ const intake = this.#readIntake(acceptedRoute);
710
+ if (intake.subscribers.length === 0) {
711
+ return;
712
+ }
713
+ for (const entityId of intake.route.entityIds) {
714
+ await InboxHandoff.handoffProjectionEvent(this.#repository, this.#runtime, this.#event, entityId);
715
+ }
716
+ }
717
+ async runTarget(entityId, acceptedRoute) {
718
+ const intake = this.#readIntake(acceptedRoute);
719
+ if (intake.subscribers.length === 0) {
720
+ return;
721
+ }
722
+ await this.#executeTarget(entityId, intake.subscribers);
723
+ }
724
+ async runDirect(acceptedRoute) {
725
+ const intake = this.#readIntake(acceptedRoute);
726
+ if (intake.subscribers.length === 0) {
727
+ return;
728
+ }
729
+ for (const entityId of intake.route.entityIds) {
730
+ await this.#executeTarget(entityId, intake.subscribers);
731
+ }
732
+ }
733
+ async #executeTarget(entityId, subscribers) {
734
+ const packedMessage = EntityInvocation.requireSignalMessage(this.#event.message, "event");
735
+ const tenantOptions = RepositoryTenants.standTenantOptions(this.#runtime.context, this.#event);
736
+ const mode = this.#rebuild ? "rebuild" : "stored";
737
+ const loaded = await this.#loadProjection(entityId, tenantOptions, mode);
738
+ HandlerDispatchPublisher.subscriber(this.#runtime, this.#repository, this.#event, entityId);
739
+ await this.#invokeSubscribers(loaded.entity, subscribers, packedMessage);
740
+ await this.#storeIfChanged(loaded, tenantOptions, loaded.current === undefined
741
+ ? undefined
742
+ : EntityRecords.unpack(this.#repository.stateSchema, loaded.current).state, mode);
743
+ }
744
+ async #executeStateTarget(entityId, state, subscribers) {
745
+ const tenantOptions = RepositoryTenants.standTenantOptions(this.#runtime.context, this.#event);
746
+ const loaded = await this.#loadProjection(entityId, tenantOptions, "stored");
747
+ await this.#invokeStateSubscribers(loaded.entity, subscribers, state);
748
+ await this.#storeIfChanged(loaded, tenantOptions, loaded.current === undefined
749
+ ? undefined
750
+ : EntityRecords.unpack(this.#repository.stateSchema, loaded.current).state, "stored");
751
+ }
752
+ #readIntake(acceptedRoute) {
753
+ const route = acceptedRoute ?? this.#repository.routeEvent(this.#event);
754
+ const packedMessage = EntityInvocation.requireSignalMessage(this.#event.message, "event");
755
+ const eventSchema = RepositoryRoutes.schemaForTypeUrl(this.#routing.eventSchemas, packedMessage.typeUrl, "event");
756
+ const message = EntityInvocation.unpackRequired(packedMessage, eventSchema, "event");
757
+ const subscribers = this.#routing.eventSubscribers(route.messageFullTypeName, message, this.#event.context?.external === true);
758
+ return Object.freeze({
759
+ route,
760
+ subscribers,
761
+ });
762
+ }
763
+ async #storeIfChanged(loaded, tenantOptions, oldState, mode) {
764
+ if (!RepositoryEntities.repositoryChanged(loaded.entity)) {
765
+ return;
766
+ }
767
+ const entityId = loaded.entity.id;
768
+ const state = RepositoryEntities.repositoryState(loaded.entity);
769
+ const version = BigInt(this.#event.context?.version?.number ?? 0);
770
+ const lifecycle = RepositoryEntities.repositoryLifecycle(loaded.entity);
771
+ const deferred = await standAccess.deferUpdate(this.#runtime.stand, this.#repository.stateSchema, state, RepositoryStand.standUpdateOptions(tenantOptions.tenantId, this.#event.context?.version, lifecycle));
772
+ try {
773
+ const outcome = await loaded.commits.commit({
774
+ context: loaded.storageInput.context,
775
+ entity: loaded.storageInput,
776
+ entityId,
777
+ ...(loaded.current === undefined ? {} : { expected: loaded.current }),
778
+ next: EntityRecords.pack(this.#repository.stateSchema, entityId, state, this.#event.context?.version ?? version, lifecycle),
779
+ ...(RepositoryStorage.historyConfiguration(this.#repository).stateHistory
780
+ ? {
781
+ states: [
782
+ EntityRecords.pack(this.#repository.stateSchema, entityId, state, this.#event.context?.version ?? version, lifecycle),
783
+ ],
784
+ }
785
+ : {}),
786
+ });
787
+ if (outcome !== "committed") {
788
+ deferred.cancel();
789
+ throw new Error("Concurrent Projection state commit conflict.");
790
+ }
791
+ }
792
+ catch (error) {
793
+ deferred.cancel();
794
+ throw error;
795
+ }
796
+ try {
797
+ deferred.notify();
798
+ }
799
+ catch (error) {
800
+ this.#runtime.publisher.reportFailure("event", this.#event, error);
801
+ }
802
+ EntityStateChangePublisher.event(this.#runtime, this.#repository, this.#event, entityId, oldState, mode === "rebuild"
803
+ ? lifecycle
804
+ : loaded.current === undefined
805
+ ? undefined
806
+ : {
807
+ archived: loaded.current.lifecycleFlags?.archived ?? false,
808
+ deleted: loaded.current.lifecycleFlags?.deleted ?? false,
809
+ }, state, lifecycle, this.#event.context?.version?.number ?? 0);
810
+ }
811
+ async #invokeSubscribers(entity, subscribers, packedMessage) {
812
+ transactionalEntityAccess.start(entity);
813
+ try {
814
+ for (const subscriber of subscribers) {
815
+ const subscriberMessage = EntityInvocation.unpackRequired(packedMessage, subscriber.handler.schema, "event");
816
+ const eventContext = EntityInvocation.eventHandlerContext(this.#event);
817
+ await EntityInvocation.invokeEntityMethod(entity, subscriber.handler.methodName, subscriberMessage, subscriber.handler.parameterCount, eventContext);
818
+ }
819
+ const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
820
+ if (commit.status === "rejected") {
821
+ throw new TransitionValidationError(commit.validation.error);
822
+ }
823
+ }
824
+ catch (error) {
825
+ transactionalEntityAccess.rollback(entity);
826
+ throw error;
827
+ }
828
+ }
829
+ async #invokeStateSubscribers(entity, subscribers, state) {
830
+ transactionalEntityAccess.start(entity);
831
+ try {
832
+ for (const subscriber of subscribers) {
833
+ const context = EntityInvocation.eventHandlerContext(this.#event);
834
+ await EntityInvocation.invokeEntityMethod(entity, subscriber.handler.methodName, state, subscriber.handler.parameterCount, context);
835
+ }
836
+ const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
837
+ if (commit.status === "rejected")
838
+ throw new TransitionValidationError(commit.validation.error);
839
+ }
840
+ catch (error) {
841
+ transactionalEntityAccess.rollback(entity);
842
+ throw error;
843
+ }
844
+ }
845
+ async #loadProjection(entityId, options, mode) {
846
+ const stored = await standAccess.readCurrent(this.#runtime.stand, this.#repository.stateSchema, entityId, options);
847
+ const entityType = this.#repository.entityType;
848
+ const entity = new entityType({
849
+ id: entityId,
850
+ schema: this.#repository.stateSchema,
851
+ state: stored === undefined || (mode === "rebuild" && stored.deleted)
852
+ ? this.#defaultState(entityId)
853
+ : stored.state,
854
+ version: mode === "rebuild" && stored?.deleted
855
+ ? 0
856
+ : RepositoryStand.projectionVersion(stored?.version),
857
+ lifecycle: mode === "rebuild" && stored?.deleted
858
+ ? { archived: false, deleted: false }
859
+ : { archived: stored?.archived ?? false, deleted: stored?.deleted ?? false },
860
+ });
861
+ const storageInput = RepositoryStorage.entityStorageInput(this.#repository, RepositoryTenants.storageContextForTenant(this.#runtime.context, options.tenantId));
862
+ const storage = RepositoryStorage.openRepositoryEntityStorage(this.#repository, this.#runtime.storageFactory, storageInput);
863
+ RepositoryHistoryInternals.bindEntityHistory(entity, storage, entityId, this.#repository.stateSchema);
864
+ return Object.freeze({
865
+ commits: storage.commits,
866
+ current: stored === undefined
867
+ ? undefined
868
+ : EntityRecords.pack(this.#repository.stateSchema, entityId, stored.state, stored.versionMessage, { archived: stored.archived, deleted: stored.deleted }),
869
+ entity,
870
+ storageInput,
871
+ });
872
+ }
873
+ #defaultState(entityId) {
874
+ return create(this.#repository.stateSchema, {
875
+ [this.#repository.idField.localName]: entityId,
876
+ });
877
+ }
878
+ }
879
+ const ProcessManagerQueries = Object.freeze({
880
+ bind(entity, runtime, actorContext, tenantId) {
881
+ const context = actorContext === undefined
882
+ ? create(ActorContextSchema)
883
+ : clone(ActorContextSchema, actorContext);
884
+ if (tenantId !== undefined) {
885
+ context.tenantId = RepositoryTenants.require(tenantId);
886
+ }
887
+ return processManagerQueryAccess.bind(entity, async (plan, schema, query) => {
888
+ const results = await QueryReader.read(runtime.stand, schema, plan, tenantId, 10_000, query);
889
+ return Object.freeze(results.map((result) => clone(schema, result.state)));
890
+ }, context);
891
+ },
892
+ });
893
+ class ProcessManagerExecutionSupport {
894
+ #repository;
895
+ #runtime;
896
+ constructor(repository, runtime) {
897
+ this.#repository = repository;
898
+ this.#runtime = runtime;
899
+ }
900
+ normalizeProducedSignals(produced) {
901
+ if (produced === undefined) {
902
+ return Object.freeze([]);
903
+ }
904
+ if (Array.isArray(produced)) {
905
+ return Object.freeze(Array.from(produced));
906
+ }
907
+ return Object.freeze([produced]);
908
+ }
909
+ async load(entityId, options) {
910
+ const stored = await standAccess.readCurrent(this.#runtime.stand, this.#repository.stateSchema, entityId, options);
911
+ const entityType = this.#repository.entityType;
912
+ const entity = new entityType({
913
+ id: entityId,
914
+ schema: this.#repository.stateSchema,
915
+ state: stored === undefined ? this.#defaultState(entityId) : stored.state,
916
+ version: RepositoryStand.projectionVersion(stored?.version),
917
+ lifecycle: { archived: stored?.archived ?? false, deleted: stored?.deleted ?? false },
918
+ });
919
+ const storageInput = RepositoryStorage.entityStorageInput(this.#repository, RepositoryTenants.storageContextForTenant(this.#runtime.context, options.tenantId));
920
+ const storage = RepositoryStorage.openRepositoryEntityStorage(this.#repository, this.#runtime.storageFactory, storageInput);
921
+ RepositoryHistoryInternals.bindEntityHistory(entity, storage, entityId, this.#repository.stateSchema);
922
+ return Object.freeze({
923
+ commits: storage.commits,
924
+ current: stored === undefined
925
+ ? undefined
926
+ : EntityRecords.pack(this.#repository.stateSchema, entityId, stored.state, stored.versionMessage, { archived: stored.archived, deleted: stored.deleted }),
927
+ entity,
928
+ storageInput,
929
+ });
930
+ }
931
+ async commit(loaded, options, createdAt, events) {
932
+ const changed = RepositoryEntities.repositoryChanged(loaded.entity);
933
+ if (!changed && events.length === 0)
934
+ return true;
935
+ const entityId = loaded.entity.id;
936
+ const state = RepositoryEntities.repositoryState(loaded.entity);
937
+ const lifecycle = RepositoryEntities.repositoryLifecycle(loaded.entity);
938
+ const version = changed
939
+ ? BigInt(RepositoryStand.processManagerVersion(loaded.entity))
940
+ : BigInt(loaded.current?.version?.number ?? 0);
941
+ const deferred = changed
942
+ ? await standAccess.deferUpdate(this.#runtime.stand, this.#repository.stateSchema, state, RepositoryStand.standUpdateOptions(options.tenantId, create(VersionSchema, { number: Number(version) }), lifecycle))
943
+ : undefined;
944
+ try {
945
+ const history = RepositoryStorage.historyConfiguration(this.#repository);
946
+ const outcome = await loaded.commits.commit({
947
+ context: loaded.storageInput.context,
948
+ entity: loaded.storageInput,
949
+ entityId,
950
+ ...(loaded.current === undefined ? {} : { expected: loaded.current }),
951
+ next: EntityRecords.pack(this.#repository.stateSchema, entityId, state, version, lifecycle),
952
+ ...(changed && history.stateHistory
953
+ ? {
954
+ states: [
955
+ EntityRecords.pack(this.#repository.stateSchema, entityId, state, create(VersionSchema, { number: Number(version), timestamp: createdAt }), lifecycle),
956
+ ],
957
+ }
958
+ : {}),
959
+ ...(history.processManagerEventHistory
960
+ ? {
961
+ diagnostics: events.map((event) => clone(EventSchema, event)),
962
+ }
963
+ : {}),
964
+ });
965
+ if (outcome !== "committed") {
966
+ deferred?.cancel();
967
+ throw new Error("Concurrent Process Manager state commit conflict.");
968
+ }
969
+ entityStateHistoryCaches.get(loaded.entity)?.clear();
970
+ }
971
+ catch (error) {
972
+ deferred?.cancel();
973
+ throw error;
974
+ }
975
+ try {
976
+ deferred?.notify();
977
+ }
978
+ catch (error) {
979
+ this.#runtime.publisher.reportFailure("event", events[events.length - 1] ?? create(EventSchema), error);
980
+ }
981
+ return true;
982
+ }
983
+ #defaultState(entityId) {
984
+ return create(this.#repository.stateSchema, {
985
+ [this.#repository.idField.localName]: entityId,
986
+ });
987
+ }
988
+ }
989
+ class ProcessManagerCommandExecution {
990
+ #repository;
991
+ #routing;
992
+ #runtime;
993
+ #command;
994
+ #support;
995
+ constructor(repository, routing, runtime, command) {
996
+ this.#repository = repository;
997
+ this.#routing = routing;
998
+ this.#runtime = runtime;
999
+ this.#command = command;
1000
+ this.#support = new ProcessManagerExecutionSupport(repository, runtime);
1001
+ }
1002
+ async run(replayedRoute) {
1003
+ RepositorySignals.requireCommandId(this.#command);
1004
+ const intake = this.#readIntake(replayedRoute);
1005
+ if (intake === undefined)
1006
+ return undefined;
1007
+ const tenantOptions = RepositoryTenants.commandStandOptions(this.#runtime.context, this.#command);
1008
+ const loaded = await this.#support.load(intake.route.entityId, tenantOptions);
1009
+ this.#publishDispatch(intake.route.entityId);
1010
+ try {
1011
+ const produced = await this.#invoke(loaded.entity, intake.assignee, intake.message, tenantOptions.tenantId);
1012
+ return await this.#commitAndPublish(loaded, tenantOptions, intake, produced);
1013
+ }
1014
+ catch (error) {
1015
+ if (!RejectionThrowable.is(error))
1016
+ throw error;
1017
+ RepositorySignals.requireDeclaredRejection(intake.assignee.handler, error);
1018
+ return RepositorySignals.postRejectionEvent(this.#runtime, this.#repository, this.#command, intake.route.entityId, error);
1019
+ }
1020
+ }
1021
+ #publishDispatch(entityId) {
1022
+ HandlerDispatchPublisher.command(this.#runtime, this.#repository, this.#command, entityId);
1023
+ }
1024
+ #readIntake(replayedRoute) {
1025
+ const commandMessage = EntityInvocation.requireSignalMessage(this.#command.message, "command");
1026
+ const commandSchema = RepositoryRoutes.schemaForTypeUrl(this.#routing.commandSchemas, commandMessage.typeUrl, "command");
1027
+ const message = EntityInvocation.unpackRequired(commandMessage, commandSchema, "command");
1028
+ const route = replayedRoute ?? this.#repository.routeCommand(this.#command);
1029
+ const assignee = this.#routing.commandReadiness?.findCommandAssignee(route.messageFullTypeName);
1030
+ return assignee === undefined ? undefined : Object.freeze({ assignee, message, route });
1031
+ }
1032
+ async #commitAndPublish(loaded, tenantOptions, intake, producedSignals) {
1033
+ const commands = this.#commandOutputs(intake.assignee, producedSignals);
1034
+ const events = this.#eventOutputs(intake.assignee, producedSignals, intake.route.entityId);
1035
+ const committed = await this.#support.commit(loaded, tenantOptions, RepositorySignals.executionTimestamp(), events);
1036
+ if (!committed)
1037
+ return undefined;
1038
+ this.#publishChangedState(loaded, intake.route.entityId);
1039
+ this.#postEvents(events);
1040
+ return commands.length === 0
1041
+ ? undefined
1042
+ : async () => {
1043
+ await this.#postCommands(commands, this.#command);
1044
+ };
1045
+ }
1046
+ #commandOutputs(assignee, produced) {
1047
+ if (assignee.handler.kind !== "command-substitution")
1048
+ return Object.freeze([]);
1049
+ const commands = this.#bindProducedCommands(produced);
1050
+ if (commands.length === 0) {
1051
+ throw new Error("Repository Process Manager command substitutions must return at least one command.");
1052
+ }
1053
+ return commands;
1054
+ }
1055
+ #eventOutputs(assignee, produced, entityId) {
1056
+ return assignee.handler.kind === "command-assignment"
1057
+ ? this.#bindProducedEvents(produced, entityId)
1058
+ : Object.freeze([]);
1059
+ }
1060
+ #publishChangedState(loaded, entityId) {
1061
+ if (RepositoryEntities.repositoryChanged(loaded.entity)) {
1062
+ EntityStateChangePublisher.command(this.#runtime, this.#repository, this.#command, entityId, loaded.current === undefined
1063
+ ? undefined
1064
+ : EntityRecords.unpack(this.#repository.stateSchema, loaded.current).state, loaded.current === undefined
1065
+ ? undefined
1066
+ : {
1067
+ archived: loaded.current.lifecycleFlags?.archived ?? false,
1068
+ deleted: loaded.current.lifecycleFlags?.deleted ?? false,
1069
+ }, RepositoryEntities.repositoryState(loaded.entity), RepositoryEntities.repositoryLifecycle(loaded.entity), RepositoryStand.processManagerVersion(loaded.entity));
1070
+ }
1071
+ }
1072
+ async #invoke(entity, assignee, message, tenantId) {
1073
+ const releaseQuery = ProcessManagerQueries.bind(entity, this.#runtime, this.#command.context?.actorContext, tenantId);
1074
+ try {
1075
+ return await this.#invokeCommandHandler(entity, assignee, message);
1076
+ }
1077
+ finally {
1078
+ releaseQuery();
1079
+ }
1080
+ }
1081
+ async #invokeCommandHandler(entity, assignee, message) {
1082
+ transactionalEntityAccess.start(entity);
1083
+ try {
1084
+ const produced = await EntityInvocation.invokeEntityMethod(entity, assignee.handler.methodName, message, assignee.handler.parameterCount, EntityInvocation.commandHandlerContext(this.#command));
1085
+ const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
1086
+ if (commit.status === "rejected") {
1087
+ throw new TransitionValidationError(commit.validation.error);
1088
+ }
1089
+ return this.#support.normalizeProducedSignals(produced);
1090
+ }
1091
+ catch (error) {
1092
+ transactionalEntityAccess.rollback(entity);
1093
+ throw error;
1094
+ }
1095
+ }
1096
+ #bindProducedEvents(produced, entityId) {
1097
+ let sequence = 0;
1098
+ return Object.freeze(produced.map((signal) => {
1099
+ sequence += 1;
1100
+ return this.#bindProducedEvent(signal, entityId, sequence);
1101
+ }));
1102
+ }
1103
+ #bindProducedEvent(signal, entityId, sequence) {
1104
+ const typeName = EntityInvocation.messageTypeName(signal);
1105
+ const schema = this.#routing.producedEventSchemas.find((candidate) => candidate.typeName === typeName);
1106
+ if (schema === undefined) {
1107
+ throw new Error(`Repository process-manager execution cannot pack event message "${typeName}".`);
1108
+ }
1109
+ const metadata = this.#runtime.signalMetadata.eventFromCommand(this.#command, {
1110
+ version: RepositoryStand.processManagerProducedVersion(sequence),
1111
+ });
1112
+ return create(EventSchema, {
1113
+ id: metadata.id,
1114
+ message: AnyMessages.pack(schema, signal),
1115
+ context: RepositorySignals.eventContextWithProducer(metadata.context, this.#repository, entityId),
1116
+ });
1117
+ }
1118
+ #bindProducedCommands(produced) {
1119
+ return Object.freeze(produced.map((signal) => {
1120
+ const typeName = EntityInvocation.messageTypeName(signal);
1121
+ const schema = this.#routing.producedCommandSchemas.find((candidate) => candidate.typeName === typeName);
1122
+ if (schema === undefined) {
1123
+ throw new Error(`Repository process-manager execution cannot pack command message "${typeName}".`);
1124
+ }
1125
+ const metadata = this.#runtime.signalMetadata.commandFromCommand(this.#command);
1126
+ return create(CommandSchema, {
1127
+ id: metadata.id,
1128
+ message: AnyMessages.pack(schema, signal),
1129
+ context: metadata.context,
1130
+ });
1131
+ }));
1132
+ }
1133
+ async #postCommands(commands, source) {
1134
+ void source;
1135
+ for (const command of commands) {
1136
+ await this.#runtime.publisher.publishCommand(command);
1137
+ }
1138
+ }
1139
+ #postEvents(events) {
1140
+ for (const event of events) {
1141
+ // spine-log-boundary: server.repository_event_follow_up
1142
+ void this.#runtime.publisher.publishEvent(event);
1143
+ }
1144
+ }
1145
+ }
1146
+ class ProcessManagerEventExecution {
1147
+ #repository;
1148
+ #routing;
1149
+ #runtime;
1150
+ #event;
1151
+ #support;
1152
+ constructor(repository, routing, runtime, event) {
1153
+ this.#repository = repository;
1154
+ this.#routing = routing;
1155
+ this.#runtime = runtime;
1156
+ this.#event = event;
1157
+ this.#support = new ProcessManagerExecutionSupport(repository, runtime);
1158
+ }
1159
+ async run(acceptedRoute) {
1160
+ const intake = this.#readIntake(acceptedRoute);
1161
+ if (intake.reactors.length === 0 && intake.commanders.length === 0) {
1162
+ return;
1163
+ }
1164
+ this.#validateSourceEventIdForFollowUps(intake);
1165
+ if (intake.route.entityIds.length === 1) {
1166
+ await InboxHandoff.handoffPmEvent(this.#repository, this.#runtime, this.#event, intake.route.entityIds[0]);
1167
+ return;
1168
+ }
1169
+ await InboxHandoff.handoffPmEvents(this.#repository, this.#runtime, this.#event, intake.route.entityIds);
1170
+ }
1171
+ async runTarget(entityId, acceptedRoute) {
1172
+ const intake = this.#readIntake(acceptedRoute);
1173
+ if (intake.reactors.length === 0 && intake.commanders.length === 0) {
1174
+ return;
1175
+ }
1176
+ this.#validateSourceEventIdForFollowUps(intake);
1177
+ await DispatchGuards.guardedEntityEventDispatch(this.#repository, this.#runtime, this.#event, entityId, async () => {
1178
+ await this.#executeEntity(entityId, intake);
1179
+ });
1180
+ }
1181
+ #readIntake(acceptedRoute) {
1182
+ const eventMessage = EntityInvocation.requireSignalMessage(this.#event.message, "event");
1183
+ const eventSchema = RepositoryRoutes.schemaForTypeUrl(this.#routing.eventSchemas, eventMessage.typeUrl, "event");
1184
+ const message = EntityInvocation.unpackRequired(eventMessage, eventSchema, "event");
1185
+ const route = acceptedRoute;
1186
+ const reactors = this.#routing.eventReactors(route.messageFullTypeName, message, this.#event.context?.external === true);
1187
+ const commanders = this.#routing.commandReactions(route.messageFullTypeName, message, this.#event.context?.external === true);
1188
+ return Object.freeze({
1189
+ message,
1190
+ route,
1191
+ reactors: Object.freeze([...reactors]),
1192
+ commanders,
1193
+ });
1194
+ }
1195
+ async #executeEntity(entityId, intake) {
1196
+ const tenantOptions = RepositoryTenants.standTenantOptions(this.#runtime.context, this.#event);
1197
+ const loaded = await this.#support.load(entityId, tenantOptions);
1198
+ const produced = await this.#invokeHandlers(entityId, loaded.entity, intake, tenantOptions.tenantId);
1199
+ const events = this.#bindProducedEvents(produced.events, entityId);
1200
+ const diagnostics = [
1201
+ DispatchGuards.guardedJournalEvent(this.#repository, this.#event, entityId),
1202
+ ...events,
1203
+ ];
1204
+ const committed = await this.#support.commit(loaded, tenantOptions, this.#event.context?.timestamp ?? create(TimestampSchema), diagnostics);
1205
+ if (!committed)
1206
+ return;
1207
+ this.#publishChangedState(loaded, entityId);
1208
+ this.#postEvents(events);
1209
+ await this.#postCommands(this.#bindProducedCommands(produced.commands));
1210
+ }
1211
+ #publishChangedState(loaded, entityId) {
1212
+ if (!RepositoryEntities.repositoryChanged(loaded.entity))
1213
+ return;
1214
+ EntityStateChangePublisher.event(this.#runtime, this.#repository, this.#event, entityId, loaded.current === undefined
1215
+ ? undefined
1216
+ : EntityRecords.unpack(this.#repository.stateSchema, loaded.current).state, loaded.current === undefined
1217
+ ? undefined
1218
+ : {
1219
+ archived: loaded.current.lifecycleFlags?.archived ?? false,
1220
+ deleted: loaded.current.lifecycleFlags?.deleted ?? false,
1221
+ }, RepositoryEntities.repositoryState(loaded.entity), RepositoryEntities.repositoryLifecycle(loaded.entity), RepositoryStand.processManagerVersion(loaded.entity));
1222
+ }
1223
+ #validateSourceEventIdForFollowUps(intake) {
1224
+ const emitsFollowUpEvents = intake.reactors.some((reactor) => RepositoryHandlers.handlerEmittedSchemas(reactor.handler).length > 0);
1225
+ if (!emitsFollowUpEvents && intake.commanders.length === 0) {
1226
+ return;
1227
+ }
1228
+ void this.#runtime.signalMetadata.originFromEvent(this.#event);
1229
+ }
1230
+ async #invokeHandlers(entityId, entity, intake, tenantId) {
1231
+ const eventContext = EntityInvocation.eventHandlerContext(this.#event);
1232
+ const commands = [];
1233
+ const events = [];
1234
+ const releaseQuery = ProcessManagerQueries.bind(entity, this.#runtime, this.#runtime.signalMetadata.originFromEvent(this.#event).actorContext, tenantId);
1235
+ transactionalEntityAccess.start(entity);
1236
+ try {
1237
+ await this.#invokeEventHandlers(entityId, entity, intake, eventContext, events, commands);
1238
+ await this.#commitEntity(entity);
1239
+ return Object.freeze({
1240
+ commands: Object.freeze(commands),
1241
+ events: Object.freeze(events),
1242
+ });
1243
+ }
1244
+ catch (error) {
1245
+ transactionalEntityAccess.rollback(entity);
1246
+ throw error;
1247
+ }
1248
+ finally {
1249
+ releaseQuery();
1250
+ }
1251
+ }
1252
+ async #invokeEventHandlers(entityId, entity, intake, context, events, commands) {
1253
+ if (intake.reactors.length > 0)
1254
+ HandlerDispatchPublisher.reactor(this.#runtime, this.#repository, this.#event, entityId);
1255
+ await this.#invokeHandlersInto(entity, intake.reactors, intake.message, context, events);
1256
+ await this.#invokeHandlersInto(entity, intake.commanders, intake.message, context, commands);
1257
+ }
1258
+ async #invokeHandlersInto(entity, handlers, message, context, output) {
1259
+ for (const handler of handlers) {
1260
+ const produced = await EntityInvocation.invokeEntityMethod(entity, handler.handler.methodName, message, handler.handler.parameterCount, context);
1261
+ output.push(...this.#support.normalizeProducedSignals(produced));
1262
+ }
1263
+ }
1264
+ async #commitEntity(entity) {
1265
+ const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
1266
+ if (commit.status === "rejected")
1267
+ throw new TransitionValidationError(commit.validation.error);
1268
+ }
1269
+ #bindProducedEvents(produced, entityId) {
1270
+ let sequence = 0;
1271
+ return Object.freeze(produced.map((signal) => {
1272
+ sequence += 1;
1273
+ return this.#bindProducedEvent(signal, entityId, sequence);
1274
+ }));
1275
+ }
1276
+ #bindProducedEvent(signal, entityId, sequence) {
1277
+ const typeName = EntityInvocation.messageTypeName(signal);
1278
+ const schema = this.#routing.producedEventSchemas.find((candidate) => candidate.typeName === typeName);
1279
+ if (schema === undefined) {
1280
+ throw new Error(`Repository process-manager execution cannot pack event message "${typeName}".`);
1281
+ }
1282
+ const metadata = this.#runtime.signalMetadata.eventFromEvent(this.#event, {
1283
+ version: RepositoryStand.processManagerProducedVersion(sequence),
1284
+ });
1285
+ return create(EventSchema, {
1286
+ id: metadata.id,
1287
+ message: AnyMessages.pack(schema, signal),
1288
+ context: RepositorySignals.eventContextWithProducer(metadata.context, this.#repository, entityId),
1289
+ });
1290
+ }
1291
+ #bindProducedCommands(produced) {
1292
+ return Object.freeze(produced.map((signal) => {
1293
+ const typeName = EntityInvocation.messageTypeName(signal);
1294
+ const schema = this.#routing.producedCommandSchemas.find((candidate) => candidate.typeName === typeName);
1295
+ if (schema === undefined) {
1296
+ throw new Error(`Repository process-manager execution cannot pack command message "${typeName}".`);
1297
+ }
1298
+ const metadata = this.#runtime.signalMetadata.commandFromEvent(this.#event);
1299
+ return create(CommandSchema, {
1300
+ id: metadata.id,
1301
+ message: AnyMessages.pack(schema, signal),
1302
+ context: metadata.context,
1303
+ });
1304
+ }));
1305
+ }
1306
+ async #postCommands(commands) {
1307
+ for (const command of commands) {
1308
+ await this.#runtime.publisher.publishCommand(command);
1309
+ }
1310
+ }
1311
+ #postEvents(events) {
1312
+ for (const event of events) {
1313
+ // spine-log-boundary: server.process_manager_event_follow_up
1314
+ void this.#runtime.publisher.publishEvent(event);
1315
+ }
1316
+ }
1317
+ }
1318
+ /**
1319
+ * Internal repository identity operations.
1320
+ */
1321
+ const RepositoryIdentity = {
1322
+ createRepositorySnapshot(entityType, entityFamily, metadata) {
1323
+ const metadataCopy = RepositoryIdentity.cloneEntityMetadata(metadata);
1324
+ return Object.freeze({
1325
+ entityType,
1326
+ entityFamily,
1327
+ stateSchema: metadataCopy.schema,
1328
+ metadata: metadataCopy,
1329
+ stateFullTypeName: metadataCopy.fullTypeName,
1330
+ idField: RepositoryIdentity.cloneFieldMetadata(metadataCopy.idField),
1331
+ });
1332
+ },
1333
+ cloneRepositorySnapshot(snapshot) {
1334
+ const metadata = RepositoryIdentity.cloneEntityMetadata(snapshot.metadata);
1335
+ return Object.freeze({
1336
+ entityType: snapshot.entityType,
1337
+ entityFamily: snapshot.entityFamily,
1338
+ stateSchema: metadata.schema,
1339
+ metadata,
1340
+ stateFullTypeName: metadata.fullTypeName,
1341
+ idField: RepositoryIdentity.cloneFieldMetadata(metadata.idField),
1342
+ });
1343
+ },
1344
+ isRepositoryOptionsObject(options) {
1345
+ return typeof options === "object" && options !== null;
1346
+ },
1347
+ readEntityTypeOption(options) {
1348
+ try {
1349
+ return options.entityType;
1350
+ }
1351
+ catch {
1352
+ throw new RepositoryIdentityError("UNSUPPORTED_ENTITY_TYPE", "Repository options entityType must be readable and resolve to a class constructor " +
1353
+ "extending Aggregate, Projection, or ProcessManager.");
1354
+ }
1355
+ },
1356
+ readRepositorySchemaOption(options, entityTypeDisplayName, entityFamily) {
1357
+ try {
1358
+ return options.schema;
1359
+ }
1360
+ catch {
1361
+ throw new RepositoryIdentityError("ENTITY_SCHEMA_KIND_MISMATCH", `Repository entity type "${entityTypeDisplayName}" is a ${entityFamily}, but ` +
1362
+ "the supplied state schema could not be read.");
1363
+ }
1364
+ },
1365
+ isClassConstructor(entityType) {
1366
+ if (typeof entityType !== "function") {
1367
+ return false;
1368
+ }
1369
+ try {
1370
+ const source = Function.prototype.toString.call(entityType);
1371
+ return source.startsWith("class ");
1372
+ }
1373
+ catch {
1374
+ return false;
1375
+ }
1376
+ },
1377
+ /**
1378
+ * Resolves the runtime family of a repository-owned entity constructor.
1379
+ *
1380
+ * @internal
1381
+ */
1382
+ resolveRepositoryEntityFamily(entityType) {
1383
+ if (typeof entityType !== "function" || !RepositoryIdentity.isClassConstructor(entityType)) {
1384
+ return undefined;
1385
+ }
1386
+ const runtimeEntityType = entityType;
1387
+ if (RepositoryIdentity.hasEntityFamilyInheritance(runtimeEntityType, Aggregate, Aggregate.prototype)) {
1388
+ return "aggregate";
1389
+ }
1390
+ if (RepositoryIdentity.hasEntityFamilyInheritance(runtimeEntityType, Projection, Projection.prototype)) {
1391
+ return "projection";
1392
+ }
1393
+ if (RepositoryIdentity.hasEntityFamilyInheritance(runtimeEntityType, ProcessManager, ProcessManager.prototype)) {
1394
+ return "process-manager";
1395
+ }
1396
+ return undefined;
1397
+ },
1398
+ hasEntityFamilyInheritance(entityType, familyConstructor, familyPrototype) {
1399
+ try {
1400
+ return (Object.prototype.isPrototypeOf.call(familyConstructor, entityType) &&
1401
+ Object.prototype.isPrototypeOf.call(familyPrototype, entityType.prototype));
1402
+ }
1403
+ catch {
1404
+ return false;
1405
+ }
1406
+ },
1407
+ entityTypeName(entityType) {
1408
+ if ((typeof entityType !== "object" && typeof entityType !== "function") ||
1409
+ entityType === null) {
1410
+ return "(anonymous)";
1411
+ }
1412
+ const name = RepositoryIdentity.safeStringProperty(entityType, "name");
1413
+ return typeof name === "string" && name.length > 0 ? name : "(anonymous)";
1414
+ },
1415
+ safeStringProperty(value, propertyName) {
1416
+ try {
1417
+ const property = value[propertyName];
1418
+ return typeof property === "string" ? property : undefined;
1419
+ }
1420
+ catch {
1421
+ return undefined;
1422
+ }
1423
+ },
1424
+ describeRepositoryEntityMetadata(entityTypeDisplayName, entityFamily, schema) {
1425
+ try {
1426
+ return describeEntityMetadata(schema);
1427
+ }
1428
+ catch {
1429
+ throw new RepositoryIdentityError("ENTITY_SCHEMA_KIND_MISMATCH", `Repository entity type "${entityTypeDisplayName}" is a ${entityFamily}, but ` +
1430
+ "the supplied state schema does not expose supported entity metadata.");
1431
+ }
1432
+ },
1433
+ cloneEntityMetadata(metadata) {
1434
+ const idField = RepositoryIdentity.cloneFieldMetadata(metadata.idField);
1435
+ const firstFieldRoutingHint = Object.freeze({
1436
+ strategy: metadata.firstFieldRoutingHint.strategy,
1437
+ field: RepositoryIdentity.cloneFieldMetadata(metadata.firstFieldRoutingHint.field),
1438
+ });
1439
+ return Object.freeze({
1440
+ schema: metadata.schema,
1441
+ descriptor: metadata.descriptor,
1442
+ fullTypeName: metadata.fullTypeName,
1443
+ fileDescriptor: metadata.fileDescriptor,
1444
+ fileName: metadata.fileName,
1445
+ kind: metadata.kind,
1446
+ declaredVisibility: metadata.declaredVisibility,
1447
+ visibility: metadata.visibility,
1448
+ visibilitySource: metadata.visibilitySource,
1449
+ idField,
1450
+ firstFieldRoutingHint,
1451
+ columns: Object.freeze(metadata.columns.map((field) => RepositoryIdentity.cloneFieldMetadata(field))),
1452
+ setOnceFields: Object.freeze(metadata.setOnceFields.map((field) => RepositoryIdentity.cloneFieldMetadata(field))),
1453
+ });
1454
+ },
1455
+ cloneFieldMetadata(field) {
1456
+ return Object.freeze({
1457
+ descriptor: field.descriptor,
1458
+ name: field.name,
1459
+ localName: field.localName,
1460
+ jsonName: field.jsonName,
1461
+ number: field.number,
1462
+ });
1463
+ },
1464
+ };
1465
+ Object.freeze(RepositoryIdentity);
1466
+ /**
1467
+ * Internal entity invocation operations.
1468
+ */
1469
+ const EntityInvocation = {
1470
+ isEventEnvelope(signal) {
1471
+ return (typeof signal === "object" &&
1472
+ signal !== null &&
1473
+ signal.$typeName === EventSchema.typeName);
1474
+ },
1475
+ messageTypeName(message) {
1476
+ const typeName = message.$typeName;
1477
+ if (typeof typeName !== "string" || typeName.length === 0) {
1478
+ throw new Error("Repository aggregate execution requires a generated event message.");
1479
+ }
1480
+ return typeName;
1481
+ },
1482
+ invokeEntityMethod(entity, methodName, message, parameterCount = 1, context) {
1483
+ const method = entity[methodName];
1484
+ if (typeof method !== "function") {
1485
+ throw new TypeError(`Repository entity execution requires method "${methodName}".`);
1486
+ }
1487
+ return Reflect.apply(method, entity, parameterCount === 2 ? [message, context] : [message]);
1488
+ },
1489
+ commandHandlerContext(command) {
1490
+ return command.context === undefined
1491
+ ? create(CommandContextSchema)
1492
+ : clone(CommandContextSchema, command.context);
1493
+ },
1494
+ eventHandlerContext(event) {
1495
+ return event.context === undefined
1496
+ ? create(EventContextSchema)
1497
+ : clone(EventContextSchema, event.context);
1498
+ },
1499
+ unpackRequired(message, schema, signalKind) {
1500
+ const unpacked = AnyMessages.unpack(message, schema);
1501
+ if (unpacked === undefined) {
1502
+ throw new Error(`Repository ${signalKind} execution requires a readable message.`);
1503
+ }
1504
+ return unpacked;
1505
+ },
1506
+ requireSignalMessage(message, signalKind) {
1507
+ if (message === undefined || message.typeUrl === "") {
1508
+ throw new Error(`Repository ${signalKind} execution requires message.typeUrl.`);
1509
+ }
1510
+ return message;
1511
+ },
1512
+ };
1513
+ Object.freeze(EntityInvocation);
1514
+ /**
1515
+ * Internal repository entities operations.
1516
+ */
1517
+ const RepositoryEntities = {
1518
+ repositoryState(entity) {
1519
+ return entity.state;
1520
+ },
1521
+ repositoryLifecycle(entity) {
1522
+ return entity.lifecycle;
1523
+ },
1524
+ repositoryChanged(entity) {
1525
+ return entity.changed === true;
1526
+ },
1527
+ };
1528
+ Object.freeze(RepositoryEntities);
1529
+ /**
1530
+ * Internal repository signals operations.
1531
+ */
1532
+ const RepositorySignals = {
1533
+ requireDeclaredRejection(handler, rejection) {
1534
+ DeclaredRejections.require(handler.methodName, HandlerMetadataValues.thrownSchemas(handler), rejection);
1535
+ },
1536
+ readEventVersion(event) {
1537
+ const number = event.context?.version?.number;
1538
+ if (number === undefined) {
1539
+ throw new Error("Repository aggregate execution requires readable event versions.");
1540
+ }
1541
+ return BigInt(number);
1542
+ },
1543
+ eventVersionNumber(version) {
1544
+ if (version > 2147483647n || version < -2147483648n) {
1545
+ throw new Error("Repository aggregate execution requires versions in the protobuf int32 range.");
1546
+ }
1547
+ return Number(version);
1548
+ },
1549
+ eventContextWithProducer(context, repository, entityId) {
1550
+ const producerId = EntityIds.pack(repository.stateSchema, entityId);
1551
+ return create(EventContextSchema, { ...context, producerId });
1552
+ },
1553
+ postRejectionEvent(runtime, repository, command, entityId, rejection) {
1554
+ const metadata = runtime.signalMetadata.eventFromCommand(command, {});
1555
+ const event = create(EventSchema, {
1556
+ id: metadata.id,
1557
+ message: AnyMessages.pack(rejection.schema, rejection.messageThrown()),
1558
+ context: create(EventContextSchema, {
1559
+ ...RepositorySignals.eventContextWithProducer(metadata.context, repository, entityId),
1560
+ rejection: create(RejectionEventContextSchema, {
1561
+ command: clone(CommandSchema, command),
1562
+ stacktrace: rejection.stack ?? "",
1563
+ }),
1564
+ }),
1565
+ });
1566
+ return async () => {
1567
+ try {
1568
+ // spine-log-boundary: server.repository_rejection_follow_up
1569
+ await runtime.publisher.publishRejectionEvent(event);
1570
+ }
1571
+ catch (error) {
1572
+ runtime.publisher.reportFailure("event", event, error);
1573
+ }
1574
+ };
1575
+ },
1576
+ requireCommandId(command) {
1577
+ if (command.id === undefined || command.id.uuid.trim().length === 0) {
1578
+ throw new Error("Repository aggregate execution requires command.id to bind event origins.");
1579
+ }
1580
+ return command.id;
1581
+ },
1582
+ requireEventId(event) {
1583
+ if (event.id === undefined || event.id.value.trim().length === 0) {
1584
+ throw new Error("Repository projection inbox handoff requires event.id.");
1585
+ }
1586
+ return event.id;
1587
+ },
1588
+ executionTimestamp() {
1589
+ const milliseconds = Date.now();
1590
+ return create(TimestampSchema, {
1591
+ seconds: BigInt(Math.floor(milliseconds / 1_000)),
1592
+ nanos: (milliseconds % 1_000) * 1_000_000,
1593
+ });
1594
+ },
1595
+ };
1596
+ /**
1597
+ * Builds and best-effort dispatches committed entity state notifications.
1598
+ */
1599
+ class EntityStateChangePublishing {
1600
+ command(runtime, repository, command, entityId, oldState, oldLifecycle, newState, lifecycle, version) {
1601
+ this.#publish(runtime, () => runtime.signalMetadata.eventFromCommand(command, {
1602
+ version,
1603
+ }), {
1604
+ id: AnyMessages.pack(CommandIdSchema, command.id),
1605
+ typeUrl: command.message?.typeUrl ?? "",
1606
+ }, { repository, entityId, oldState, oldLifecycle, newState, lifecycle, version });
1607
+ }
1608
+ event(runtime, repository, source, entityId, oldState, oldLifecycle, newState, lifecycle, version) {
1609
+ this.#publish(runtime, () => runtime.signalMetadata.eventFromEvent(source, {
1610
+ version,
1611
+ }), {
1612
+ id: AnyMessages.pack(EventIdSchema, source.id),
1613
+ typeUrl: source.message?.typeUrl ?? "",
1614
+ }, { repository, entityId, oldState, oldLifecycle, newState, lifecycle, version });
1615
+ }
1616
+ #publish(runtime, metadataFor, origin, change) {
1617
+ const drafts = this.#drafts(origin, change);
1618
+ drafts.forEach((draft) => {
1619
+ const metadata = metadataFor();
1620
+ runtime.registerSystemEventSchema(draft.schema);
1621
+ const event = create(EventSchema, {
1622
+ id: metadata.id,
1623
+ message: AnyMessages.pack(draft.schema, draft.messageAt(metadata.context.timestamp)),
1624
+ context: RepositorySignals.eventContextWithProducer(metadata.context, change.repository, change.entityId),
1625
+ });
1626
+ this.#post(runtime, event);
1627
+ });
1628
+ }
1629
+ #drafts(origin, change) {
1630
+ const fields = this.#fields(origin, change);
1631
+ const archive = this.#archiveDraft(fields, change);
1632
+ const deletion = this.#deleteDraft(fields, change);
1633
+ return [...this.#stateDrafts(fields, change), archive, deletion].filter((draft) => draft !== undefined);
1634
+ }
1635
+ #fields(origin, change) {
1636
+ const entity = create(MessageIdSchema, {
1637
+ id: this.#packEntityId(change.repository, change.entityId),
1638
+ typeUrl: TypeUrls.derive(change.repository.stateSchema),
1639
+ });
1640
+ return {
1641
+ entity,
1642
+ signalId: [create(MessageIdSchema, { id: origin.id, typeUrl: origin.typeUrl })],
1643
+ state: AnyMessages.pack(change.repository.stateSchema, change.newState),
1644
+ version: create(VersionSchema, { number: change.version }),
1645
+ };
1646
+ }
1647
+ #stateDrafts(fields, change) {
1648
+ const drafts = [];
1649
+ if (change.oldState === undefined) {
1650
+ drafts.push({
1651
+ schema: EntityLog.EntityCreatedSchema,
1652
+ messageAt: () => create(EntityLog.EntityCreatedSchema, {
1653
+ entity: fields.entity,
1654
+ kind: this.#kind(change.repository.metadata.kind),
1655
+ }),
1656
+ });
1657
+ }
1658
+ if (change.oldState === undefined ||
1659
+ !this.#sameState(change.repository.stateSchema, change.oldState, change.newState)) {
1660
+ drafts.push(this.#stateChangedDraft(fields, change));
1661
+ }
1662
+ return drafts;
1663
+ }
1664
+ #stateChangedDraft(fields, change) {
1665
+ return {
1666
+ schema: EntityLog.EntityStateChangedSchema,
1667
+ messageAt: (when) => create(EntityLog.EntityStateChangedSchema, {
1668
+ entity: fields.entity,
1669
+ ...(change.oldState === undefined
1670
+ ? {}
1671
+ : {
1672
+ oldState: AnyMessages.pack(change.repository.stateSchema, change.oldState),
1673
+ }),
1674
+ newState: fields.state,
1675
+ signalId: fields.signalId,
1676
+ when,
1677
+ newVersion: fields.version,
1678
+ }),
1679
+ };
1680
+ }
1681
+ #archiveDraft(fields, change) {
1682
+ const previous = change.oldLifecycle ?? { archived: false, deleted: false };
1683
+ if (previous.archived === change.lifecycle.archived)
1684
+ return undefined;
1685
+ return change.lifecycle.archived
1686
+ ? {
1687
+ schema: EntityLog.EntityArchivedSchema,
1688
+ messageAt: (when) => create(EntityLog.EntityArchivedSchema, {
1689
+ entity: fields.entity,
1690
+ signalId: fields.signalId,
1691
+ when,
1692
+ version: fields.version,
1693
+ lastState: fields.state,
1694
+ }),
1695
+ }
1696
+ : {
1697
+ schema: EntityLog.EntityUnarchivedSchema,
1698
+ messageAt: (when) => create(EntityLog.EntityUnarchivedSchema, {
1699
+ entity: fields.entity,
1700
+ signalId: fields.signalId,
1701
+ when,
1702
+ version: fields.version,
1703
+ state: fields.state,
1704
+ }),
1705
+ };
1706
+ }
1707
+ #deleteDraft(fields, change) {
1708
+ const previous = change.oldLifecycle ?? { archived: false, deleted: false };
1709
+ if (previous.deleted === change.lifecycle.deleted)
1710
+ return undefined;
1711
+ return change.lifecycle.deleted
1712
+ ? {
1713
+ schema: EntityLog.EntityDeletedSchema,
1714
+ messageAt: (when) => create(EntityLog.EntityDeletedSchema, {
1715
+ entity: fields.entity,
1716
+ signalId: fields.signalId,
1717
+ when,
1718
+ version: fields.version,
1719
+ deletion: { case: "markedAsDeleted", value: true },
1720
+ lastState: fields.state,
1721
+ }),
1722
+ }
1723
+ : {
1724
+ schema: EntityLog.EntityRestoredSchema,
1725
+ messageAt: (when) => create(EntityLog.EntityRestoredSchema, {
1726
+ entity: fields.entity,
1727
+ signalId: fields.signalId,
1728
+ when,
1729
+ version: fields.version,
1730
+ state: fields.state,
1731
+ }),
1732
+ };
1733
+ }
1734
+ #post(runtime, event) {
1735
+ try {
1736
+ // spine-log-boundary: server.repository_system_follow_up
1737
+ void runtime.publisher.publishSystemEvent(event);
1738
+ }
1739
+ catch (error) {
1740
+ runtime.publisher.reportFailure("system-event", event, error);
1741
+ }
1742
+ }
1743
+ #sameState(schema, left, right) {
1744
+ const leftBytes = toBinary(schema, left);
1745
+ const rightBytes = toBinary(schema, right);
1746
+ return (leftBytes.length === rightBytes.length &&
1747
+ leftBytes.every((value, index) => value === rightBytes[index]));
1748
+ }
1749
+ #kind(kind) {
1750
+ return kind === "aggregate"
1751
+ ? EntityOption_Kind.AGGREGATE
1752
+ : kind === "projection"
1753
+ ? EntityOption_Kind.PROJECTION
1754
+ : EntityOption_Kind.PROCESS_MANAGER;
1755
+ }
1756
+ #packEntityId(repository, entityId) {
1757
+ return EntityIds.pack(repository.stateSchema, entityId);
1758
+ }
1759
+ }
1760
+ const EntityStateChangePublisher = Object.freeze(new EntityStateChangePublishing());
1761
+ /**
1762
+ * Builds and best-effort dispatches accepted handler diagnostics.
1763
+ */
1764
+ class HandlerDispatchPublishing {
1765
+ command(runtime, repository, command, entityId) {
1766
+ try {
1767
+ const context = runtime.signalMetadata.eventContext({
1768
+ origin: runtime.signalMetadata.originFromCommand(command),
1769
+ });
1770
+ const event = create(EventSchema, {
1771
+ id: runtime.signalMetadata.eventId(),
1772
+ message: AnyMessages.pack(EntityLog.CommandDispatchedToHandlerSchema, this.#message(repository, command, entityId, context.timestamp)),
1773
+ context,
1774
+ });
1775
+ this.#post(runtime, EntityLog.CommandDispatchedToHandlerSchema, event);
1776
+ }
1777
+ catch (error) {
1778
+ runtime.publisher.reportFailure("event", create(EventSchema), error);
1779
+ }
1780
+ }
1781
+ subscriber(runtime, repository, event, entityId) {
1782
+ this.#publishEvent(runtime, repository, event, entityId, EntityLog.EventDispatchedToSubscriberSchema);
1783
+ }
1784
+ reactor(runtime, repository, event, entityId) {
1785
+ this.#publishEvent(runtime, repository, event, entityId, EntityLog.EventDispatchedToReactorSchema);
1786
+ }
1787
+ #publishEvent(runtime, repository, event, entityId, schema) {
1788
+ try {
1789
+ const context = runtime.signalMetadata.eventContext({
1790
+ origin: runtime.signalMetadata.originFromEvent(event),
1791
+ });
1792
+ const diagnostic = create(EventSchema, {
1793
+ id: runtime.signalMetadata.eventId(),
1794
+ message: AnyMessages.pack(schema, this.#eventMessage(schema, repository, event, entityId, context.timestamp)),
1795
+ context,
1796
+ });
1797
+ this.#post(runtime, schema, diagnostic);
1798
+ }
1799
+ catch (error) {
1800
+ runtime.publisher.reportFailure("event", create(EventSchema), error);
1801
+ }
1802
+ }
1803
+ #message(repository, command, entityId, whenDispatched) {
1804
+ return create(EntityLog.CommandDispatchedToHandlerSchema, {
1805
+ receiver: create(MessageIdSchema, {
1806
+ id: this.#packEntityId(repository, entityId),
1807
+ typeUrl: TypeUrls.derive(repository.stateSchema),
1808
+ }),
1809
+ payload: clone(CommandSchema, command),
1810
+ whenDispatched,
1811
+ entityType: create(EntityTypeNameSchema, {
1812
+ impl: { case: "javaClassName", value: repository.entityType.name },
1813
+ }),
1814
+ });
1815
+ }
1816
+ #post(runtime, schema, event) {
1817
+ try {
1818
+ runtime.registerSystemEventSchema(schema);
1819
+ // spine-log-boundary: server.repository_system_dispatch_follow_up
1820
+ void runtime.publisher.publishSystemEvent(event);
1821
+ }
1822
+ catch (error) {
1823
+ runtime.publisher.reportFailure("system-event", event, error);
1824
+ }
1825
+ }
1826
+ #packEntityId(repository, entityId) {
1827
+ return EntityIds.pack(repository.stateSchema, entityId);
1828
+ }
1829
+ #eventMessage(schema, repository, event, entityId, whenDispatched) {
1830
+ const fields = {
1831
+ receiver: create(MessageIdSchema, {
1832
+ id: this.#packEntityId(repository, entityId),
1833
+ typeUrl: TypeUrls.derive(repository.stateSchema),
1834
+ }),
1835
+ payload: clone(EventSchema, event),
1836
+ whenDispatched,
1837
+ entityType: create(EntityTypeNameSchema, {
1838
+ impl: { case: "javaClassName", value: repository.entityType.name },
1839
+ }),
1840
+ };
1841
+ return schema === EntityLog.EventDispatchedToSubscriberSchema
1842
+ ? create(EntityLog.EventDispatchedToSubscriberSchema, fields)
1843
+ : create(EntityLog.EventDispatchedToReactorSchema, fields);
1844
+ }
1845
+ }
1846
+ const HandlerDispatchPublisher = Object.freeze(new HandlerDispatchPublishing());
1847
+ Object.freeze(RepositorySignals);
1848
+ /**
1849
+ * Internal repository stand operations.
1850
+ */
1851
+ const RepositoryStand = {
1852
+ standUpdateOptions(tenantId, version, lifecycle) {
1853
+ return Object.freeze({
1854
+ ...(tenantId === undefined ? {} : { tenantId }),
1855
+ ...(version === undefined ? {} : { version }),
1856
+ lifecycle,
1857
+ });
1858
+ },
1859
+ projectionVersion(version) {
1860
+ return typeof version === "bigint" ? Number(version) : (version?.number ?? 0);
1861
+ },
1862
+ processManagerVersion(entity) {
1863
+ const version = entity.version;
1864
+ return typeof version === "number" ? version + 1 : 1;
1865
+ },
1866
+ processManagerProducedVersion(sequence) {
1867
+ return sequence;
1868
+ },
1869
+ };
1870
+ Object.freeze(RepositoryStand);
1871
+ /**
1872
+ * Internal repository tenants operations.
1873
+ */
1874
+ const RepositoryTenants = {
1875
+ entityInboxDeliveryContext(context, tenantId) {
1876
+ if (!context.multitenant) {
1877
+ return Object.freeze({ name: context.name, multitenant: false });
1878
+ }
1879
+ const tid = tenantId;
1880
+ if (tid === undefined) {
1881
+ throw new Error(`Multitenant Entity Inbox handoff for "${context.name}" requires tenantId.`);
1882
+ }
1883
+ return Object.freeze({
1884
+ name: context.name,
1885
+ multitenant: true,
1886
+ tenantId: RepositoryTenants.require(tid),
1887
+ });
1888
+ },
1889
+ projectionDeliveryContext(context, tenantId) {
1890
+ if (!context.multitenant) {
1891
+ return Object.freeze({ name: context.name, multitenant: false });
1892
+ }
1893
+ const tid = tenantId;
1894
+ if (tid === undefined) {
1895
+ throw new Error(`Multitenant projection inbox handoff for "${context.name}" requires tenantId.`);
1896
+ }
1897
+ return Object.freeze({
1898
+ name: context.name,
1899
+ multitenant: true,
1900
+ tenantId: RepositoryTenants.require(tid),
1901
+ });
1902
+ },
1903
+ requireProjectionTenant(context, event) {
1904
+ if (!context.multitenant) {
1905
+ return undefined;
1906
+ }
1907
+ const tenantId = RepositoryTenants.readEventTenant(event);
1908
+ if (tenantId === undefined) {
1909
+ throw new Error(`Multitenant projection inbox handoff for "${context.name}" requires tenantId.`);
1910
+ }
1911
+ return RepositoryTenants.require(tenantId);
1912
+ },
1913
+ requirePmEventTenant(context, event) {
1914
+ if (!context.multitenant) {
1915
+ return undefined;
1916
+ }
1917
+ const tenantId = RepositoryTenants.readEventTenant(event);
1918
+ if (tenantId === undefined) {
1919
+ throw new Error(`Multitenant Entity Inbox handoff for "${context.name}" requires tenantId.`);
1920
+ }
1921
+ return RepositoryTenants.require(tenantId);
1922
+ },
1923
+ requireCommandTenant(context, command) {
1924
+ if (!context.multitenant) {
1925
+ return undefined;
1926
+ }
1927
+ const tenantId = RepositoryTenants.readCommandTenant(command);
1928
+ if (tenantId === undefined) {
1929
+ throw new Error(`Multitenant Entity Inbox handoff for "${context.name}" requires tenantId.`);
1930
+ }
1931
+ return RepositoryTenants.require(tenantId);
1932
+ },
1933
+ storageContextForCommand(context, command) {
1934
+ if (!context.multitenant) {
1935
+ return Object.freeze({ name: context.name, multitenant: false });
1936
+ }
1937
+ const tenantId = RepositoryTenants.readCommandTenant(command);
1938
+ if (tenantId === undefined)
1939
+ throw new Error(`Multitenant command for "${context.name}" requires tenantId.`);
1940
+ return Object.freeze({
1941
+ name: context.name,
1942
+ multitenant: true,
1943
+ tenantId: RepositoryTenants.require(tenantId),
1944
+ });
1945
+ },
1946
+ storageContextForEvent(context, event) {
1947
+ if (!context.multitenant) {
1948
+ return Object.freeze({ name: context.name, multitenant: false });
1949
+ }
1950
+ const tenantId = RepositoryTenants.readEventTenant(event);
1951
+ if (tenantId === undefined)
1952
+ throw new Error(`Multitenant event for "${context.name}" requires tenantId.`);
1953
+ return Object.freeze({
1954
+ name: context.name,
1955
+ multitenant: true,
1956
+ tenantId: RepositoryTenants.require(tenantId),
1957
+ });
1958
+ },
1959
+ storageContextForTenant(context, tenantId) {
1960
+ if (!context.multitenant)
1961
+ return Object.freeze({ name: context.name, multitenant: false });
1962
+ if (tenantId === undefined)
1963
+ throw new Error(`Multitenant storage for "${context.name}" requires tenantId.`);
1964
+ return Object.freeze({
1965
+ name: context.name,
1966
+ multitenant: true,
1967
+ tenantId: RepositoryTenants.require(tenantId),
1968
+ });
1969
+ },
1970
+ readCommandTenant(command) {
1971
+ return RepositoryTenants.tenantValue(command.context?.actorContext?.tenantId);
1972
+ },
1973
+ standTenantOptions(context, event) {
1974
+ if (!context.multitenant) {
1975
+ return {};
1976
+ }
1977
+ const tenantId = RepositoryTenants.readEventTenant(event);
1978
+ return tenantId === undefined ? {} : { tenantId };
1979
+ },
1980
+ commandStandOptions(context, command) {
1981
+ if (!context.multitenant) {
1982
+ return {};
1983
+ }
1984
+ const tenantId = RepositoryTenants.readCommandTenant(command);
1985
+ return tenantId === undefined ? {} : { tenantId };
1986
+ },
1987
+ readEventTenant(event) {
1988
+ switch (event.context?.origin.case) {
1989
+ case "importContext":
1990
+ return RepositoryTenants.tenantValue(event.context.origin.value.tenantId);
1991
+ case "pastMessage":
1992
+ return RepositoryTenants.tenantValue(event.context.origin.value.actorContext?.tenantId);
1993
+ default:
1994
+ return undefined;
1995
+ }
1996
+ },
1997
+ tenantValue(tenantId) {
1998
+ return tenantId === undefined ? undefined : clone(TenantIdSchema, tenantId);
1999
+ },
2000
+ require(tenantId) {
2001
+ return TenantBoundary.from(tenantId).tenantId;
2002
+ },
2003
+ equal(left, right) {
2004
+ return TenantBoundary.from(left).key === TenantBoundary.from(right).key;
2005
+ },
2006
+ };
2007
+ Object.freeze(RepositoryTenants);
2008
+ /**
2009
+ * Internal repository handlers operations.
2010
+ */
2011
+ const RepositoryHandlers = {
2012
+ normalizeHandlers(handlersOption) {
2013
+ if (handlersOption === undefined) {
2014
+ return Object.freeze([]);
2015
+ }
2016
+ if (RepositoryHandlers.isHandlersArray(handlersOption)) {
2017
+ return Object.freeze([...handlersOption]);
2018
+ }
2019
+ return Object.freeze([handlersOption]);
2020
+ },
2021
+ createCommandReactionMap(handlers) {
2022
+ const byEvent = new Map();
2023
+ const registry = new HandlerMetadataRegistry(handlers);
2024
+ for (const entry of registry.findHandlersByKind("command-reaction")) {
2025
+ if (RepositoryHandlers.handlerEmittedSchemas(entry.handler).length === 0) {
2026
+ continue;
2027
+ }
2028
+ RepositoryHandlers.pushMapValue(byEvent, entry.handler.messageFullTypeName, entry);
2029
+ }
2030
+ return byEvent;
2031
+ },
2032
+ createEventFilterPlans(byEvent, stringifiers) {
2033
+ const plans = new Map();
2034
+ for (const [eventType, values] of byEvent) {
2035
+ plans.set(eventType, EventHandlerFilters.compile(values.map((value) => ({
2036
+ value,
2037
+ schema: value.handler.schema,
2038
+ ...(value.handler.where === undefined ? {} : { where: value.handler.where }),
2039
+ })), stringifiers));
2040
+ }
2041
+ return plans;
2042
+ },
2043
+ forOrigin(values, external) {
2044
+ return Object.freeze(values.filter((value) => (value.handler.origin === "external") === external));
2045
+ },
2046
+ readinessMap(schemas, find) {
2047
+ return new Map(schemas.map((schema) => [schema.typeName, find(schema.typeName)]));
2048
+ },
2049
+ handlerEmittedSchemas(handler) {
2050
+ return HandlerMetadataValues.returnedSchemas(handler);
2051
+ },
2052
+ handlerThrownSchemas(handler) {
2053
+ return HandlerMetadataValues.thrownSchemas(handler);
2054
+ },
2055
+ isHandlersArray(value) {
2056
+ return Array.isArray(value);
2057
+ },
2058
+ validateHandlers(entityType, metadata, handlers) {
2059
+ for (const handlersMetadata of handlers) {
2060
+ if (!HandlerMetadataValues.isAuthentic(handlersMetadata) ||
2061
+ handlersMetadata.entityType !== entityType ||
2062
+ handlersMetadata.entity.fullTypeName !== metadata.fullTypeName) {
2063
+ throw new RepositoryIdentityError("ENTITY_SCHEMA_KIND_MISMATCH", `Repository entity type "${entityType.name}" does not match the supplied handler metadata.`);
2064
+ }
2065
+ if (metadata.kind !== "process-manager" &&
2066
+ (handlersMetadata.commandSubstitutions.length > 0 ||
2067
+ handlersMetadata.commandReactions.length > 0)) {
2068
+ throw new RepositoryIdentityError("UNSUPPORTED_ENTITY_TYPE", "Only Process Manager repositories support @Command handlers.");
2069
+ }
2070
+ }
2071
+ },
2072
+ uniqueSchemas(schemas) {
2073
+ const byTypeUrl = new Map();
2074
+ for (const schema of schemas) {
2075
+ byTypeUrl.set(TypeUrls.derive(schema), schema);
2076
+ }
2077
+ return Object.freeze([...byTypeUrl.values()]);
2078
+ },
2079
+ pushMapValue(map, key, value) {
2080
+ const values = map.get(key);
2081
+ if (values === undefined) {
2082
+ map.set(key, [value]);
2083
+ return;
2084
+ }
2085
+ values.push(value);
2086
+ },
2087
+ };
2088
+ Object.freeze(RepositoryHandlers);
2089
+ /**
2090
+ * Internal repository routes operations.
2091
+ */
2092
+ const RepositoryRoutes = {
2093
+ createRepositoryRouting(entityType, entityFamily, metadata, handlersOption, producedEvents, commandRouting, eventRouting, stateUpdateRouting, stringifiers) {
2094
+ return RepositoryRoutes.prepareRouting({
2095
+ entityType,
2096
+ entityFamily,
2097
+ metadata,
2098
+ handlersOption,
2099
+ producedEvents,
2100
+ commandRouting,
2101
+ eventRouting,
2102
+ stateUpdateRouting,
2103
+ stringifiers,
2104
+ });
2105
+ },
2106
+ prepareRouting(input) {
2107
+ const handlers = RepositoryHandlers.normalizeHandlers(input.handlersOption);
2108
+ RepositoryHandlers.validateHandlers(input.entityType, input.metadata, handlers);
2109
+ const readiness = RepositoryRoutes.readiness(handlers);
2110
+ const schemas = RepositoryRoutes.routingSchemas(handlers, input.producedEvents);
2111
+ RepositoryRoutes.validateStateSchemas(input.entityFamily, input.metadata, schemas.state);
2112
+ const stateSubscriptions = RepositoryRoutes.stateSubscriptions(handlers);
2113
+ const filters = RepositoryRoutes.routingFilters(handlers, schemas.event, readiness.event, input.stringifiers);
2114
+ const routes = RepositoryRoutes.routingMaps(schemas, input.commandRouting, input.eventRouting, input.stateUpdateRouting, input.metadata.idField);
2115
+ return RepositoryRoutes.freezeRouting(schemas, readiness, stateSubscriptions, filters, routes, input.metadata.idField);
2116
+ },
2117
+ readiness(handlers) {
2118
+ return handlers.length === 0
2119
+ ? { command: undefined, event: undefined }
2120
+ : {
2121
+ command: CommandRegistrationReadiness.fromEntityHandlers(handlers),
2122
+ event: EventRegistrationReadiness.fromEntityHandlers(handlers),
2123
+ };
2124
+ },
2125
+ routingSchemas(handlers, producedEvents) {
2126
+ const accepted = RepositoryRoutes.acceptedSchemas(handlers);
2127
+ const origins = RepositoryRoutes.originSchemas(handlers);
2128
+ const produced = RepositoryRoutes.producedSchemas(handlers, producedEvents);
2129
+ return { ...accepted, ...origins, ...produced };
2130
+ },
2131
+ acceptedSchemas(handlers) {
2132
+ const command = RepositoryHandlers.uniqueSchemas(handlers.flatMap((handler) => [
2133
+ ...handler.commandAssignments.map((assignment) => assignment.schema),
2134
+ ...handler.commandSubstitutions.map((substitution) => substitution.schema),
2135
+ ]));
2136
+ const event = RepositoryHandlers.uniqueSchemas(handlers.flatMap((handler) => [
2137
+ ...handler.commandReactions.map((reaction) => reaction.schema),
2138
+ ...handler.eventSubscriptions.map((subscription) => subscription.schema),
2139
+ ...handler.eventReactions.map((reaction) => reaction.schema),
2140
+ ...handler.eventApplications.map((application) => application.schema),
2141
+ ]));
2142
+ const state = RepositoryHandlers.uniqueSchemas(handlers.flatMap((handler) => handler.stateSubscriptions.map((subscription) => subscription.schema)));
2143
+ return { command, event, state };
2144
+ },
2145
+ originSchemas(handlers) {
2146
+ const schemas = (origin) => RepositoryHandlers.uniqueSchemas(handlers.flatMap((handler) => handler.handlers
2147
+ .filter((candidate) => candidate.origin === origin &&
2148
+ (candidate.kind === "event-subscription" ||
2149
+ candidate.kind === "event-reaction" ||
2150
+ candidate.kind === "command-reaction"))
2151
+ .map((candidate) => candidate.schema)));
2152
+ return { domesticEvent: schemas("domestic"), externalEvent: schemas("external") };
2153
+ },
2154
+ producedSchemas(handlers, explicitEvents) {
2155
+ const producedEvent = RepositoryHandlers.uniqueSchemas([
2156
+ ...explicitEvents,
2157
+ ...handlers.flatMap((handler) => [
2158
+ ...handler.commandAssignments.flatMap((value) => RepositoryHandlers.handlerEmittedSchemas(value)),
2159
+ ...handler.commandAssignments.flatMap((value) => RepositoryHandlers.handlerThrownSchemas(value)),
2160
+ ...handler.commandSubstitutions.flatMap((value) => RepositoryHandlers.handlerThrownSchemas(value)),
2161
+ ...handler.eventReactions.flatMap((value) => RepositoryHandlers.handlerEmittedSchemas(value)),
2162
+ ]),
2163
+ ]);
2164
+ const producedCommand = RepositoryHandlers.uniqueSchemas(handlers.flatMap((handler) => [
2165
+ ...handler.commandSubstitutions.flatMap((value) => RepositoryHandlers.handlerEmittedSchemas(value)),
2166
+ ...handler.commandReactions.flatMap((value) => RepositoryHandlers.handlerEmittedSchemas(value)),
2167
+ ]));
2168
+ return { producedEvent, producedCommand };
2169
+ },
2170
+ validateStateSchemas(entityFamily, metadata, schemas) {
2171
+ if (entityFamily !== "projection" && schemas.length > 0) {
2172
+ throw new Error("Entity state subscriptions are supported only by Projection repositories.");
2173
+ }
2174
+ if (schemas.some((schema) => schema.typeName === metadata.schema.typeName)) {
2175
+ throw new Error("A Projection cannot subscribe to updates of its repository state because each " +
2176
+ "resulting update would be routed back to the same repository.");
2177
+ }
2178
+ },
2179
+ routingFilters(handlers, eventSchemas, readiness, stringifiers) {
2180
+ const commandReactionMap = RepositoryHandlers.createCommandReactionMap(handlers);
2181
+ const subscribers = RepositoryHandlers.readinessMap(eventSchemas, (typeName) => readiness?.findEventSubscribers(typeName) ?? []);
2182
+ const reactors = RepositoryHandlers.readinessMap(eventSchemas, (typeName) => readiness?.findEventReactors(typeName) ?? []);
2183
+ return {
2184
+ commandReactionMap,
2185
+ commandReactions: RepositoryHandlers.createEventFilterPlans(commandReactionMap, stringifiers),
2186
+ eventSubscribers: RepositoryHandlers.createEventFilterPlans(subscribers, stringifiers),
2187
+ eventReactors: RepositoryHandlers.createEventFilterPlans(reactors, stringifiers),
2188
+ };
2189
+ },
2190
+ routingMaps(schemas, command, event, state, idField) {
2191
+ return {
2192
+ command: RepositoryRoutes.resolveCommandRoutes(schemas.command, command),
2193
+ event: RepositoryRoutes.resolveEventRoutes(schemas.event, event),
2194
+ state: RepositoryRoutes.resolveStateRoutes(schemas.state, state, idField),
2195
+ };
2196
+ },
2197
+ freezeRouting(schemas, readiness, stateSubscriptions, filters, routes, idField) {
2198
+ return Object.freeze({
2199
+ commandSchemas: schemas.command,
2200
+ eventSchemas: schemas.event,
2201
+ domesticEventSchemas: schemas.domesticEvent,
2202
+ externalEventSchemas: schemas.externalEvent,
2203
+ producedEventSchemas: schemas.producedEvent,
2204
+ producedCommandSchemas: schemas.producedCommand,
2205
+ commandReadiness: readiness.command,
2206
+ eventReadiness: readiness.event,
2207
+ stateSchemas: schemas.state,
2208
+ stateSubscriptions,
2209
+ ...RepositoryRoutes.handlerSelectors(filters),
2210
+ ...RepositoryRoutes.routeSelectors(schemas, readiness, stateSubscriptions, filters, routes, idField),
2211
+ });
2212
+ },
2213
+ handlerSelectors(filters) {
2214
+ return {
2215
+ commandReactions: (typeName, message, external) => RepositoryRoutes.selectHandlers(filters.commandReactions, typeName, message, external),
2216
+ eventReactors: (typeName, message, external) => RepositoryRoutes.selectHandlers(filters.eventReactors, typeName, message, external),
2217
+ eventSubscribers: (typeName, message, external) => RepositoryRoutes.selectHandlers(filters.eventSubscribers, typeName, message, external),
2218
+ };
2219
+ },
2220
+ selectHandlers(filters, typeName, message, external) {
2221
+ return RepositoryHandlers.forOrigin(filters.get(typeName)?.select(message) ?? Object.freeze([]), external);
2222
+ },
2223
+ routeSelectors(schemas, readiness, stateSubscriptions, filters, routes, idField) {
2224
+ return {
2225
+ routeCommand: RepositoryRoutes.commandSelector(schemas, readiness, routes, idField),
2226
+ routeEvent: RepositoryRoutes.eventSelector(schemas, readiness, filters, routes, idField),
2227
+ routeStateUpdate: RepositoryRoutes.stateSelector(schemas, stateSubscriptions, routes, idField),
2228
+ };
2229
+ },
2230
+ commandSelector(schemas, readiness, routes, idField) {
2231
+ return (command) => RepositoryRoutes.routeCommand(command, readiness.command, schemas.command, idField, routes.command);
2232
+ },
2233
+ eventSelector(schemas, readiness, filters, routes, idField) {
2234
+ return (event) => RepositoryRoutes.routeEvent(event, readiness.event, filters.commandReactionMap, schemas.event, idField, routes.event);
2235
+ },
2236
+ stateSelector(schemas, subscriptions, routes, idField) {
2237
+ return (event) => RepositoryRoutes.routeStateUpdate(event, schemas.state, subscriptions, idField, routes.state);
2238
+ },
2239
+ routeCommand(command, readiness, schemas, targetIdField, commandRoutes) {
2240
+ const message = command.message;
2241
+ if (message === undefined || message.typeUrl === "") {
2242
+ throw new Error("Repository command routing requires command.message.typeUrl.");
2243
+ }
2244
+ const schema = RepositoryRoutes.schemaForTypeUrl(schemas, message.typeUrl, "command");
2245
+ const assignee = readiness?.findCommandAssignee(schema.typeName);
2246
+ if (assignee === undefined) {
2247
+ throw new Error(`Repository command routing has no assignee for "${schema.typeName}".`);
2248
+ }
2249
+ const customRoute = commandRoutes.get(schema);
2250
+ const candidateId = customRoute === undefined
2251
+ ? RepositoryRoutes.readFirstFieldId(message, schema, "command")
2252
+ : RepositoryRoutes.callCommandRoute(customRoute, message, schema, command.context);
2253
+ return Object.freeze({
2254
+ entityId: RepositoryRoutes.readRouteId(candidateId, targetIdField, "command"),
2255
+ messageFullTypeName: schema.typeName,
2256
+ invocation: "deferred",
2257
+ });
2258
+ },
2259
+ resolveCommandRoutes(schemas, routing) {
2260
+ const routes = new Map();
2261
+ RoutingDeclarations.validate(routing, schemas, "command");
2262
+ for (const schema of schemas) {
2263
+ const route = RoutingDeclarations.select(routing, schema);
2264
+ if (route !== undefined)
2265
+ routes.set(schema, route);
2266
+ }
2267
+ return routes;
2268
+ },
2269
+ callCommandRoute(route, message, schema, context) {
2270
+ const unpacked = AnyMessages.unpack(message, schema);
2271
+ if (unpacked === undefined) {
2272
+ throw new Error("Repository command routing requires a readable Command message.");
2273
+ }
2274
+ return route(unpacked, context ?? create(CommandContextSchema));
2275
+ },
2276
+ routeEvent(event, readiness, commandReactions, schemas, targetIdField, eventRoutes) {
2277
+ const message = event.message;
2278
+ if (message === undefined || message.typeUrl === "") {
2279
+ throw new Error("Repository event routing requires event.message.typeUrl.");
2280
+ }
2281
+ const schema = RepositoryRoutes.schemaForTypeUrl(schemas, message.typeUrl, "event");
2282
+ const hasReceiver = (commandReactions.get(schema.typeName)?.length ?? 0) > 0 ||
2283
+ (readiness?.findEventSubscribers(schema.typeName).length ?? 0) > 0 ||
2284
+ (readiness?.findEventReactors(schema.typeName).length ?? 0) > 0 ||
2285
+ (readiness?.findEventApplications(schema.typeName).length ?? 0) > 0;
2286
+ if (!hasReceiver) {
2287
+ throw new Error(`Repository event routing has no receiver for "${schema.typeName}".`);
2288
+ }
2289
+ const customRoute = eventRoutes.get(schema);
2290
+ if (customRoute !== undefined) {
2291
+ return Object.freeze({
2292
+ entityIds: RepositoryRoutes.callEventRoute(customRoute, message, schema, event.context, targetIdField),
2293
+ messageFullTypeName: schema.typeName,
2294
+ invocation: "deferred",
2295
+ });
2296
+ }
2297
+ const targetId = RepositoryRoutes.readEventEntityId(event, message, schema, targetIdField);
2298
+ return Object.freeze({
2299
+ entityIds: Object.freeze([targetId]),
2300
+ messageFullTypeName: schema.typeName,
2301
+ invocation: "deferred",
2302
+ });
2303
+ },
2304
+ resolveEventRoutes(schemas, routing) {
2305
+ const routes = new Map();
2306
+ RoutingDeclarations.validate(routing, schemas, "event");
2307
+ for (const schema of schemas) {
2308
+ const route = RoutingDeclarations.select(routing, schema);
2309
+ if (route !== undefined)
2310
+ routes.set(schema, route);
2311
+ }
2312
+ return routes;
2313
+ },
2314
+ stateSubscriptions(handlers) {
2315
+ const byType = new Map();
2316
+ for (const entityHandlers of handlers) {
2317
+ for (const handler of entityHandlers.stateSubscriptions) {
2318
+ RepositoryHandlers.pushMapValue(byType, handler.messageFullTypeName, Object.freeze({
2319
+ entityHandlers,
2320
+ entityType: entityHandlers.entityType,
2321
+ entity: entityHandlers.entity,
2322
+ handler,
2323
+ }));
2324
+ }
2325
+ }
2326
+ return new Map([...byType].map(([typeName, subscriptions]) => [typeName, Object.freeze([...subscriptions])]));
2327
+ },
2328
+ resolveStateRoutes(schemas, routing, targetIdField) {
2329
+ const routes = new Map();
2330
+ RoutingDeclarations.validate(routing, schemas, "state-update");
2331
+ for (const schema of schemas) {
2332
+ const route = RoutingDeclarations.select(routing, schema);
2333
+ if (route !== undefined)
2334
+ routes.set(schema, route);
2335
+ else if (!schema.fields.some((field) => RepositoryRoutes.compatibleStateIdField(field, targetIdField))) {
2336
+ throw new Error(`Repository state-update routing has no compatible field in "${schema.typeName}".`);
2337
+ }
2338
+ }
2339
+ return routes;
2340
+ },
2341
+ routeStateUpdate(event, schemas, subscriptions, targetIdField, routes) {
2342
+ const update = RepositoryRoutes.decodeStateUpdate(event, schemas, "Repository state-update routing");
2343
+ const candidates = subscriptions.get(update?.schema.typeName ?? "") ?? [];
2344
+ const interested = Object.freeze(candidates.filter((subscriber) => (subscriber.handler.origin === "external") === (event.context?.external === true)));
2345
+ if (update === undefined || interested.length === 0) {
2346
+ return undefined;
2347
+ }
2348
+ const { schema, state } = update;
2349
+ const custom = routes.get(schema);
2350
+ const candidateIds = custom === undefined
2351
+ ? [RepositoryRoutes.firstCompatibleId(state, schema, targetIdField, "state update")]
2352
+ : RepositoryRoutes.callStateUpdateRoute(custom, state, event.context, targetIdField);
2353
+ return Object.freeze({
2354
+ entityIds: Object.freeze([...candidateIds]),
2355
+ messageFullTypeName: schema.typeName,
2356
+ state,
2357
+ subscribers: interested,
2358
+ invocation: "deferred",
2359
+ });
2360
+ },
2361
+ decodeStateUpdate(event, schemas, operation) {
2362
+ const message = event.message;
2363
+ if (message?.typeUrl !== TypeUrls.derive(EntityLog.EntityStateChangedSchema)) {
2364
+ throw new Error(`${operation} requires an EntityStateChanged System event.`);
2365
+ }
2366
+ const changed = AnyMessages.unpack(message, EntityLog.EntityStateChangedSchema);
2367
+ const packedState = changed?.newState;
2368
+ if (packedState === undefined || packedState.typeUrl === "") {
2369
+ throw new Error(`${operation} requires EntityStateChanged.newState.`);
2370
+ }
2371
+ const schema = schemas.find((candidate) => TypeUrls.derive(candidate) === packedState.typeUrl);
2372
+ if (schema === undefined)
2373
+ return undefined;
2374
+ const state = AnyMessages.unpack(packedState, schema);
2375
+ if (state === undefined)
2376
+ throw new Error(`${operation} requires a readable Entity state.`);
2377
+ return Object.freeze({ schema, state });
2378
+ },
2379
+ callStateUpdateRoute(route, state, context, targetIdField) {
2380
+ const candidates = route(state, context ?? create(EventContextSchema));
2381
+ if (!Array.isArray(candidates))
2382
+ throw new Error("Repository state-update routing requires an array of Entity IDs.");
2383
+ if (candidates.length > 1_000)
2384
+ throw new Error("Repository state-update routing accepts at most 1,000 Entity IDs.");
2385
+ const unique = new Map();
2386
+ for (const candidate of candidates) {
2387
+ const id = RepositoryRoutes.readRouteId(candidate, targetIdField, "state update");
2388
+ const key = InboxTargets.key(InboxMessages.inboxTargetId(id, targetIdField));
2389
+ if (!unique.has(key))
2390
+ unique.set(key, structuredClone(id));
2391
+ }
2392
+ return Object.freeze([...unique.values()]);
2393
+ },
2394
+ callEventRoute(route, message, schema, context, targetIdField) {
2395
+ const unpacked = AnyMessages.unpack(message, schema);
2396
+ if (unpacked === undefined) {
2397
+ throw new Error("Repository event routing requires a readable Event message.");
2398
+ }
2399
+ const candidates = route(unpacked, context ?? create(EventContextSchema));
2400
+ if (!Array.isArray(candidates)) {
2401
+ throw new Error("Repository event routing requires an array of Entity IDs.");
2402
+ }
2403
+ if (candidates.length > 1_000) {
2404
+ throw new Error("Repository event routing accepts at most 1,000 Entity IDs.");
2405
+ }
2406
+ const unique = new Map();
2407
+ for (const candidate of candidates) {
2408
+ const id = RepositoryRoutes.readRouteId(candidate, targetIdField, "event");
2409
+ const key = InboxTargets.key(InboxMessages.inboxTargetId(id, targetIdField));
2410
+ if (!unique.has(key))
2411
+ unique.set(key, structuredClone(id));
2412
+ }
2413
+ return Object.freeze([...unique.values()]);
2414
+ },
2415
+ schemaForTypeUrl(schemas, typeUrl, signalKind) {
2416
+ const schema = schemas.find((candidate) => TypeUrls.derive(candidate) === typeUrl);
2417
+ if (schema === undefined) {
2418
+ throw new Error(`Repository ${signalKind} routing has no schema for "${typeUrl}".`);
2419
+ }
2420
+ return schema;
2421
+ },
2422
+ readFirstFieldId(message, schema, signalKind) {
2423
+ const unpacked = AnyMessages.unpack(message, schema);
2424
+ const firstField = schema.fields[0];
2425
+ if (unpacked === undefined || firstField === undefined) {
2426
+ throw new Error(`Repository ${signalKind} routing requires a readable first field.`);
2427
+ }
2428
+ if (firstField.fieldKind === "list" || firstField.fieldKind === "map") {
2429
+ throw new Error(`Repository ${signalKind} routing requires a singular non-map first field.`);
2430
+ }
2431
+ const value = unpacked[firstField.localName];
2432
+ if (value === undefined || value === null || RepositoryRoutes.isBlankRouteId(value)) {
2433
+ throw new Error(`Repository ${signalKind} routing requires a non-empty first field.`);
2434
+ }
2435
+ if (signalKind === "command" && (value === 0 || value === false)) {
2436
+ throw new Error("Repository command routing requires a non-default first field.");
2437
+ }
2438
+ return value;
2439
+ },
2440
+ firstCompatibleId(state, schema, targetIdField, signalKind) {
2441
+ const field = schema.fields.find((candidate) => RepositoryRoutes.compatibleStateIdField(candidate, targetIdField));
2442
+ if (field === undefined)
2443
+ throw new Error("Repository state-update routing requires a compatible state field.");
2444
+ const value = state[field.localName];
2445
+ return RepositoryRoutes.readRouteId(value, targetIdField, signalKind);
2446
+ },
2447
+ compatibleStateIdField(field, targetIdField) {
2448
+ if (field.fieldKind === "list" || field.fieldKind === "map")
2449
+ return false;
2450
+ const target = targetIdField.descriptor;
2451
+ if (field.fieldKind === "message" && target.fieldKind === "message")
2452
+ return field.message.typeName === target.message.typeName;
2453
+ if (field.fieldKind !== "scalar" || target.fieldKind !== "scalar")
2454
+ return false;
2455
+ try {
2456
+ return (RepositoryRoutes.primitiveIdentifierType(field.scalar) ===
2457
+ RepositoryRoutes.primitiveIdentifierType(target.scalar));
2458
+ }
2459
+ catch {
2460
+ return false;
2461
+ }
2462
+ },
2463
+ isBlankRouteId(value) {
2464
+ const id = PrimitiveIds.readFinite(value);
2465
+ return typeof id === "string" && id.trim().length === 0;
2466
+ },
2467
+ readEventEntityId(event, message, schema, targetIdField) {
2468
+ const producerId = event.context?.producerId;
2469
+ if (producerId === undefined || producerId.typeUrl.trim().length === 0) {
2470
+ throw new Error("Repository event routing requires a producer ID.");
2471
+ }
2472
+ const compatibleProducer = RepositoryRoutes.compatibleProducerId(producerId, targetIdField);
2473
+ if (compatibleProducer.compatible) {
2474
+ return RepositoryRoutes.readRouteId(compatibleProducer.id, targetIdField, "event");
2475
+ }
2476
+ return RepositoryRoutes.readRouteId(RepositoryRoutes.readFirstFieldId(message, schema, "event"), targetIdField, "event");
2477
+ },
2478
+ compatibleProducerId(producerId, targetIdField) {
2479
+ const descriptor = targetIdField.descriptor;
2480
+ if (descriptor.fieldKind === "message") {
2481
+ const schema = descriptor.message;
2482
+ if (producerId.typeUrl !== TypeUrls.derive(schema))
2483
+ return { compatible: false };
2484
+ const id = Identifiers.unpack(schema, producerId);
2485
+ if (id === undefined) {
2486
+ throw new Error("Repository event routing requires a readable compatible producer ID.");
2487
+ }
2488
+ return { compatible: true, id };
2489
+ }
2490
+ if (descriptor.fieldKind !== "scalar") {
2491
+ throw new Error("Repository event routing requires a scalar or message-valued ID.");
2492
+ }
2493
+ const type = RepositoryRoutes.primitiveIdentifierType(descriptor.scalar);
2494
+ const schema = type === "string"
2495
+ ? StringValueSchema
2496
+ : type === "int32"
2497
+ ? Int32ValueSchema
2498
+ : Int64ValueSchema;
2499
+ if (producerId.typeUrl !== TypeUrls.derive(schema))
2500
+ return { compatible: false };
2501
+ const id = type === "string"
2502
+ ? Identifiers.unpack("string", producerId)
2503
+ : type === "int32"
2504
+ ? Identifiers.unpack("int32", producerId)
2505
+ : Identifiers.unpack("int64", producerId);
2506
+ if (id === undefined) {
2507
+ throw new Error("Repository event routing requires a readable compatible producer ID.");
2508
+ }
2509
+ return { compatible: true, id };
2510
+ },
2511
+ primitiveIdentifierType(type) {
2512
+ switch (type) {
2513
+ case ScalarType.STRING:
2514
+ return "string";
2515
+ case ScalarType.INT32:
2516
+ case ScalarType.SINT32:
2517
+ case ScalarType.SFIXED32:
2518
+ return "int32";
2519
+ case ScalarType.INT64:
2520
+ case ScalarType.SINT64:
2521
+ case ScalarType.SFIXED64:
2522
+ return "int64";
2523
+ default:
2524
+ throw new Error("Repository event routing requires a supported Entity ID type.");
2525
+ }
2526
+ },
2527
+ readRouteId(value, targetIdField, signalKind) {
2528
+ const descriptor = targetIdField.descriptor;
2529
+ if (descriptor.fieldKind === "message") {
2530
+ return RepositoryRoutes.readMessageRouteId(value, descriptor.message, signalKind);
2531
+ }
2532
+ if (descriptor.fieldKind === "scalar") {
2533
+ return RepositoryRoutes.readPrimitiveRouteId(value, descriptor.scalar, signalKind);
2534
+ }
2535
+ throw new Error(`Repository ${signalKind} routing requires a scalar or message-valued ID.`);
2536
+ },
2537
+ readMessageRouteId(value, targetSchema, signalKind) {
2538
+ const id = MessageIds.read(value);
2539
+ if (id === undefined) {
2540
+ throw new Error(`Repository ${signalKind} routing requires a "${targetSchema.typeName}" ID.`);
2541
+ }
2542
+ if (id.$typeName !== targetSchema.typeName) {
2543
+ throw new Error(`Repository ${signalKind} routing requires a "${targetSchema.typeName}" ID.`);
2544
+ }
2545
+ try {
2546
+ Validate.check(targetSchema, id);
2547
+ }
2548
+ catch (error) {
2549
+ throw new Error(`Repository ${signalKind} routing requires a valid "${targetSchema.typeName}" ID.`, { cause: error });
2550
+ }
2551
+ try {
2552
+ Identifiers.pack(targetSchema, id);
2553
+ }
2554
+ catch (error) {
2555
+ throw new Error(`Repository ${signalKind} routing requires an encodable "${targetSchema.typeName}" ID.`, { cause: error });
2556
+ }
2557
+ return id;
2558
+ },
2559
+ readPrimitiveRouteId(value, targetType, signalKind) {
2560
+ const id = RepositoryRoutes.readCompatiblePrimitiveId(value, targetType);
2561
+ if (id === undefined) {
2562
+ throw new Error(`Repository ${signalKind} routing requires an ID compatible with the Entity state.`);
2563
+ }
2564
+ return id;
2565
+ },
2566
+ readCompatiblePrimitiveId(value, targetType) {
2567
+ switch (targetType) {
2568
+ case ScalarType.STRING:
2569
+ return typeof value === "string" && value.trim().length > 0 ? value : undefined;
2570
+ case ScalarType.INT32:
2571
+ case ScalarType.SINT32:
2572
+ case ScalarType.SFIXED32:
2573
+ return typeof value === "number" &&
2574
+ Number.isInteger(value) &&
2575
+ value >= -(2 ** 31) &&
2576
+ value < 2 ** 31
2577
+ ? value
2578
+ : undefined;
2579
+ case ScalarType.INT64:
2580
+ case ScalarType.SINT64:
2581
+ case ScalarType.SFIXED64:
2582
+ return typeof value === "bigint" && value >= -(1n << 63n) && value < 1n << 63n
2583
+ ? value
2584
+ : undefined;
2585
+ default:
2586
+ return undefined;
2587
+ }
2588
+ },
2589
+ };
2590
+ Object.freeze(RepositoryRoutes);
2591
+ /**
2592
+ * Internal repository storage operations.
2593
+ */
2594
+ const RepositoryStorage = {
2595
+ openEntityStorage(factory, input) {
2596
+ const candidate = factory;
2597
+ if (candidate.createEntityStorage === undefined) {
2598
+ throw new Error("StorageFactory does not provide the required atomic Entity commit storage seam.");
2599
+ }
2600
+ const entity = candidate.createEntityStorage(input);
2601
+ const commits = EntityCommitStorageFactories.create(factory, input);
2602
+ return {
2603
+ ...entity,
2604
+ commits,
2605
+ close: () => {
2606
+ commits.close();
2607
+ entity.close();
2608
+ },
2609
+ };
2610
+ },
2611
+ openRepositoryEntityStorage(repository, factory, input) {
2612
+ const handle = RepositoryStorage.openEntityStorage(factory, input);
2613
+ const key = JSON.stringify({ context: input.context, state: input.sourceType.typeName });
2614
+ let handles = repositoryEntityHandles.get(repository);
2615
+ if (handles === undefined) {
2616
+ handles = new Map();
2617
+ repositoryEntityHandles.set(repository, handles);
2618
+ }
2619
+ const existing = handles.get(key);
2620
+ if (existing !== undefined) {
2621
+ handle.close();
2622
+ return existing;
2623
+ }
2624
+ handles.set(key, handle);
2625
+ return handle;
2626
+ },
2627
+ entityStorageInput(repository, context) {
2628
+ const descriptor = entityStorageDescriptor(context, SpecScanner.scan(repository.entityType));
2629
+ const history = RepositoryStorage.historyConfiguration(repository);
2630
+ return {
2631
+ ...descriptor,
2632
+ stateHistory: history.stateHistory,
2633
+ eventHistory: repository.entityFamily === "aggregate" || history.processManagerEventHistory,
2634
+ };
2635
+ },
2636
+ readHistoryConfiguration(options, family) {
2637
+ const guard = options.doubleDispatchGuard;
2638
+ const depth = guard === true
2639
+ ? 100
2640
+ : guard === false || guard === undefined
2641
+ ? undefined
2642
+ : (guard.depth ?? 100);
2643
+ if (depth !== undefined && (!Number.isSafeInteger(depth) || depth <= 0)) {
2644
+ throw new RangeError("Repository doubleDispatchGuard.depth must be a positive safe integer.");
2645
+ }
2646
+ if (depth !== undefined && family === "projection") {
2647
+ throw new Error("Repository doubleDispatchGuard is unavailable for Projections.");
2648
+ }
2649
+ if (depth !== undefined &&
2650
+ family === "process-manager" &&
2651
+ options.processManagerEventHistory !== true) {
2652
+ throw new Error("Process Manager doubleDispatchGuard requires processManagerEventHistory.");
2653
+ }
2654
+ return {
2655
+ stateHistory: options.stateHistory ?? false,
2656
+ processManagerEventHistory: options.processManagerEventHistory ?? false,
2657
+ dispatchGuardDepth: depth,
2658
+ };
2659
+ },
2660
+ historyConfiguration(repository) {
2661
+ const configuration = repositoryHistoryConfigurations.get(repository);
2662
+ if (configuration === undefined)
2663
+ throw new Error("Repository history configuration is unavailable.");
2664
+ return configuration;
2665
+ },
2666
+ /**
2667
+ * In-process bounded duplicate guard. Completion is recorded only after a successful execution;
2668
+ * therefore provider/journal failures may be retried and separate machines remain independent.
2669
+ */
2670
+ };
2671
+ Object.freeze(RepositoryStorage);
2672
+ /**
2673
+ * Internal repository history internals operations.
2674
+ */
2675
+ const RepositoryHistoryInternals = {
2676
+ bindEntityHistory(entity, storage, entityId, schema) {
2677
+ const stateCache = RepositoryHistoryInternals.createHistoryCache((depth, startingFromVersion) => storage.states.backward(entityId, depth, startingFromVersion), (record) => BigInt(EntityRecords.unpack(schema, record).versionMessage.number), { requireDescendingVersions: true });
2678
+ entityStateHistoryCaches.set(entity, stateCache);
2679
+ const eventCache = RepositoryHistoryInternals.createHistoryCache((depth, startingFromVersion) => storage.events.backward(entityId, depth, startingFromVersion), (event) => RepositorySignals.readEventVersion(event), { cacheCompleteVersionGroups: true });
2680
+ entityHistoryAccess.bind(entity, {
2681
+ stateAt: async (time) => RepositoryHistoryInternals.cloneHistoryState(await storage.states.stateAt(entityId, time)),
2682
+ states: async (depth) => RepositoryHistoryInternals.freezeHistoryStates((await stateCache.read(depth)).map((record) => EntityRecords.unpack(schema, record).state)),
2683
+ events: async (depth) => RepositoryHistoryInternals.freezeHistoryEvents(await eventCache.read(depth)),
2684
+ stateMaintenance: storage.states,
2685
+ eventMaintenance: storage.events,
2686
+ });
2687
+ },
2688
+ /**
2689
+ * Creates a per-live-entity continuation cache that serializes a larger request behind a prior read.
2690
+ *
2691
+ * @internal Shared repository history-cache implementation, exercised by repository tests.
2692
+ */
2693
+ createHistoryCache(load, versionOf, options = {}) {
2694
+ return new RepositoryHistoryCache(load, versionOf, options).api();
2695
+ },
2696
+ cloneHistoryState(state) {
2697
+ return state === undefined ? undefined : Object.freeze(structuredClone(state));
2698
+ },
2699
+ freezeHistoryStates(states) {
2700
+ return Object.freeze(states.map((state) => Object.freeze(structuredClone(state))));
2701
+ },
2702
+ freezeHistoryEvents(events) {
2703
+ return Object.freeze(events.map((event) => Object.freeze(clone(EventSchema, event))));
2704
+ },
2705
+ };
2706
+ Object.freeze(RepositoryHistoryInternals);
2707
+ class RepositoryHistoryCache {
2708
+ #load;
2709
+ #versionOf;
2710
+ #requireDescendingVersions;
2711
+ #cacheCompleteVersionGroups;
2712
+ #entries = Object.freeze([]);
2713
+ #exhausted = false;
2714
+ #continuation = Promise.resolve();
2715
+ #nextVersion;
2716
+ #newestVersion;
2717
+ #generation = 0;
2718
+ constructor(load, versionOf, options) {
2719
+ this.#load = load;
2720
+ this.#versionOf = versionOf;
2721
+ this.#requireDescendingVersions = options.requireDescendingVersions ?? false;
2722
+ this.#cacheCompleteVersionGroups = options.cacheCompleteVersionGroups ?? false;
2723
+ }
2724
+ api() {
2725
+ return Object.freeze({
2726
+ read: (depth) => this.read(depth),
2727
+ clear: () => {
2728
+ this.clear();
2729
+ },
2730
+ });
2731
+ }
2732
+ clear() {
2733
+ this.#generation += 1;
2734
+ this.#entries = Object.freeze([]);
2735
+ this.#exhausted = false;
2736
+ this.#nextVersion = undefined;
2737
+ this.#newestVersion = undefined;
2738
+ }
2739
+ async read(depth) {
2740
+ await this.#continuation;
2741
+ if (this.#isSatisfied(depth))
2742
+ return this.#entries.slice(0, depth);
2743
+ let result;
2744
+ const next = this.#continuation.then(async () => {
2745
+ if (this.#isSatisfied(depth))
2746
+ return;
2747
+ result = await this.#extend(depth);
2748
+ });
2749
+ // spine-log-boundary: server.repository_history_prefetch
2750
+ this.#continuation = next.catch(() => undefined);
2751
+ await next;
2752
+ return result ?? this.#entries.slice(0, depth);
2753
+ }
2754
+ #isSatisfied(depth) {
2755
+ return this.#entries.length >= depth || this.#exhausted;
2756
+ }
2757
+ async #extend(depth) {
2758
+ const generation = this.#generation;
2759
+ const requested = Math.max(1, depth - this.#entries.length);
2760
+ return this.#cacheCompleteVersionGroups
2761
+ ? this.#extendCompleteGroups(depth, requested, generation)
2762
+ : this.#extendVersioned(depth, requested, generation);
2763
+ }
2764
+ async #extendCompleteGroups(depth, requested, generation) {
2765
+ const loaded = await this.#load(requested + 1, this.#nextVersion);
2766
+ if (generation !== this.#generation)
2767
+ return undefined;
2768
+ const combined = [...this.#entries, ...loaded];
2769
+ const cacheable = this.#completeGroups(loaded, requested);
2770
+ this.#append(cacheable);
2771
+ this.#exhausted = loaded.length < requested + 1;
2772
+ return Object.freeze(combined.slice(0, depth));
2773
+ }
2774
+ #completeGroups(loaded, requested) {
2775
+ const terminal = loaded.at(-1);
2776
+ const terminalVersion = terminal === undefined ? undefined : this.#versionOf(terminal);
2777
+ const length = loaded.length < requested + 1 || terminalVersion === undefined
2778
+ ? loaded.length
2779
+ : loaded.findIndex((entry) => this.#versionOf(entry) === terminalVersion);
2780
+ return loaded.slice(0, Math.max(0, length));
2781
+ }
2782
+ async #extendVersioned(depth, requested, generation) {
2783
+ const loaded = await this.#load(requested, this.#nextVersion);
2784
+ if (generation !== this.#generation)
2785
+ return undefined;
2786
+ const latest = loaded[0] === undefined ? undefined : this.#versionOf(loaded[0]);
2787
+ if (this.#hasInvalidPage(loaded)) {
2788
+ this.clear();
2789
+ return undefined;
2790
+ }
2791
+ if (this.#hasNewerPage(latest))
2792
+ return this.#refresh(depth, generation);
2793
+ if (this.#hasInvalidContinuation(latest)) {
2794
+ this.clear();
2795
+ return undefined;
2796
+ }
2797
+ this.#append(loaded);
2798
+ this.#newestVersion ??= latest;
2799
+ this.#exhausted = loaded.length < requested;
2800
+ return undefined;
2801
+ }
2802
+ #hasInvalidPage(loaded) {
2803
+ if (!this.#requireDescendingVersions)
2804
+ return false;
2805
+ for (let index = 1; index < loaded.length; index += 1) {
2806
+ const newer = this.#versionOf(loaded[index - 1]);
2807
+ const older = this.#versionOf(loaded[index]);
2808
+ if (newer !== undefined && older !== undefined && newer <= older)
2809
+ return true;
2810
+ }
2811
+ return false;
2812
+ }
2813
+ #hasNewerPage(latest) {
2814
+ return (this.#newestVersion !== undefined &&
2815
+ latest !== undefined &&
2816
+ latest > this.#newestVersion &&
2817
+ this.#nextVersion !== undefined);
2818
+ }
2819
+ #hasInvalidContinuation(latest) {
2820
+ const oldest = this.#entries.at(-1);
2821
+ const oldestVersion = oldest === undefined ? undefined : this.#versionOf(oldest);
2822
+ return (this.#requireDescendingVersions &&
2823
+ oldestVersion !== undefined &&
2824
+ latest !== undefined &&
2825
+ latest >= oldestVersion);
2826
+ }
2827
+ async #refresh(depth, generation) {
2828
+ this.clear();
2829
+ const refreshed = await this.#load(depth);
2830
+ if (generation + 1 !== this.#generation)
2831
+ return undefined;
2832
+ if (this.#hasInvalidPage(refreshed))
2833
+ return undefined;
2834
+ this.#append(refreshed);
2835
+ this.#newestVersion = refreshed[0] === undefined ? undefined : this.#versionOf(refreshed[0]);
2836
+ this.#exhausted = refreshed.length < depth;
2837
+ return undefined;
2838
+ }
2839
+ #append(loaded) {
2840
+ this.#entries = Object.freeze([...this.#entries, ...loaded]);
2841
+ const last = loaded.at(-1);
2842
+ this.#nextVersion = last === undefined ? this.#nextVersion : this.#versionOf(last);
2843
+ }
2844
+ }
2845
+ /**
2846
+ * Internal dispatch guards operations.
2847
+ */
2848
+ const DispatchGuards = {
2849
+ async guardedEntityEventDispatch(repository, runtime, event, entityId, dispatch) {
2850
+ const depth = RepositoryStorage.historyConfiguration(repository).dispatchGuardDepth;
2851
+ const eventId = event.id?.value;
2852
+ if (depth === undefined || eventId === undefined || eventId.length === 0)
2853
+ return dispatch();
2854
+ if (entityId === undefined)
2855
+ return dispatch();
2856
+ const key = DispatchGuards.canonicalEntityIdKey(repository, entityId);
2857
+ const journalEventId = DispatchGuards.guardedJournalEventId(eventId, key);
2858
+ const { guard, guards } = DispatchGuards.guardLane(repository, key);
2859
+ DispatchGuards.touchGuardLane(guards, key);
2860
+ guard.active += 1;
2861
+ return DispatchGuards.scheduleDispatch(repository, runtime, event, entityId, eventId, journalEventId, depth, guard, guards, dispatch);
2862
+ },
2863
+ scheduleDispatch(repository, runtime, event, entityId, eventId, journalEventId, depth, guard, guards, dispatch) {
2864
+ const next = guard.chain.then(() => DispatchGuards.dispatchOnce(repository, runtime, event, entityId, eventId, journalEventId, depth, guard, dispatch));
2865
+ // spine-log-boundary: server.repository_dispatch_guard
2866
+ guard.chain = next
2867
+ .catch(() => undefined)
2868
+ .finally(() => {
2869
+ guard.active -= 1;
2870
+ DispatchGuards.trimGuardLanes(guards, depth);
2871
+ });
2872
+ return next;
2873
+ },
2874
+ guardLane(repository, key) {
2875
+ let guards = repositoryDispatchGuards.get(repository);
2876
+ if (guards === undefined) {
2877
+ guards = { lanes: new Map(), order: [] };
2878
+ repositoryDispatchGuards.set(repository, guards);
2879
+ }
2880
+ let guard = guards.lanes.get(key);
2881
+ if (guard === undefined) {
2882
+ guard = { completed: new Set(), order: [], chain: Promise.resolve(), active: 0 };
2883
+ guards.lanes.set(key, guard);
2884
+ }
2885
+ return { guard, guards };
2886
+ },
2887
+ async dispatchOnce(repository, runtime, event, entityId, eventId, journalEventId, depth, guard, dispatch) {
2888
+ if (guard.completed.has(eventId))
2889
+ return;
2890
+ const storage = RepositoryStorage.openEntityStorage(runtime.storageFactory, RepositoryStorage.entityStorageInput(repository, RepositoryTenants.storageContextForEvent(runtime.context, event)));
2891
+ let persisted;
2892
+ try {
2893
+ persisted = await storage.events.backward(entityId, depth);
2894
+ }
2895
+ finally {
2896
+ storage.close();
2897
+ }
2898
+ if (persisted.some((candidate) => candidate.id?.value === journalEventId)) {
2899
+ DispatchGuards.rememberGuardCompletion(guard, eventId, depth);
2900
+ return;
2901
+ }
2902
+ await dispatch();
2903
+ DispatchGuards.rememberGuardCompletion(guard, eventId, depth);
2904
+ },
2905
+ rememberGuardCompletion(guard, eventId, depth) {
2906
+ if (!guard.completed.has(eventId)) {
2907
+ guard.completed.add(eventId);
2908
+ guard.order.push(eventId);
2909
+ }
2910
+ while (guard.order.length > depth) {
2911
+ const expired = guard.order.shift();
2912
+ if (expired !== undefined)
2913
+ guard.completed.delete(expired);
2914
+ }
2915
+ },
2916
+ touchGuardLane(guards, key) {
2917
+ const index = guards.order.indexOf(key);
2918
+ if (index >= 0)
2919
+ guards.order.splice(index, 1);
2920
+ guards.order.push(key);
2921
+ },
2922
+ trimGuardLanes(guards, depth) {
2923
+ while (guards.order.length > depth) {
2924
+ const index = guards.order.findIndex((key) => guards.lanes.get(key)?.active === 0);
2925
+ if (index < 0)
2926
+ return;
2927
+ const [key] = guards.order.splice(index, 1);
2928
+ if (key === undefined)
2929
+ return;
2930
+ guards.lanes.delete(key);
2931
+ }
2932
+ },
2933
+ guardedJournalEvent(repository, event, entityId) {
2934
+ const sourceId = event.id?.value;
2935
+ if (sourceId === undefined)
2936
+ return event;
2937
+ return create(EventSchema, {
2938
+ ...event,
2939
+ id: create(EventIdSchema, {
2940
+ value: DispatchGuards.guardedJournalEventId(sourceId, DispatchGuards.canonicalEntityIdKey(repository, entityId)),
2941
+ }),
2942
+ });
2943
+ },
2944
+ guardedJournalEventId(sourceId, entityKey) {
2945
+ return `${sourceId}.guard.${encodeURIComponent(entityKey)}`;
2946
+ },
2947
+ canonicalEntityIdKey(repository, id) {
2948
+ return InboxTargets.key(InboxMessages.inboxTargetId(id, repository.idField));
2949
+ },
2950
+ };
2951
+ Object.freeze(DispatchGuards);
2952
+ /**
2953
+ * Internal inbox messages operations.
2954
+ */
2955
+ const InboxMessages = {
2956
+ inboxTargetId(entityId, idField) {
2957
+ if (idField.descriptor.fieldKind === "message") {
2958
+ return Identifiers.pack(idField.descriptor.message, entityId);
2959
+ }
2960
+ if (typeof entityId === "string")
2961
+ return Identifiers.pack("string", entityId);
2962
+ if (typeof entityId === "number")
2963
+ return Identifiers.pack("int32", entityId);
2964
+ if (typeof entityId === "bigint")
2965
+ return Identifiers.pack("int64", entityId);
2966
+ throw new Error("Repository Entity Inbox handoff requires a supported target ID.");
2967
+ },
2968
+ targetEntityId(targetId, idField) {
2969
+ const descriptor = idField.descriptor;
2970
+ let entityId;
2971
+ if (descriptor.fieldKind === "message") {
2972
+ entityId = Identifiers.unpack(descriptor.message, targetId);
2973
+ }
2974
+ else if (descriptor.fieldKind === "scalar") {
2975
+ switch (descriptor.scalar) {
2976
+ case ScalarType.STRING:
2977
+ entityId = Identifiers.unpack("string", targetId);
2978
+ break;
2979
+ case ScalarType.INT32:
2980
+ case ScalarType.SINT32:
2981
+ case ScalarType.SFIXED32:
2982
+ entityId = Identifiers.unpack("int32", targetId);
2983
+ break;
2984
+ case ScalarType.INT64:
2985
+ case ScalarType.SINT64:
2986
+ case ScalarType.SFIXED64:
2987
+ entityId = Identifiers.unpack("int64", targetId);
2988
+ break;
2989
+ }
2990
+ }
2991
+ if (entityId === undefined) {
2992
+ throw new Error("Entity Inbox replay stored target ID is incompatible with the repository.");
2993
+ }
2994
+ return RepositoryRoutes.readRouteId(entityId, idField, "command");
2995
+ },
2996
+ readInboxCommand(message) {
2997
+ if (message.label !== "HANDLE_COMMAND") {
2998
+ throw new Error(`Entity Inbox replay does not handle "${message.label}" messages.`);
2999
+ }
3000
+ const command = message.signal === undefined ? undefined : AnyMessages.unpack(message.signal, CommandSchema);
3001
+ if (command === undefined) {
3002
+ throw CommandValidationError.invalidPayload();
3003
+ }
3004
+ return command;
3005
+ },
3006
+ readPmInboxEvent(message) {
3007
+ return InboxMessages.readStoredEvent(message, "REACT_UPON_EVENT", "Entity Inbox replay", "Entity Inbox replay requires a readable stored event.");
3008
+ },
3009
+ readProjectionInboxEvent(message) {
3010
+ return InboxMessages.readStoredEvent(message, "UPDATE_SUBSCRIBER", "Projection inbox replay", "Projection inbox replay requires a readable stored event.");
3011
+ },
3012
+ readStoredEvent(message, expectedLabel, replayName, unreadableMessage) {
3013
+ if (message.label !== expectedLabel) {
3014
+ throw new Error(`${replayName} does not handle "${message.label}" messages.`);
3015
+ }
3016
+ const signal = message.signal === undefined
3017
+ ? undefined
3018
+ : create(AnySchema, {
3019
+ typeUrl: message.signal.typeUrl,
3020
+ value: new Uint8Array(message.signal.value),
3021
+ });
3022
+ const event = signal === undefined ? undefined : AnyMessages.unpack(signal, EventSchema);
3023
+ if (event === undefined) {
3024
+ throw new Error(unreadableMessage);
3025
+ }
3026
+ return event;
3027
+ },
3028
+ };
3029
+ Object.freeze(InboxMessages);
3030
+ /**
3031
+ * Internal inbox replay operations.
3032
+ */
3033
+ const InboxReplay = {
3034
+ async replayAggregateCommand(repository, routing, message, deliveryTenantId) {
3035
+ const runtime = repositoryRuntimes.get(repository);
3036
+ if (runtime === undefined) {
3037
+ throw new Error("Aggregate inbox replay requires a bound repository runtime.");
3038
+ }
3039
+ if (message.label !== "HANDLE_COMMAND") {
3040
+ throw new Error(`Aggregate inbox replay does not handle "${message.label}" messages.`);
3041
+ }
3042
+ const command = InboxMessages.readInboxCommand(message);
3043
+ InboxReplay.validateReplayTenant(runtime.context, deliveryTenantId, command);
3044
+ InboxReplay.validateReplayedCommandPayload(routing, command);
3045
+ const route = InboxReplay.replayCommandRoute(repository, routing, message, command);
3046
+ return await new AggregateCommandExecution(repository, routing, runtime, command).run(route);
3047
+ },
3048
+ async replayPmInbox(repository, routing, message, deliveryTenantId) {
3049
+ if (message.label === "HANDLE_COMMAND") {
3050
+ return await InboxReplay.replayProcessManagerCommand(repository, routing, message, deliveryTenantId);
3051
+ }
3052
+ if (message.label === "REACT_UPON_EVENT") {
3053
+ await InboxReplay.replayProcessManagerEvent(repository, routing, message, deliveryTenantId);
3054
+ return undefined;
3055
+ }
3056
+ throw new Error(`Entity Inbox replay does not handle "${message.label}" messages.`);
3057
+ },
3058
+ async replayProcessManagerCommand(repository, routing, message, deliveryTenantId) {
3059
+ const runtime = repositoryRuntimes.get(repository);
3060
+ if (runtime === undefined) {
3061
+ throw new Error("Entity Inbox replay requires a bound repository runtime.");
3062
+ }
3063
+ const command = InboxMessages.readInboxCommand(message);
3064
+ InboxReplay.validateReplayTenant(runtime.context, deliveryTenantId, command);
3065
+ InboxReplay.validateReplayedCommandPayload(routing, command);
3066
+ const route = InboxReplay.replayCommandRoute(repository, routing, message, command);
3067
+ return await new ProcessManagerCommandExecution(repository, routing, runtime, command).run(route);
3068
+ },
3069
+ async replayProcessManagerEvent(repository, routing, message, deliveryTenantId) {
3070
+ const runtime = repositoryRuntimes.get(repository);
3071
+ if (runtime === undefined) {
3072
+ throw new Error("Entity Inbox replay requires a bound repository runtime.");
3073
+ }
3074
+ const event = InboxMessages.readPmInboxEvent(message);
3075
+ InboxReplay.validatePmReplayTenant(runtime.context, deliveryTenantId, event);
3076
+ InboxReplay.validateReplayedEventPayload(routing, event, "Entity Inbox replay requires a readable event payload.");
3077
+ const route = InboxReplay.replayEventRoute(repository, routing, message, event, "Entity Inbox replay");
3078
+ const [entityId] = route.entityIds;
3079
+ await new ProcessManagerEventExecution(repository, routing, runtime, event).runTarget(entityId, route);
3080
+ },
3081
+ async replayProjectionEvent(repository, routing, message, deliveryTenantId) {
3082
+ const runtime = repositoryRuntimes.get(repository);
3083
+ if (runtime === undefined) {
3084
+ throw new Error("Projection inbox replay requires a bound repository runtime.");
3085
+ }
3086
+ const event = InboxMessages.readProjectionInboxEvent(message);
3087
+ InboxReplay.validateProjectionReplayTenant(runtime.context, deliveryTenantId, event);
3088
+ InboxReplay.validateReplayedEventPayload(routing, event);
3089
+ const route = InboxReplay.replayEventRoute(repository, routing, message, event, "Projection inbox replay");
3090
+ const [entityId] = route.entityIds;
3091
+ await new ProjectionEventExecution(repository, routing, runtime, event).runTarget(entityId, route);
3092
+ },
3093
+ async replayProjectionMessage(repository, routing, message, deliveryTenantId) {
3094
+ const event = InboxMessages.readProjectionInboxEvent(message);
3095
+ if (event.message?.typeUrl === TypeUrls.derive(EntityLog.EntityStateChangedSchema)) {
3096
+ await InboxReplay.replayProjectionStateUpdate(repository, routing, message, event, deliveryTenantId);
3097
+ return;
3098
+ }
3099
+ await InboxReplay.replayProjectionEvent(repository, routing, message, deliveryTenantId);
3100
+ },
3101
+ async replayProjectionStateUpdate(repository, routing, message, event, deliveryTenantId) {
3102
+ const runtime = repositoryRuntimes.get(repository);
3103
+ if (runtime === undefined)
3104
+ throw new Error("Projection inbox replay requires a bound repository runtime.");
3105
+ InboxReplay.validateProjectionReplayTenant(runtime.context, deliveryTenantId, event);
3106
+ const route = InboxReplay.replayStateUpdateRoute(repository, routing, message, event);
3107
+ const [entityId] = route.entityIds;
3108
+ const subscribers = route.subscribers;
3109
+ await ProjectionEventExecution.runStateTarget(repository, routing, runtime, event, entityId, route, subscribers);
3110
+ },
3111
+ validateReplayedCommandPayload(routing, command) {
3112
+ const commandMessage = EntityInvocation.requireSignalMessage(command.message, "command");
3113
+ const commandSchema = RepositoryRoutes.schemaForTypeUrl(routing.commandSchemas, commandMessage.typeUrl, "command");
3114
+ const payload = AnyMessages.unpack(commandMessage, commandSchema);
3115
+ if (payload === undefined) {
3116
+ throw CommandValidationError.invalidPayload();
3117
+ }
3118
+ try {
3119
+ Validate.check(commandSchema, payload);
3120
+ }
3121
+ catch (error) {
3122
+ if (error instanceof ValidationException) {
3123
+ throw new CommandValidationError(error.asMessage());
3124
+ }
3125
+ throw error;
3126
+ }
3127
+ const implicitId = ImplicitRequiredIds.validateCommand(commandSchema, payload);
3128
+ if (!implicitId.valid) {
3129
+ throw new CommandValidationError(implicitId.error);
3130
+ }
3131
+ },
3132
+ validateReplayedEventPayload(routing, event, invalidPayloadMessage = "Projection inbox replay requires a readable event payload.") {
3133
+ const eventMessage = EntityInvocation.requireSignalMessage(event.message, "event");
3134
+ const eventSchema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, eventMessage.typeUrl, "event");
3135
+ const payload = AnyMessages.unpack(eventMessage, eventSchema);
3136
+ if (payload === undefined) {
3137
+ throw new Error(invalidPayloadMessage);
3138
+ }
3139
+ Validate.check(eventSchema, payload);
3140
+ },
3141
+ validateReplayTenant(context, deliveryTenantId, command) {
3142
+ if (!context.multitenant) {
3143
+ return;
3144
+ }
3145
+ if (deliveryTenantId === undefined) {
3146
+ throw new Error(`Multitenant Entity Inbox replay for "${context.name}" requires tenantId.`);
3147
+ }
3148
+ const envelopeTenantId = RepositoryTenants.readCommandTenant(command);
3149
+ if (envelopeTenantId === undefined) {
3150
+ throw new Error("Entity Inbox replay requires stored command tenant metadata.");
3151
+ }
3152
+ if (!RepositoryTenants.equal(envelopeTenantId, deliveryTenantId)) {
3153
+ throw new Error("Entity Inbox replay stored command tenant does not match.");
3154
+ }
3155
+ },
3156
+ validateProjectionReplayTenant(context, deliveryTenantId, event) {
3157
+ if (!context.multitenant) {
3158
+ return;
3159
+ }
3160
+ if (deliveryTenantId === undefined) {
3161
+ throw new Error(`Multitenant projection inbox replay for "${context.name}" requires tenantId.`);
3162
+ }
3163
+ const envelopeTenantId = RepositoryTenants.readEventTenant(event);
3164
+ if (envelopeTenantId === undefined) {
3165
+ throw new Error("Projection inbox replay requires stored event tenant metadata.");
3166
+ }
3167
+ if (!RepositoryTenants.equal(envelopeTenantId, deliveryTenantId)) {
3168
+ throw new Error("Projection inbox replay stored event tenant does not match.");
3169
+ }
3170
+ },
3171
+ validatePmReplayTenant(context, deliveryTenantId, event) {
3172
+ if (!context.multitenant) {
3173
+ return;
3174
+ }
3175
+ if (deliveryTenantId === undefined) {
3176
+ throw new Error(`Multitenant Entity Inbox replay for "${context.name}" requires tenantId.`);
3177
+ }
3178
+ const envelopeTenantId = RepositoryTenants.readEventTenant(event);
3179
+ if (envelopeTenantId === undefined) {
3180
+ throw new Error("Entity Inbox replay requires stored event tenant metadata.");
3181
+ }
3182
+ if (!RepositoryTenants.equal(envelopeTenantId, deliveryTenantId)) {
3183
+ throw new Error("Entity Inbox replay stored event tenant does not match.");
3184
+ }
3185
+ },
3186
+ replayCommandRoute(repository, routing, message, command) {
3187
+ const expectedTargetTypeUrl = TypeUrls.derive(repository.stateSchema);
3188
+ if (message.inboxId.targetTypeUrl !== expectedTargetTypeUrl) {
3189
+ throw new Error("Entity Inbox replay stored target type does not match the routed repository.");
3190
+ }
3191
+ const commandMessage = EntityInvocation.requireSignalMessage(command.message, "command");
3192
+ const schema = RepositoryRoutes.schemaForTypeUrl(routing.commandSchemas, commandMessage.typeUrl, "command");
3193
+ return Object.freeze({
3194
+ entityId: InboxMessages.targetEntityId(message.inboxId.targetId, repository.idField),
3195
+ messageFullTypeName: schema.typeName,
3196
+ invocation: "deferred",
3197
+ });
3198
+ },
3199
+ replayEventRoute(repository, routing, message, event, replayName) {
3200
+ const expectedTargetTypeUrl = TypeUrls.derive(repository.stateSchema);
3201
+ if (message.inboxId.targetTypeUrl !== expectedTargetTypeUrl) {
3202
+ throw new Error(`${replayName} stored target type does not match the routed repository.`);
3203
+ }
3204
+ const eventMessage = EntityInvocation.requireSignalMessage(event.message, "event");
3205
+ const schema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, eventMessage.typeUrl, "event");
3206
+ const entityId = InboxMessages.targetEntityId(message.inboxId.targetId, repository.idField);
3207
+ const entityIds = Object.freeze([entityId]);
3208
+ return Object.freeze({
3209
+ entityIds,
3210
+ messageFullTypeName: schema.typeName,
3211
+ invocation: "deferred",
3212
+ });
3213
+ },
3214
+ replayStateUpdateRoute(repository, routing, message, event) {
3215
+ const expectedTargetTypeUrl = TypeUrls.derive(repository.stateSchema);
3216
+ if (message.inboxId.targetTypeUrl !== expectedTargetTypeUrl) {
3217
+ throw new Error("Projection inbox replay stored target type does not match the routed repository.");
3218
+ }
3219
+ const update = RepositoryRoutes.decodeStateUpdate(event, routing.stateSchemas, "Projection inbox replay");
3220
+ if (update === undefined ||
3221
+ (routing.stateSubscriptions.get(update.schema.typeName)?.length ?? 0) === 0) {
3222
+ throw new Error("Projection inbox replay requires a readable stored Entity state update.");
3223
+ }
3224
+ const { schema, state } = update;
3225
+ const entityId = InboxMessages.targetEntityId(message.inboxId.targetId, repository.idField);
3226
+ const entityIds = Object.freeze([entityId]);
3227
+ return Object.freeze({
3228
+ entityIds,
3229
+ messageFullTypeName: schema.typeName,
3230
+ state,
3231
+ subscribers: Object.freeze((() => {
3232
+ const candidates = routing.stateSubscriptions.get(schema.typeName) ?? [];
3233
+ return candidates.filter((subscriber) => (subscriber.handler.origin === "external") === (event.context?.external === true));
3234
+ })()),
3235
+ invocation: "deferred",
3236
+ });
3237
+ },
3238
+ };
3239
+ Object.freeze(InboxReplay);
3240
+ /**
3241
+ * Internal inbox handoff operations.
3242
+ */
3243
+ const InboxHandoff = {
3244
+ async handoffEntityCommand(repository, runtime, command) {
3245
+ const route = repository.routeCommand(command);
3246
+ const commandId = RepositorySignals.requireCommandId(command);
3247
+ const whenReceived = new Date();
3248
+ const keepUntil = new Date(whenReceived.getTime() + inboxDedupMs);
3249
+ const deliveryTenantId = RepositoryTenants.requireCommandTenant(runtime.context, command);
3250
+ const delivery = new Delivery({
3251
+ context: RepositoryTenants.entityInboxDeliveryContext(runtime.context, deliveryTenantId),
3252
+ storageFactory: runtime.storageFactory,
3253
+ strategy: runtime.entityInbox.strategy(),
3254
+ });
3255
+ await runtime.entityInbox.receive(delivery, {
3256
+ inboxId: {
3257
+ targetId: InboxMessages.inboxTargetId(route.entityId, repository.idField),
3258
+ targetTypeUrl: TypeUrls.derive(repository.stateSchema),
3259
+ },
3260
+ signalId: commandId.uuid,
3261
+ signal: AnyMessages.pack(CommandSchema, command, { validate: false }),
3262
+ label: "HANDLE_COMMAND",
3263
+ status: "TO_DELIVER",
3264
+ keepUntil,
3265
+ }, deliveryTenantId);
3266
+ },
3267
+ async handoffProjectionEvent(repository, runtime, event, entityId) {
3268
+ const eventId = RepositorySignals.requireEventId(event);
3269
+ const whenReceived = new Date();
3270
+ const keepUntil = new Date(whenReceived.getTime() + inboxDedupMs);
3271
+ const deliveryTenantId = RepositoryTenants.requireProjectionTenant(runtime.context, event);
3272
+ const delivery = new Delivery({
3273
+ context: RepositoryTenants.projectionDeliveryContext(runtime.context, deliveryTenantId),
3274
+ storageFactory: runtime.storageFactory,
3275
+ });
3276
+ await runtime.projectionInbox.receive(delivery, {
3277
+ inboxId: {
3278
+ targetId: InboxMessages.inboxTargetId(entityId, repository.idField),
3279
+ targetTypeUrl: TypeUrls.derive(repository.stateSchema),
3280
+ },
3281
+ signalId: eventId.value,
3282
+ signal: AnyMessages.pack(EventSchema, event, { validate: false }),
3283
+ label: "UPDATE_SUBSCRIBER",
3284
+ status: "TO_DELIVER",
3285
+ shard: ShardIndex.single(),
3286
+ keepUntil,
3287
+ }, deliveryTenantId);
3288
+ },
3289
+ async handoffPmEvent(repository, runtime, event, entityId) {
3290
+ const eventId = RepositorySignals.requireEventId(event);
3291
+ const whenReceived = new Date();
3292
+ const keepUntil = new Date(whenReceived.getTime() + inboxDedupMs);
3293
+ const deliveryTenantId = RepositoryTenants.requirePmEventTenant(runtime.context, event);
3294
+ const delivery = new Delivery({
3295
+ context: RepositoryTenants.entityInboxDeliveryContext(runtime.context, deliveryTenantId),
3296
+ storageFactory: runtime.storageFactory,
3297
+ strategy: runtime.entityInbox.strategy(),
3298
+ });
3299
+ await runtime.entityInbox.receive(delivery, InboxHandoff.pmEventInboxInput(repository, eventId.value, event, entityId, keepUntil), deliveryTenantId);
3300
+ },
3301
+ async handoffPmEvents(repository, runtime, event, entityIds) {
3302
+ const eventId = RepositorySignals.requireEventId(event);
3303
+ const whenReceived = new Date();
3304
+ const keepUntil = new Date(whenReceived.getTime() + inboxDedupMs);
3305
+ const deliveryTenantId = RepositoryTenants.requirePmEventTenant(runtime.context, event);
3306
+ const delivery = new Delivery({
3307
+ context: RepositoryTenants.entityInboxDeliveryContext(runtime.context, deliveryTenantId),
3308
+ storageFactory: runtime.storageFactory,
3309
+ strategy: runtime.entityInbox.strategy(),
3310
+ });
3311
+ const inputs = entityIds.map((entityId) => InboxHandoff.pmEventInboxInput(repository, eventId.value, event, entityId, keepUntil));
3312
+ await runtime.entityInbox.receiveAll(delivery, inputs, deliveryTenantId);
3313
+ },
3314
+ pmEventInboxInput(repository, signalId, event, entityId, keepUntil) {
3315
+ return {
3316
+ inboxId: {
3317
+ targetId: InboxMessages.inboxTargetId(entityId, repository.idField),
3318
+ targetTypeUrl: TypeUrls.derive(repository.stateSchema),
3319
+ },
3320
+ signalId,
3321
+ signal: AnyMessages.pack(EventSchema, event, { validate: false }),
3322
+ label: "REACT_UPON_EVENT",
3323
+ status: "TO_DELIVER",
3324
+ keepUntil,
3325
+ };
3326
+ },
3327
+ };
3328
+ Object.freeze(InboxHandoff);
3329
+ /**
3330
+ * Internal repository dispatch operations.
3331
+ */
3332
+ const RepositoryDispatch = {
3333
+ createRepositoryDispatchers(repository, routing) {
3334
+ const acceptedEventRoutes = new WeakMap();
3335
+ const acceptedStateRoutes = new WeakMap();
3336
+ return Object.freeze({
3337
+ command: RepositoryDispatch.commandDispatcher(repository, routing),
3338
+ event: RepositoryDispatch.eventDispatcher(repository, routing, acceptedEventRoutes),
3339
+ systemEvent: RepositoryDispatch.stateDispatcher(repository, routing, acceptedStateRoutes),
3340
+ });
3341
+ },
3342
+ commandDispatcher(repository, routing) {
3343
+ return routing.commandSchemas.length === 0
3344
+ ? undefined
3345
+ : Object.freeze({
3346
+ messageSchemas: () => routing.commandSchemas,
3347
+ dispatch: (command) => RepositoryDispatch.dispatchRepositoryCommand(repository, routing, command),
3348
+ });
3349
+ },
3350
+ eventDispatcher(repository, routing, accepted) {
3351
+ if (routing.eventSchemas.length === 0)
3352
+ return undefined;
3353
+ const dispatcher = Object.freeze({
3354
+ messageSchemas: () => routing.eventSchemas,
3355
+ externalEventSchemas: () => routing.externalEventSchemas,
3356
+ accept: (event) => {
3357
+ accepted.set(event, repository.routeEvent(event));
3358
+ return Promise.resolve();
3359
+ },
3360
+ dispatch: (event) => {
3361
+ const route = accepted.get(event);
3362
+ accepted.delete(event);
3363
+ return RepositoryDispatch.dispatchRepositoryEvent(repository, routing, event, route);
3364
+ },
3365
+ });
3366
+ return EventDispatcherOriginSchemas.define(dispatcher, routing.domesticEventSchemas, routing.externalEventSchemas);
3367
+ },
3368
+ stateDispatcher(repository, routing, accepted) {
3369
+ if (routing.stateSchemas.length === 0)
3370
+ return undefined;
3371
+ const hasOrigin = (origin) => [...routing.stateSubscriptions.values()].some((values) => values.some((value) => value.handler.origin === origin));
3372
+ const schema = EntityLog.EntityStateChangedSchema;
3373
+ const dispatcher = Object.freeze({
3374
+ messageSchemas: () => Object.freeze([schema]),
3375
+ externalEventSchemas: () => (hasOrigin("external") ? [schema] : []),
3376
+ accept: (event) => {
3377
+ accepted.set(event, routing.routeStateUpdate(event) ?? null);
3378
+ return Promise.resolve();
3379
+ },
3380
+ dispatch: (event) => RepositoryDispatch.dispatchAcceptedState(repository, routing, event, accepted),
3381
+ });
3382
+ return EventDispatcherOriginSchemas.define(dispatcher, hasOrigin("domestic") ? [schema] : [], hasOrigin("external") ? [schema] : []);
3383
+ },
3384
+ dispatchAcceptedState(repository, routing, event, accepted) {
3385
+ const route = accepted.get(event);
3386
+ accepted.delete(event);
3387
+ return route === null
3388
+ ? Promise.resolve()
3389
+ : RepositoryDispatch.dispatchRepositoryStateUpdate(repository, routing, event, route);
3390
+ },
3391
+ createEntityInboxTarget(repository, routing) {
3392
+ if ((repository.entityFamily !== "aggregate" && repository.entityFamily !== "process-manager") ||
3393
+ (routing.commandSchemas.length === 0 && routing.eventSchemas.length === 0)) {
3394
+ return undefined;
3395
+ }
3396
+ return Object.freeze({
3397
+ targetTypeUrl: TypeUrls.derive(repository.stateSchema),
3398
+ labels: Object.freeze([
3399
+ ...(routing.commandSchemas.length === 0 ? [] : ["HANDLE_COMMAND"]),
3400
+ ...(repository.entityFamily === "process-manager" && routing.eventSchemas.length > 0
3401
+ ? ["REACT_UPON_EVENT"]
3402
+ : []),
3403
+ ]),
3404
+ replay: (message, deliveryTenantId) => repository.entityFamily === "aggregate"
3405
+ ? InboxReplay.replayAggregateCommand(repository, routing, message, deliveryTenantId)
3406
+ : InboxReplay.replayPmInbox(repository, routing, message, deliveryTenantId),
3407
+ });
3408
+ },
3409
+ createProjectionInboxTarget(repository, routing) {
3410
+ if (repository.entityFamily !== "projection" ||
3411
+ (routing.eventSchemas.length === 0 && routing.stateSchemas.length === 0)) {
3412
+ return undefined;
3413
+ }
3414
+ return Object.freeze({
3415
+ targetTypeUrl: TypeUrls.derive(repository.stateSchema),
3416
+ replay: (message, deliveryTenantId) => InboxReplay.replayProjectionMessage(repository, routing, message, deliveryTenantId),
3417
+ });
3418
+ },
3419
+ createProjectionDirectDispatch(repository, routing) {
3420
+ return (event, rebuild) => {
3421
+ const runtime = repositoryRuntimes.get(repository);
3422
+ if (runtime === undefined) {
3423
+ void repository.routeEvent(event);
3424
+ return Promise.resolve();
3425
+ }
3426
+ return new ProjectionEventExecution(repository, routing, runtime, event, rebuild).runDirect();
3427
+ };
3428
+ },
3429
+ async dispatchRepositoryEvent(repository, routing, event, acceptedRoute) {
3430
+ const runtime = repositoryRuntimes.get(repository);
3431
+ if (runtime === undefined) {
3432
+ if (acceptedRoute === undefined)
3433
+ void repository.routeEvent(event);
3434
+ return;
3435
+ }
3436
+ const route = acceptedRoute ?? repository.routeEvent(event);
3437
+ switch (repository.entityFamily) {
3438
+ case "aggregate":
3439
+ await RepositoryDispatch.dispatchAggregateEvent(repository, routing, runtime, event, route);
3440
+ return;
3441
+ case "process-manager":
3442
+ await new ProcessManagerEventExecution(repository, routing, runtime, event).run(route);
3443
+ return;
3444
+ case "projection":
3445
+ await new ProjectionEventExecution(repository, routing, runtime, event).run(route);
3446
+ return;
3447
+ }
3448
+ },
3449
+ async dispatchAggregateEvent(repository, routing, runtime, event, route) {
3450
+ const execution = new AggregateEventExecution(repository, routing, runtime, event);
3451
+ for (const entityId of route.entityIds) {
3452
+ await DispatchGuards.guardedEntityEventDispatch(repository, runtime, event, entityId, () => execution.runTarget(entityId, route));
3453
+ }
3454
+ },
3455
+ async dispatchRepositoryStateUpdate(repository, routing, event, acceptedRoute) {
3456
+ const runtime = repositoryRuntimes.get(repository);
3457
+ if (runtime === undefined) {
3458
+ if (acceptedRoute === undefined)
3459
+ void routing.routeStateUpdate(event);
3460
+ return;
3461
+ }
3462
+ const route = acceptedRoute ?? routing.routeStateUpdate(event);
3463
+ if (route === undefined)
3464
+ return;
3465
+ for (const entityId of route.entityIds) {
3466
+ await InboxHandoff.handoffProjectionEvent(repository, runtime, event, entityId);
3467
+ }
3468
+ },
3469
+ async dispatchRepositoryCommand(repository, routing, command) {
3470
+ const runtime = repositoryRuntimes.get(repository);
3471
+ if (runtime === undefined) {
3472
+ void repository.routeCommand(command);
3473
+ return;
3474
+ }
3475
+ if (repository.entityFamily === "aggregate") {
3476
+ await InboxHandoff.handoffEntityCommand(repository, runtime, command);
3477
+ return;
3478
+ }
3479
+ if (repository.entityFamily === "process-manager") {
3480
+ await InboxHandoff.handoffEntityCommand(repository, runtime, command);
3481
+ return;
3482
+ }
3483
+ void repository.routeCommand(command);
3484
+ },
3485
+ };
3486
+ Object.freeze(RepositoryDispatch);
3487
+ /**
3488
+ * Defines continuation-aware repository history caches.
3489
+ */
3490
+ const repositoryHistory = {
3491
+ // prettier-ignore
3492
+ /**
3493
+ * Creates a cache that extends a repository history only when a caller requests more entries.
3494
+ *
3495
+ * @typeParam Entry - The history entry type.
3496
+ * @param load Loads entries before an optional continuation version.
3497
+ * @param versionOf Reads the optional version carried by an entry.
3498
+ * @param options Selects strictly descending-version and complete-group behavior.
3499
+ * @returns A cache with read and clear operations.
3500
+ */
3501
+ createCache(load, versionOf, options = {}) {
3502
+ return RepositoryHistoryInternals.createHistoryCache(load, versionOf, options);
3503
+ },
3504
+ };
3505
+ Object.freeze(repositoryHistory);
3506
+ /**
3507
+ * Defines continuation-aware repository history caches.
3508
+ */
3509
+ export const RepositoryHistory = repositoryHistory;
3510
+ //# sourceMappingURL=repository.js.map