@cratis/chronicle 3.0.0 → 3.1.1

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 (338) hide show
  1. package/ChronicleClient.ts +4 -3
  2. package/EventStore.ts +20 -3
  3. package/IEventStore.ts +16 -0
  4. package/README.md +40 -28
  5. package/auditing/CausationType.ts +10 -0
  6. package/compliance/ComplianceContracts.ts +84 -0
  7. package/compliance/IPIIManager.ts +17 -0
  8. package/compliance/PIIManager.ts +31 -0
  9. package/compliance/index.ts +2 -0
  10. package/connection/ChronicleConnection.ts +7 -1
  11. package/connection/ChronicleServices.ts +2 -0
  12. package/connection/Guid.ts +18 -0
  13. package/connection/callResults.spec.ts +90 -0
  14. package/connection/callResults.ts +112 -0
  15. package/dist/ChronicleClient.d.ts.map +1 -1
  16. package/dist/ChronicleClient.js +4 -3
  17. package/dist/ChronicleClient.js.map +1 -1
  18. package/dist/EventStore.d.ts +8 -0
  19. package/dist/EventStore.d.ts.map +1 -1
  20. package/dist/EventStore.js +16 -3
  21. package/dist/EventStore.js.map +1 -1
  22. package/dist/IEventStore.d.ts +12 -0
  23. package/dist/IEventStore.d.ts.map +1 -1
  24. package/dist/auditing/CausationType.d.ts +8 -0
  25. package/dist/auditing/CausationType.d.ts.map +1 -1
  26. package/dist/auditing/CausationType.js +8 -0
  27. package/dist/auditing/CausationType.js.map +1 -1
  28. package/dist/compliance/ComplianceContracts.d.ts +40 -0
  29. package/dist/compliance/ComplianceContracts.d.ts.map +1 -1
  30. package/dist/compliance/ComplianceContracts.js +71 -0
  31. package/dist/compliance/ComplianceContracts.js.map +1 -1
  32. package/dist/compliance/IPIIManager.d.ts +15 -0
  33. package/dist/compliance/IPIIManager.d.ts.map +1 -0
  34. package/dist/compliance/IPIIManager.js +4 -0
  35. package/dist/compliance/IPIIManager.js.map +1 -0
  36. package/dist/compliance/PIIManager.d.ts +20 -0
  37. package/dist/compliance/PIIManager.d.ts.map +1 -0
  38. package/dist/compliance/PIIManager.js +30 -0
  39. package/dist/compliance/PIIManager.js.map +1 -0
  40. package/dist/compliance/index.d.ts +2 -0
  41. package/dist/compliance/index.d.ts.map +1 -1
  42. package/dist/compliance/index.js +1 -0
  43. package/dist/compliance/index.js.map +1 -1
  44. package/dist/connection/ChronicleConnection.d.ts +1 -0
  45. package/dist/connection/ChronicleConnection.d.ts.map +1 -1
  46. package/dist/connection/ChronicleConnection.js +6 -1
  47. package/dist/connection/ChronicleConnection.js.map +1 -1
  48. package/dist/connection/ChronicleServices.d.ts +2 -0
  49. package/dist/connection/ChronicleServices.d.ts.map +1 -1
  50. package/dist/connection/Guid.d.ts +5 -0
  51. package/dist/connection/Guid.d.ts.map +1 -1
  52. package/dist/connection/Guid.js +15 -0
  53. package/dist/connection/Guid.js.map +1 -1
  54. package/dist/connection/callResults.d.ts +69 -0
  55. package/dist/connection/callResults.d.ts.map +1 -0
  56. package/dist/connection/callResults.js +74 -0
  57. package/dist/connection/callResults.js.map +1 -0
  58. package/dist/eventSequences/AppendOperationsBroadcaster.d.ts +16 -0
  59. package/dist/eventSequences/AppendOperationsBroadcaster.d.ts.map +1 -0
  60. package/dist/eventSequences/AppendOperationsBroadcaster.js +77 -0
  61. package/dist/eventSequences/AppendOperationsBroadcaster.js.map +1 -0
  62. package/dist/eventSequences/AppendOptions.d.ts +7 -0
  63. package/dist/eventSequences/AppendOptions.d.ts.map +1 -1
  64. package/dist/eventSequences/AppendResult.d.ts +14 -0
  65. package/dist/eventSequences/AppendResult.d.ts.map +1 -1
  66. package/dist/eventSequences/AppendedEventWithResult.d.ts +13 -0
  67. package/dist/eventSequences/AppendedEventWithResult.d.ts.map +1 -0
  68. package/dist/eventSequences/AppendedEventWithResult.js +4 -0
  69. package/dist/eventSequences/AppendedEventWithResult.js.map +1 -0
  70. package/dist/eventSequences/CompleteStreamError.d.ts +17 -0
  71. package/dist/eventSequences/CompleteStreamError.d.ts.map +1 -0
  72. package/dist/eventSequences/CompleteStreamError.js +20 -0
  73. package/dist/eventSequences/CompleteStreamError.js.map +1 -0
  74. package/dist/eventSequences/CompleteStreamResult.d.ts +14 -0
  75. package/dist/eventSequences/CompleteStreamResult.d.ts.map +1 -0
  76. package/dist/eventSequences/CompleteStreamResult.js +4 -0
  77. package/dist/eventSequences/CompleteStreamResult.js.map +1 -0
  78. package/dist/eventSequences/ConcurrencyViolation.d.ts +13 -0
  79. package/dist/eventSequences/ConcurrencyViolation.d.ts.map +1 -0
  80. package/dist/eventSequences/ConcurrencyViolation.js +4 -0
  81. package/dist/eventSequences/ConcurrencyViolation.js.map +1 -0
  82. package/dist/eventSequences/EventForEventSourceId.d.ts +8 -0
  83. package/dist/eventSequences/EventForEventSourceId.d.ts.map +1 -1
  84. package/dist/eventSequences/EventSequence.d.ts +35 -1
  85. package/dist/eventSequences/EventSequence.d.ts.map +1 -1
  86. package/dist/eventSequences/EventSequence.js +350 -19
  87. package/dist/eventSequences/EventSequence.js.map +1 -1
  88. package/dist/eventSequences/IEventSequence.d.ts +85 -1
  89. package/dist/eventSequences/IEventSequence.d.ts.map +1 -1
  90. package/dist/eventSequences/WaitForCompletionResult.d.ts +11 -0
  91. package/dist/eventSequences/WaitForCompletionResult.d.ts.map +1 -0
  92. package/dist/eventSequences/WaitForCompletionResult.js +4 -0
  93. package/dist/eventSequences/WaitForCompletionResult.js.map +1 -0
  94. package/dist/eventSequences/index.d.ts +5 -0
  95. package/dist/eventSequences/index.d.ts.map +1 -1
  96. package/dist/eventSequences/index.js +1 -0
  97. package/dist/eventSequences/index.js.map +1 -1
  98. package/dist/eventStoreSubscriptions/EventStoreSubscriptions.d.ts +3 -0
  99. package/dist/eventStoreSubscriptions/EventStoreSubscriptions.d.ts.map +1 -1
  100. package/dist/eventStoreSubscriptions/EventStoreSubscriptions.js +9 -0
  101. package/dist/eventStoreSubscriptions/EventStoreSubscriptions.js.map +1 -1
  102. package/dist/eventStoreSubscriptions/IEventStoreSubscriptions.d.ts +6 -0
  103. package/dist/eventStoreSubscriptions/IEventStoreSubscriptions.d.ts.map +1 -1
  104. package/dist/eventStoreSubscriptions/contracts.d.ts +18 -0
  105. package/dist/eventStoreSubscriptions/contracts.d.ts.map +1 -1
  106. package/dist/eventStoreSubscriptions/contracts.js +106 -0
  107. package/dist/eventStoreSubscriptions/contracts.js.map +1 -1
  108. package/dist/events/constraints/Constraints.js +4 -4
  109. package/dist/events/constraints/Constraints.js.map +1 -1
  110. package/dist/externalServices/ExternalServiceBuilder.d.ts +42 -0
  111. package/dist/externalServices/ExternalServiceBuilder.d.ts.map +1 -0
  112. package/dist/externalServices/ExternalServiceBuilder.js +110 -0
  113. package/dist/externalServices/ExternalServiceBuilder.js.map +1 -0
  114. package/dist/externalServices/ExternalServices.d.ts +19 -0
  115. package/dist/externalServices/ExternalServices.d.ts.map +1 -0
  116. package/dist/externalServices/ExternalServices.js +30 -0
  117. package/dist/externalServices/ExternalServices.js.map +1 -0
  118. package/dist/externalServices/ExternalServicesContracts.d.ts +137 -0
  119. package/dist/externalServices/ExternalServicesContracts.d.ts.map +1 -0
  120. package/dist/externalServices/ExternalServicesContracts.js +654 -0
  121. package/dist/externalServices/ExternalServicesContracts.js.map +1 -0
  122. package/dist/externalServices/IExternalServiceBuilder.d.ts +67 -0
  123. package/dist/externalServices/IExternalServiceBuilder.d.ts.map +1 -0
  124. package/dist/externalServices/IExternalServiceBuilder.js +4 -0
  125. package/dist/externalServices/IExternalServiceBuilder.js.map +1 -0
  126. package/dist/externalServices/IExternalServices.d.ts +13 -0
  127. package/dist/externalServices/IExternalServices.d.ts.map +1 -0
  128. package/dist/externalServices/IExternalServices.js +4 -0
  129. package/dist/externalServices/IExternalServices.js.map +1 -0
  130. package/dist/externalServices/index.d.ts +5 -0
  131. package/dist/externalServices/index.d.ts.map +1 -0
  132. package/dist/externalServices/index.js +5 -0
  133. package/dist/externalServices/index.js.map +1 -0
  134. package/dist/identities/IIdentityManager.d.ts +17 -0
  135. package/dist/identities/IIdentityManager.d.ts.map +1 -0
  136. package/dist/identities/IIdentityManager.js +4 -0
  137. package/dist/identities/IIdentityManager.js.map +1 -0
  138. package/dist/identities/IdentityManager.d.ts +20 -0
  139. package/dist/identities/IdentityManager.d.ts.map +1 -0
  140. package/dist/identities/IdentityManager.js +31 -0
  141. package/dist/identities/IdentityManager.js.map +1 -0
  142. package/dist/identities/index.d.ts +3 -0
  143. package/dist/identities/index.d.ts.map +1 -0
  144. package/dist/identities/index.js +4 -0
  145. package/dist/identities/index.js.map +1 -0
  146. package/dist/index.d.ts +5 -1
  147. package/dist/index.d.ts.map +1 -1
  148. package/dist/index.js +5 -1
  149. package/dist/index.js.map +1 -1
  150. package/dist/jobs/IJobs.d.ts +4 -4
  151. package/dist/jobs/IJobs.d.ts.map +1 -1
  152. package/dist/jobs/Jobs.d.ts +4 -4
  153. package/dist/jobs/Jobs.d.ts.map +1 -1
  154. package/dist/jobs/Jobs.js +12 -10
  155. package/dist/jobs/Jobs.js.map +1 -1
  156. package/dist/observation/FailedPartition.d.ts +15 -0
  157. package/dist/observation/FailedPartition.d.ts.map +1 -0
  158. package/dist/observation/FailedPartition.js +4 -0
  159. package/dist/observation/FailedPartition.js.map +1 -0
  160. package/dist/observation/FailedPartitionAttempt.d.ts +15 -0
  161. package/dist/observation/FailedPartitionAttempt.d.ts.map +1 -0
  162. package/dist/observation/FailedPartitionAttempt.js +4 -0
  163. package/dist/observation/FailedPartitionAttempt.js.map +1 -0
  164. package/dist/observation/FailedPartitions.d.ts +24 -0
  165. package/dist/observation/FailedPartitions.d.ts.map +1 -0
  166. package/dist/observation/FailedPartitions.js +39 -0
  167. package/dist/observation/FailedPartitions.js.map +1 -0
  168. package/dist/observation/ICanBeNotifiedWhenPartitionReplayed.d.ts +19 -0
  169. package/dist/observation/ICanBeNotifiedWhenPartitionReplayed.d.ts.map +1 -0
  170. package/dist/observation/ICanBeNotifiedWhenPartitionReplayed.js +4 -0
  171. package/dist/observation/ICanBeNotifiedWhenPartitionReplayed.js.map +1 -0
  172. package/dist/observation/ICanBeNotifiedWhenReplay.d.ts +17 -0
  173. package/dist/observation/ICanBeNotifiedWhenReplay.d.ts.map +1 -0
  174. package/dist/observation/ICanBeNotifiedWhenReplay.js +4 -0
  175. package/dist/observation/ICanBeNotifiedWhenReplay.js.map +1 -0
  176. package/dist/observation/IFailedPartitions.d.ts +18 -0
  177. package/dist/observation/IFailedPartitions.d.ts.map +1 -0
  178. package/dist/observation/IFailedPartitions.js +4 -0
  179. package/dist/observation/IFailedPartitions.js.map +1 -0
  180. package/dist/observation/index.d.ts +6 -0
  181. package/dist/observation/index.d.ts.map +1 -1
  182. package/dist/observation/index.js +1 -0
  183. package/dist/observation/index.js.map +1 -1
  184. package/dist/observation/notifyReplayLifecycle.d.ts +13 -0
  185. package/dist/observation/notifyReplayLifecycle.d.ts.map +1 -0
  186. package/dist/observation/notifyReplayLifecycle.js +54 -0
  187. package/dist/observation/notifyReplayLifecycle.js.map +1 -0
  188. package/dist/observation/toClientFailedPartition.d.ts +9 -0
  189. package/dist/observation/toClientFailedPartition.d.ts.map +1 -0
  190. package/dist/observation/toClientFailedPartition.js +23 -0
  191. package/dist/observation/toClientFailedPartition.js.map +1 -0
  192. package/dist/projections/Projections.d.ts +0 -4
  193. package/dist/projections/Projections.d.ts.map +1 -1
  194. package/dist/projections/Projections.js +25 -103
  195. package/dist/projections/Projections.js.map +1 -1
  196. package/dist/projections/declarative/AddBuilder.d.ts +17 -0
  197. package/dist/projections/declarative/AddBuilder.d.ts.map +1 -0
  198. package/dist/projections/declarative/AddBuilder.js +28 -0
  199. package/dist/projections/declarative/AddBuilder.js.map +1 -0
  200. package/dist/projections/declarative/AddChildBuilder.d.ts +56 -0
  201. package/dist/projections/declarative/AddChildBuilder.d.ts.map +1 -0
  202. package/dist/projections/declarative/AddChildBuilder.js +72 -0
  203. package/dist/projections/declarative/AddChildBuilder.js.map +1 -0
  204. package/dist/projections/declarative/ChildrenBuilder.d.ts +28 -0
  205. package/dist/projections/declarative/ChildrenBuilder.d.ts.map +1 -0
  206. package/dist/projections/declarative/ChildrenBuilder.js +72 -0
  207. package/dist/projections/declarative/ChildrenBuilder.js.map +1 -0
  208. package/dist/projections/declarative/CompositeKeyBuilder.d.ts +16 -0
  209. package/dist/projections/declarative/CompositeKeyBuilder.d.ts.map +1 -0
  210. package/dist/projections/declarative/CompositeKeyBuilder.js +29 -0
  211. package/dist/projections/declarative/CompositeKeyBuilder.js.map +1 -0
  212. package/dist/projections/declarative/FromBuilder.d.ts +8 -6
  213. package/dist/projections/declarative/FromBuilder.d.ts.map +1 -1
  214. package/dist/projections/declarative/FromBuilder.js +44 -14
  215. package/dist/projections/declarative/FromBuilder.js.map +1 -1
  216. package/dist/projections/declarative/ICompositeKeyBuilder.d.ts +5 -0
  217. package/dist/projections/declarative/ICompositeKeyBuilder.d.ts.map +1 -1
  218. package/dist/projections/declarative/JoinBuilder.d.ts +7 -5
  219. package/dist/projections/declarative/JoinBuilder.d.ts.map +1 -1
  220. package/dist/projections/declarative/JoinBuilder.js +50 -16
  221. package/dist/projections/declarative/JoinBuilder.js.map +1 -1
  222. package/dist/projections/declarative/NestedBuilder.d.ts +24 -0
  223. package/dist/projections/declarative/NestedBuilder.d.ts.map +1 -0
  224. package/dist/projections/declarative/NestedBuilder.js +60 -0
  225. package/dist/projections/declarative/NestedBuilder.js.map +1 -0
  226. package/dist/projections/declarative/ProjectionBuilderCore.d.ts +127 -0
  227. package/dist/projections/declarative/ProjectionBuilderCore.d.ts.map +1 -0
  228. package/dist/projections/declarative/ProjectionBuilderCore.js +180 -0
  229. package/dist/projections/declarative/ProjectionBuilderCore.js.map +1 -0
  230. package/dist/projections/declarative/ProjectionBuilderFor.d.ts +6 -32
  231. package/dist/projections/declarative/ProjectionBuilderFor.d.ts.map +1 -1
  232. package/dist/projections/declarative/ProjectionBuilderFor.js +24 -118
  233. package/dist/projections/declarative/ProjectionBuilderFor.js.map +1 -1
  234. package/dist/projections/declarative/SubtractBuilder.d.ts +17 -0
  235. package/dist/projections/declarative/SubtractBuilder.d.ts.map +1 -0
  236. package/dist/projections/declarative/SubtractBuilder.js +28 -0
  237. package/dist/projections/declarative/SubtractBuilder.js.map +1 -0
  238. package/dist/projections/declarative/index.d.ts +9 -0
  239. package/dist/projections/declarative/index.d.ts.map +1 -1
  240. package/dist/projections/declarative/index.js +8 -0
  241. package/dist/projections/declarative/index.js.map +1 -1
  242. package/dist/projections/modelBound/childrenAndNestedBuilder.d.ts +90 -0
  243. package/dist/projections/modelBound/childrenAndNestedBuilder.d.ts.map +1 -0
  244. package/dist/projections/modelBound/childrenAndNestedBuilder.js +290 -0
  245. package/dist/projections/modelBound/childrenAndNestedBuilder.js.map +1 -0
  246. package/dist/reactors/ReactorSideEffects.d.ts +29 -0
  247. package/dist/reactors/ReactorSideEffects.d.ts.map +1 -0
  248. package/dist/reactors/ReactorSideEffects.js +70 -0
  249. package/dist/reactors/ReactorSideEffects.js.map +1 -0
  250. package/dist/reactors/Reactors.d.ts +4 -1
  251. package/dist/reactors/Reactors.d.ts.map +1 -1
  252. package/dist/reactors/Reactors.js +23 -4
  253. package/dist/reactors/Reactors.js.map +1 -1
  254. package/dist/reducers/Reducers.d.ts.map +1 -1
  255. package/dist/reducers/Reducers.js +15 -3
  256. package/dist/reducers/Reducers.js.map +1 -1
  257. package/dist/reducers/reducer.d.ts +9 -1
  258. package/dist/reducers/reducer.d.ts.map +1 -1
  259. package/dist/reducers/reducer.js +4 -2
  260. package/dist/reducers/reducer.js.map +1 -1
  261. package/dist/transactions/IUnitOfWork.d.ts +18 -0
  262. package/dist/transactions/IUnitOfWork.d.ts.map +1 -1
  263. package/dist/transactions/UnitOfWork.d.ts +9 -0
  264. package/dist/transactions/UnitOfWork.d.ts.map +1 -1
  265. package/dist/transactions/UnitOfWork.js +14 -0
  266. package/dist/transactions/UnitOfWork.js.map +1 -1
  267. package/dist/tsconfig.tsbuildinfo +1 -1
  268. package/eventSequences/AppendOperationsBroadcaster.ts +83 -0
  269. package/eventSequences/AppendOptions.ts +8 -0
  270. package/eventSequences/AppendResult.ts +16 -0
  271. package/eventSequences/AppendedEventWithResult.ts +17 -0
  272. package/eventSequences/CompleteStreamError.ts +20 -0
  273. package/eventSequences/CompleteStreamResult.ts +13 -0
  274. package/eventSequences/ConcurrencyViolation.ts +18 -0
  275. package/eventSequences/EventForEventSourceId.ts +12 -0
  276. package/eventSequences/EventSequence.spec.ts +588 -0
  277. package/eventSequences/EventSequence.ts +406 -20
  278. package/eventSequences/IEventSequence.ts +109 -1
  279. package/eventSequences/WaitForCompletionResult.ts +15 -0
  280. package/eventSequences/index.ts +5 -0
  281. package/eventStoreSubscriptions/EventStoreSubscriptions.spec.ts +52 -0
  282. package/eventStoreSubscriptions/EventStoreSubscriptions.ts +19 -1
  283. package/eventStoreSubscriptions/IEventStoreSubscriptions.ts +7 -0
  284. package/eventStoreSubscriptions/contracts.ts +126 -0
  285. package/events/constraints/Constraints.ts +4 -4
  286. package/externalServices/ExternalServiceBuilder.ts +128 -0
  287. package/externalServices/ExternalServices.ts +34 -0
  288. package/externalServices/ExternalServicesContracts.ts +709 -0
  289. package/externalServices/IExternalServiceBuilder.ts +76 -0
  290. package/externalServices/IExternalServices.ts +16 -0
  291. package/externalServices/index.ts +7 -0
  292. package/identities/IIdentityManager.ts +19 -0
  293. package/identities/IdentityManager.ts +32 -0
  294. package/identities/index.ts +5 -0
  295. package/index.ts +6 -0
  296. package/jobs/IJobs.ts +4 -4
  297. package/jobs/Jobs.ts +16 -14
  298. package/observation/FailedPartition.ts +21 -0
  299. package/observation/FailedPartitionAttempt.ts +21 -0
  300. package/observation/FailedPartitions.spec.ts +75 -0
  301. package/observation/FailedPartitions.ts +44 -0
  302. package/observation/ICanBeNotifiedWhenPartitionReplayed.ts +22 -0
  303. package/observation/ICanBeNotifiedWhenReplay.ts +20 -0
  304. package/observation/IFailedPartitions.ts +22 -0
  305. package/observation/index.ts +6 -0
  306. package/observation/notifyReplayLifecycle.spec.ts +98 -0
  307. package/observation/notifyReplayLifecycle.ts +61 -0
  308. package/observation/toClientFailedPartition.ts +26 -0
  309. package/package.json +28 -3
  310. package/projections/Projections.childrenAndNested.spec.ts +188 -0
  311. package/projections/Projections.spec.ts +131 -0
  312. package/projections/Projections.ts +37 -119
  313. package/projections/declarative/AddBuilder.ts +28 -0
  314. package/projections/declarative/AddChildBuilder.ts +97 -0
  315. package/projections/declarative/ChildrenBuilder.spec.ts +157 -0
  316. package/projections/declarative/ChildrenBuilder.ts +90 -0
  317. package/projections/declarative/CompositeKeyBuilder.spec.ts +117 -0
  318. package/projections/declarative/CompositeKeyBuilder.ts +38 -0
  319. package/projections/declarative/FromBuilder.ts +60 -15
  320. package/projections/declarative/ICompositeKeyBuilder.ts +6 -0
  321. package/projections/declarative/JoinBuilder.ts +66 -17
  322. package/projections/declarative/NestedBuilder.ts +76 -0
  323. package/projections/declarative/ProjectionBuilderCore.ts +257 -0
  324. package/projections/declarative/ProjectionBuilderFor.spec.ts +115 -0
  325. package/projections/declarative/ProjectionBuilderFor.ts +29 -168
  326. package/projections/declarative/SubtractBuilder.ts +28 -0
  327. package/projections/declarative/index.ts +9 -0
  328. package/projections/modelBound/childrenAndNestedBuilder.ts +354 -0
  329. package/reactors/ReactorSideEffects.spec.ts +182 -0
  330. package/reactors/ReactorSideEffects.ts +97 -0
  331. package/reactors/Reactors.ts +29 -4
  332. package/reducers/Reducers.spec.ts +87 -0
  333. package/reducers/Reducers.ts +15 -3
  334. package/reducers/reducer.spec.ts +40 -0
  335. package/reducers/reducer.ts +11 -2
  336. package/transactions/IUnitOfWork.ts +21 -0
  337. package/transactions/UnitOfWork.spec.ts +82 -0
  338. package/transactions/UnitOfWork.ts +20 -0
@@ -12,21 +12,25 @@ import { ChronicleConnection } from '../connection';
12
12
  import { toContractsGuid } from '../connection/Guid';
13
13
  import { WellKnownSinks } from '../sinks';
14
14
  import { EventSequenceId } from '../eventSequences/EventSequenceId';
15
- import { getEventTypeFor } from '../events/eventTypeDecorator';
16
15
  import { getReadModelMetadata } from '../readModels';
17
16
  import { TypeIntrospector } from '../types';
18
17
  import { IProjections } from './IProjections';
19
18
  import { getProjectionMetadata } from './declarative/projection';
20
19
  import { ProjectionBuilderFor } from './declarative/ProjectionBuilderFor';
21
20
  import type { IProjectionFor } from './declarative/IProjectionFor';
22
- import { getAddFromMetadata } from './modelBound/addFrom';
21
+ import {
22
+ applyPropertyMappings,
23
+ buildChildrenEntry,
24
+ buildNestedEntry,
25
+ ChildrenDefinitionLike,
26
+ ContractEventType,
27
+ FromRecord,
28
+ getEventTypeMapKey,
29
+ toContractEventType
30
+ } from './modelBound/childrenAndNestedBuilder';
23
31
  import { getChildrenFromMetadata } from './modelBound/childrenFrom';
24
- import { getClearWithClassMetadata, getClearWithPropertyMetadata } from './modelBound/clearWith';
25
- import { getCountMetadata } from './modelBound/count';
26
- import { getDecrementMetadata } from './modelBound/decrement';
27
32
  import { getFromEveryMetadata } from './modelBound/fromEvery';
28
33
  import { getFromEventMetadata, hasFromEventMetadata } from './modelBound/fromEvent';
29
- import { getIncrementMetadata } from './modelBound/increment';
30
34
  import { getJoinMetadata } from './modelBound/join';
31
35
  import { ProjectionId } from './ProjectionId';
32
36
  import { isNested } from './modelBound/nested';
@@ -34,12 +38,6 @@ import { isNotRewindable } from './modelBound/notRewindable';
34
38
  import { isPassive } from './modelBound/passive';
35
39
  import { getRemovedWithClassMetadata, getRemovedWithPropertyMetadata } from './modelBound/removedWith';
36
40
  import { getRemovedWithJoinClassMetadata, getRemovedWithJoinPropertyMetadata } from './modelBound/removedWithJoin';
37
- import { getSetFromMetadata } from './modelBound/setFrom';
38
- import { getSetFromContextMetadata } from './modelBound/setFromContext';
39
- import { getSetValueMetadata } from './modelBound/setValue';
40
- import { getSubtractFromMetadata } from './modelBound/subtractFrom';
41
-
42
- type ContractEventType = { Id: string; Generation: number; Tombstone: boolean };
43
41
 
44
42
  interface ResolvedModelBoundMetadata {
45
43
  id: ProjectionId;
@@ -277,15 +275,17 @@ export class Projections implements IProjections {
277
275
  const properties = TypeIntrospector.getTrackedProperties(type);
278
276
  const prototype = type.prototype;
279
277
 
280
- const fromByEventType = new Map<string, { Key: ContractEventType; Value: { Properties: Record<string, string>; Key: string; ParentKey: string } }>();
278
+ const fromByEventType = new Map<string, FromRecord>();
281
279
  const joinByEventType = new Map<string, { Key: ContractEventType; Value: { On: string; Properties: Record<string, string>; Key: string } }>();
282
280
  const removedWithByEventType = new Map<string, { Key: ContractEventType; Value: { Key: string; ParentKey: string } }>();
283
281
  const removedWithJoinByEventType = new Map<string, { Key: ContractEventType; Value: { Key: string } }>();
282
+ const childrenByProperty: Record<string, ChildrenDefinitionLike> = {};
283
+ const nestedByProperty: Record<string, ChildrenDefinitionLike> = {};
284
284
 
285
285
  const fromEvents = getFromEventMetadata(type);
286
286
  for (const fromEvent of fromEvents) {
287
- const eventType = this.toContractEventType(fromEvent.eventType);
288
- const eventKey = this.getEventTypeMapKey(eventType);
287
+ const eventType = toContractEventType(fromEvent.eventType);
288
+ const eventKey = getEventTypeMapKey(eventType);
289
289
  fromByEventType.set(eventKey, {
290
290
  Key: eventType,
291
291
  Value: {
@@ -296,15 +296,10 @@ export class Projections implements IProjections {
296
296
  });
297
297
  }
298
298
 
299
- const clearWithClass = getClearWithClassMetadata(type);
300
- if (clearWithClass.length > 0) {
301
- throw new Error(`Model-bound projection '${type.name}' uses @clearWith on class level, which is not implemented yet.`);
302
- }
303
-
304
299
  const removedWithClass = getRemovedWithClassMetadata(type);
305
300
  for (const removed of removedWithClass) {
306
- const eventType = this.toContractEventType(removed.eventType);
307
- removedWithByEventType.set(this.getEventTypeMapKey(eventType), {
301
+ const eventType = toContractEventType(removed.eventType);
302
+ removedWithByEventType.set(getEventTypeMapKey(eventType), {
308
303
  Key: eventType,
309
304
  Value: { Key: removed.key ?? '$eventSourceId', ParentKey: removed.parentKey ?? '' }
310
305
  });
@@ -312,30 +307,15 @@ export class Projections implements IProjections {
312
307
 
313
308
  const removedWithJoinClass = getRemovedWithJoinClassMetadata(type);
314
309
  for (const removed of removedWithJoinClass) {
315
- const eventType = this.toContractEventType(removed.eventType);
316
- removedWithJoinByEventType.set(this.getEventTypeMapKey(eventType), {
310
+ const eventType = toContractEventType(removed.eventType);
311
+ removedWithJoinByEventType.set(getEventTypeMapKey(eventType), {
317
312
  Key: eventType,
318
313
  Value: { Key: removed.key ?? '$eventSourceId' }
319
314
  });
320
315
  }
321
316
 
322
317
  for (const property of properties) {
323
- this.throwIfUnsupportedModelBoundDecorators(type.name, prototype, property);
324
-
325
- for (const mapping of getSetFromMetadata(prototype, property)) {
326
- const entry = this.ensureFromEntry(fromByEventType, mapping.eventType);
327
- entry.Value.Properties[property] = mapping.eventPropertyName ?? property;
328
- }
329
-
330
- for (const mapping of getSetFromContextMetadata(prototype, property)) {
331
- const entry = this.ensureFromEntry(fromByEventType, mapping.eventType);
332
- entry.Value.Properties[property] = mapping.contextPropertyName ?? property;
333
- }
334
-
335
- for (const mapping of getSetValueMetadata(prototype, property)) {
336
- const entry = this.ensureFromEntry(fromByEventType, mapping.eventType);
337
- entry.Value.Properties[property] = JSON.stringify(mapping.value);
338
- }
318
+ applyPropertyMappings(prototype, property, fromByEventType);
339
319
 
340
320
  for (const mapping of getJoinMetadata(prototype, property)) {
341
321
  const entry = this.ensureJoinEntry(joinByEventType, mapping.eventType);
@@ -343,26 +323,30 @@ export class Projections implements IProjections {
343
323
  entry.Value.Properties[property] = mapping.eventPropertyName ?? property;
344
324
  }
345
325
 
346
- const fromEvery = getFromEveryMetadata(prototype, property);
347
- if (fromEvery) {
348
- // Applied later to projection.All
349
- }
350
-
351
326
  for (const removed of getRemovedWithPropertyMetadata(prototype, property)) {
352
- const eventType = this.toContractEventType(removed.eventType);
353
- removedWithByEventType.set(this.getEventTypeMapKey(eventType), {
327
+ const eventType = toContractEventType(removed.eventType);
328
+ removedWithByEventType.set(getEventTypeMapKey(eventType), {
354
329
  Key: eventType,
355
330
  Value: { Key: removed.key ?? '$eventSourceId', ParentKey: removed.parentKey ?? '' }
356
331
  });
357
332
  }
358
333
 
359
334
  for (const removed of getRemovedWithJoinPropertyMetadata(prototype, property)) {
360
- const eventType = this.toContractEventType(removed.eventType);
361
- removedWithJoinByEventType.set(this.getEventTypeMapKey(eventType), {
335
+ const eventType = toContractEventType(removed.eventType);
336
+ removedWithJoinByEventType.set(getEventTypeMapKey(eventType), {
362
337
  Key: eventType,
363
338
  Value: { Key: removed.key ?? '$eventSourceId' }
364
339
  });
365
340
  }
341
+
342
+ const childrenFromList = getChildrenFromMetadata(prototype, property);
343
+ if (childrenFromList.length > 0) {
344
+ childrenByProperty[property] = buildChildrenEntry(type, property, childrenFromList);
345
+ }
346
+
347
+ if (isNested(prototype, property)) {
348
+ nestedByProperty[property] = buildNestedEntry(type, property);
349
+ }
366
350
  }
367
351
 
368
352
  const allProperties: Record<string, string> = {};
@@ -384,7 +368,7 @@ export class Projections implements IProjections {
384
368
  InitialModelState: '{}',
385
369
  From: Array.from(fromByEventType.values()),
386
370
  Join: Array.from(joinByEventType.values()),
387
- Children: {},
371
+ Children: childrenByProperty,
388
372
  FromEvery: [],
389
373
  All: {
390
374
  Properties: allProperties,
@@ -396,7 +380,7 @@ export class Projections implements IProjections {
396
380
  LastUpdated: { Value: '' },
397
381
  Tags: [],
398
382
  AutoMap: AutoMap.Enabled,
399
- Nested: {}
383
+ Nested: nestedByProperty
400
384
  };
401
385
  definition.LastUpdated = { Value: this.computeStableLastUpdated(definition) };
402
386
  return definition;
@@ -416,62 +400,12 @@ export class Projections implements IProjections {
416
400
  return undefined;
417
401
  }
418
402
 
419
- private throwIfUnsupportedModelBoundDecorators(typeName: string, prototype: object, property: string): void {
420
- if (getAddFromMetadata(prototype, property).length > 0) {
421
- throw new Error(`Model-bound projection '${typeName}' uses @addFrom on '${property}', which is not implemented yet.`);
422
- }
423
- if (getSubtractFromMetadata(prototype, property).length > 0) {
424
- throw new Error(`Model-bound projection '${typeName}' uses @subtractFrom on '${property}', which is not implemented yet.`);
425
- }
426
- if (getIncrementMetadata(prototype, property).length > 0) {
427
- throw new Error(`Model-bound projection '${typeName}' uses @increment on '${property}', which is not implemented yet.`);
428
- }
429
- if (getDecrementMetadata(prototype, property).length > 0) {
430
- throw new Error(`Model-bound projection '${typeName}' uses @decrement on '${property}', which is not implemented yet.`);
431
- }
432
- if (getCountMetadata(prototype, property).length > 0) {
433
- throw new Error(`Model-bound projection '${typeName}' uses @count on '${property}', which is not implemented yet.`);
434
- }
435
- if (getChildrenFromMetadata(prototype, property).length > 0) {
436
- throw new Error(`Model-bound projection '${typeName}' uses @childrenFrom on '${property}', which is not implemented yet.`);
437
- }
438
- if (isNested(prototype, property)) {
439
- throw new Error(`Model-bound projection '${typeName}' uses @nested on '${property}', which is not implemented yet.`);
440
- }
441
- if (getClearWithPropertyMetadata(prototype, property).length > 0) {
442
- throw new Error(`Model-bound projection '${typeName}' uses @clearWith on '${property}', which is not implemented yet.`);
443
- }
444
- }
445
-
446
- private ensureFromEntry(
447
- fromByEventType: Map<string, { Key: ContractEventType; Value: { Properties: Record<string, string>; Key: string; ParentKey: string } }>,
448
- eventTypeConstructor: Function
449
- ): { Key: ContractEventType; Value: { Properties: Record<string, string>; Key: string; ParentKey: string } } {
450
- const eventType = this.toContractEventType(eventTypeConstructor);
451
- const key = this.getEventTypeMapKey(eventType);
452
- const existing = fromByEventType.get(key);
453
- if (existing) {
454
- return existing;
455
- }
456
-
457
- const created = {
458
- Key: eventType,
459
- Value: {
460
- Properties: {},
461
- Key: '$eventSourceId',
462
- ParentKey: ''
463
- }
464
- };
465
- fromByEventType.set(key, created);
466
- return created;
467
- }
468
-
469
403
  private ensureJoinEntry(
470
404
  joinByEventType: Map<string, { Key: ContractEventType; Value: { On: string; Properties: Record<string, string>; Key: string } }>,
471
405
  eventTypeConstructor: Function
472
406
  ): { Key: ContractEventType; Value: { On: string; Properties: Record<string, string>; Key: string } } {
473
- const eventType = this.toContractEventType(eventTypeConstructor);
474
- const key = this.getEventTypeMapKey(eventType);
407
+ const eventType = toContractEventType(eventTypeConstructor);
408
+ const key = getEventTypeMapKey(eventType);
475
409
  const existing = joinByEventType.get(key);
476
410
  if (existing) {
477
411
  return existing;
@@ -489,22 +423,6 @@ export class Projections implements IProjections {
489
423
  return created;
490
424
  }
491
425
 
492
- private toContractEventType(eventTypeConstructor: Function): ContractEventType {
493
- const eventType = getEventTypeFor(eventTypeConstructor);
494
- if (eventType.id.value === '') {
495
- throw new Error(`Event type '${eventTypeConstructor.name}' is not decorated with @eventType().`);
496
- }
497
- return {
498
- Id: eventType.id.value,
499
- Generation: eventType.generation.value,
500
- Tombstone: false
501
- };
502
- }
503
-
504
- private getEventTypeMapKey(eventType: ContractEventType): string {
505
- return `${eventType.Id}:${eventType.Generation}:${eventType.Tombstone ? '1' : '0'}`;
506
- }
507
-
508
426
  /**
509
427
  * Computes a stable, deterministic ISO timestamp from the projection definition content,
510
428
  * excluding the LastUpdated field itself. This ensures the server does not interpret
@@ -0,0 +1,28 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { PropertyAccessor, PropertyPathResolverProxyHandler } from '@cratis/fundamentals';
5
+ import { IAddBuilder } from './IAddBuilder';
6
+
7
+ /**
8
+ * Concrete implementation of {@link IAddBuilder} that records an add expression
9
+ * into the owning from/join builder.
10
+ * @template TEvent - The event type.
11
+ * @template TParentBuilder - The parent builder type.
12
+ */
13
+ export class AddBuilder<TEvent, TParentBuilder> implements IAddBuilder<TEvent, TParentBuilder> {
14
+ constructor(
15
+ private readonly _readModelProperty: string,
16
+ private readonly _setProperty: (readModelProp: string, expression: string) => void,
17
+ private readonly _parent: TParentBuilder
18
+ ) {}
19
+
20
+ /** @inheritdoc */
21
+ with(eventPropertyAccessor: PropertyAccessor<TEvent>): TParentBuilder {
22
+ const handler = new PropertyPathResolverProxyHandler();
23
+ const proxy = new Proxy({}, handler);
24
+ eventPropertyAccessor(proxy as TEvent);
25
+ this._setProperty(this._readModelProperty, `$add(${handler.property})`);
26
+ return this._parent;
27
+ }
28
+ }
@@ -0,0 +1,97 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { PropertyAccessor, PropertyPathResolverProxyHandler } from '@cratis/fundamentals';
5
+ import { IAddChildBuilder } from './IAddChildBuilder';
6
+
7
+ /**
8
+ * A single `.addChild()` accumulation captured on a from/join builder, later merged into the
9
+ * owning projection builder's `Children` definitions once the enclosing `.from()`/`.join()`
10
+ * call resolves the event type it belongs to.
11
+ */
12
+ export interface ChildAdditionEntry {
13
+ /** The read model property the child is added to. */
14
+ targetProperty: string;
15
+ /** The child model property used to identify instances, when set via the builder callback overload. */
16
+ identifiedBy?: string;
17
+ /** The event property used as the key, when set via the builder callback overload. */
18
+ usingKey?: string;
19
+ /** The event property whose value becomes the child directly, when set via the plain accessor overload. */
20
+ fromEventProperty?: string;
21
+ }
22
+
23
+ /**
24
+ * Implements {@link IAddChildBuilder} and doubles as a {@link ProxyHandler} so a single probe
25
+ * object can resolve `.addChild()`'s two call shapes at runtime: a plain event-property
26
+ * accessor (`e => e.someProperty`), or a builder callback (`b => b.usingKey(...)`). Known
27
+ * builder members are dispatched to the real implementation below; any other property access
28
+ * (the plain-accessor shape) falls through to the same path-capturing proxy used everywhere
29
+ * else in this file for `PropertyAccessor<T>` resolution.
30
+ * @template TChildModel - The child model type.
31
+ * @template TEvent - The event type.
32
+ */
33
+ export class AddChildBuilder<TChildModel, TEvent> implements IAddChildBuilder<TChildModel, TEvent>, ProxyHandler<object> {
34
+ private readonly _pathHandler = new PropertyPathResolverProxyHandler();
35
+ private readonly _pathProxy = new Proxy({}, this._pathHandler);
36
+ private _usedAsBuilder = false;
37
+ private _identifiedByProperty: string | undefined;
38
+ private _usingKeyProperty: string | undefined;
39
+
40
+ /** Whether any builder member (`identifiedBy`/`usingKey`) was invoked on the probe. */
41
+ get usedAsBuilder(): boolean {
42
+ return this._usedAsBuilder;
43
+ }
44
+
45
+ /** The child model property captured via `identifiedBy()`, if any. */
46
+ get identifiedByProperty(): string | undefined {
47
+ return this._identifiedByProperty;
48
+ }
49
+
50
+ /** The event property captured via `usingKey()`, if any. */
51
+ get usingKeyProperty(): string | undefined {
52
+ return this._usingKeyProperty;
53
+ }
54
+
55
+ /** The event property path captured when the probe was used as a plain accessor. */
56
+ get capturedEventProperty(): string {
57
+ return this._pathHandler.property;
58
+ }
59
+
60
+ /** @inheritdoc */
61
+ identifiedBy(childPropertyAccessor: PropertyAccessor<TChildModel>): IAddChildBuilder<TChildModel, TEvent> {
62
+ this._usedAsBuilder = true;
63
+ const handler = new PropertyPathResolverProxyHandler();
64
+ const proxy = new Proxy({}, handler);
65
+ childPropertyAccessor(proxy as TChildModel);
66
+ this._identifiedByProperty = handler.property;
67
+ return this;
68
+ }
69
+
70
+ /** @inheritdoc */
71
+ usingKey(eventPropertyAccessor: PropertyAccessor<TEvent>): IAddChildBuilder<TChildModel, TEvent> {
72
+ this._usedAsBuilder = true;
73
+ const handler = new PropertyPathResolverProxyHandler();
74
+ const proxy = new Proxy({}, handler);
75
+ eventPropertyAccessor(proxy as TEvent);
76
+ this._usingKeyProperty = handler.property;
77
+ return this;
78
+ }
79
+
80
+ /**
81
+ * Proxy `get` trap: known builder members dispatch to the real implementation above;
82
+ * anything else (a plain event property accessed by the accessor overload) falls through
83
+ * to path-capturing so the accessor overload keeps working against the same probe.
84
+ * @param _target - The proxy's (unused) empty target object.
85
+ * @param prop - The accessed property name.
86
+ * @returns The resolved member.
87
+ */
88
+ get(_target: object, prop: string | symbol): unknown {
89
+ if (prop === 'identifiedBy') {
90
+ return this.identifiedBy.bind(this);
91
+ }
92
+ if (prop === 'usingKey') {
93
+ return this.usingKey.bind(this);
94
+ }
95
+ return Reflect.get(this._pathProxy as object, prop);
96
+ }
97
+ }
@@ -0,0 +1,157 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import 'reflect-metadata';
5
+ import { describe, expect, it } from 'vitest';
6
+ import { eventType } from '../../events/eventTypeDecorator';
7
+ import { ProjectionBuilderFor } from './ProjectionBuilderFor';
8
+
9
+ // Decorators are applied as plain function calls (rather than `@decorator` syntax) so these
10
+ // fixtures don't depend on the test runner's decorator-syntax support.
11
+
12
+ class LineAdded {
13
+ productId!: string;
14
+ quantity!: number;
15
+ }
16
+ eventType()(LineAdded);
17
+
18
+ class TagAdded {
19
+ name!: string;
20
+ }
21
+ eventType()(TagAdded);
22
+
23
+ class SummaryUpdated {
24
+ total!: number;
25
+ }
26
+ eventType()(SummaryUpdated);
27
+
28
+ class SummaryCleared {}
29
+ eventType()(SummaryCleared);
30
+
31
+ class WholeLineReceived {
32
+ productId!: string;
33
+ }
34
+ eventType()(WholeLineReceived);
35
+
36
+ class OrderLine {
37
+ productId!: string;
38
+ quantity!: number;
39
+ }
40
+
41
+ class OrderSummary {
42
+ total!: number;
43
+ }
44
+
45
+ class Order {
46
+ id!: string;
47
+ lines!: OrderLine[];
48
+ tags!: string[];
49
+ summary!: OrderSummary | undefined;
50
+ }
51
+
52
+ interface FromRecord {
53
+ Key: { Id: string };
54
+ Value: { Properties: Record<string, string>; Key: string; ParentKey: string };
55
+ }
56
+
57
+ interface ChildrenDefinition {
58
+ IdentifiedBy: string;
59
+ From: FromRecord[];
60
+ RemovedWith: Array<{ Key: { Id: string } }>;
61
+ FromEventProperty?: { Event: { Id: string } | undefined; PropertyExpression: string };
62
+ }
63
+
64
+ interface BuiltDefinition {
65
+ From: FromRecord[];
66
+ Children: Record<string, ChildrenDefinition>;
67
+ Nested: Record<string, ChildrenDefinition>;
68
+ }
69
+
70
+ describe('ChildrenBuilder and NestedBuilder', () => {
71
+ describe('when using children on the top-level builder', () => {
72
+ it('should produce a children definition keyed by the target property', () => {
73
+ const builder = new ProjectionBuilderFor<Order>();
74
+ builder.children<OrderLine>(order => order.lines, children => children
75
+ .identifiedBy(line => line.productId)
76
+ .from(LineAdded, from => from.set(line => line.quantity).to(event => event.quantity)));
77
+
78
+ const definition = builder.build('order', 'Order') as unknown as BuiltDefinition;
79
+ const linesDefinition = definition.Children.lines;
80
+
81
+ expect(linesDefinition.IdentifiedBy).toBe('productId');
82
+ const fromEntry = linesDefinition.From.find(candidate => candidate.Key.Id === 'LineAdded')!;
83
+ expect(fromEntry.Value.Properties.quantity).toBe('quantity');
84
+ });
85
+ });
86
+
87
+ describe('when using nested with clearWith on the top-level builder', () => {
88
+ it('should produce a nested definition with a RemovedWith entry', () => {
89
+ const builder = new ProjectionBuilderFor<Order>();
90
+ builder.nested<OrderSummary>(order => order.summary, nested => nested
91
+ .from(SummaryUpdated, from => from.set(summary => summary.total).to(event => event.total))
92
+ .clearWith(SummaryCleared));
93
+
94
+ const definition = builder.build('order', 'Order') as unknown as BuiltDefinition;
95
+ const summaryDefinition = definition.Nested.summary;
96
+
97
+ const fromEntry = summaryDefinition.From.find(candidate => candidate.Key.Id === 'SummaryUpdated')!;
98
+ expect(fromEntry.Value.Properties.total).toBe('total');
99
+ expect(summaryDefinition.RemovedWith[0].Key.Id).toBe('SummaryCleared');
100
+ });
101
+ });
102
+
103
+ describe('when using addChild with a builder callback', () => {
104
+ it('should produce a children definition keyed by identifiedBy and usingKey', () => {
105
+ const builder = new ProjectionBuilderFor<Order>();
106
+ builder.from(LineAdded, from => from.addChild<OrderLine>(order => order.lines, child => child
107
+ .identifiedBy(line => line.productId)
108
+ .usingKey(event => event.productId)));
109
+
110
+ const definition = builder.build('order', 'Order') as unknown as BuiltDefinition;
111
+ const linesDefinition = definition.Children.lines;
112
+
113
+ expect(linesDefinition.IdentifiedBy).toBe('productId');
114
+ const fromEntry = linesDefinition.From.find(candidate => candidate.Key.Id === 'LineAdded')!;
115
+ expect(fromEntry.Value.Key).toBe('productId');
116
+ });
117
+ });
118
+
119
+ describe('when using addChild with a plain event property accessor', () => {
120
+ it('should produce a children definition with a FromEventProperty expression', () => {
121
+ const builder = new ProjectionBuilderFor<Order>();
122
+ builder.from(TagAdded, from => from.addChild<string>(order => order.tags, event => event.name));
123
+
124
+ const definition = builder.build('order', 'Order') as unknown as BuiltDefinition;
125
+ const tagsDefinition = definition.Children.tags;
126
+
127
+ expect(tagsDefinition.FromEventProperty?.PropertyExpression).toBe('name');
128
+ expect(tagsDefinition.FromEventProperty?.Event?.Id).toBe('TagAdded');
129
+ });
130
+ });
131
+
132
+ describe('when using addChild on a join builder', () => {
133
+ it('should produce a children definition from the join clause', () => {
134
+ const builder = new ProjectionBuilderFor<Order>();
135
+ builder.join(WholeLineReceived, join => join.addChild<OrderLine>(order => order.lines, child => child
136
+ .identifiedBy(line => line.productId)));
137
+
138
+ const definition = builder.build('order', 'Order') as unknown as BuiltDefinition;
139
+ const linesDefinition = definition.Children.lines;
140
+
141
+ expect(linesDefinition.IdentifiedBy).toBe('productId');
142
+ expect(linesDefinition.From.some(candidate => candidate.Key.Id === 'WholeLineReceived')).toBe(true);
143
+ });
144
+ });
145
+
146
+ describe('when using setThisValue on a from builder', () => {
147
+ it('should map the whole event property onto the $this well-known expression', () => {
148
+ const builder = new ProjectionBuilderFor<Order>();
149
+ builder.from(TagAdded, from => from.setThisValue().to(event => event.name));
150
+
151
+ const definition = builder.build('order', 'Order') as unknown as BuiltDefinition;
152
+ const fromEntry = definition.From.find(candidate => candidate.Key.Id === 'TagAdded')!;
153
+
154
+ expect(fromEntry.Value.Properties.$this).toBe('name');
155
+ });
156
+ });
157
+ });
@@ -0,0 +1,90 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { PropertyAccessor, PropertyPathResolverProxyHandler } from '@cratis/fundamentals';
5
+ import { ChildrenDefinitionLike, ProjectionBuilderCore } from './ProjectionBuilderCore';
6
+ import { IChildrenBuilder } from './IChildrenBuilder';
7
+ import { INestedBuilder } from './INestedBuilder';
8
+ import { NestedBuilder } from './NestedBuilder';
9
+
10
+ /**
11
+ * Concrete implementation of {@link IChildrenBuilder}, building the `ChildrenDefinition` for a
12
+ * children collection sub-projection reached through `.children()`.
13
+ * @template TParentReadModel - The parent read model type.
14
+ * @template TChildReadModel - The child read model type.
15
+ */
16
+ export class ChildrenBuilder<TParentReadModel, TChildReadModel>
17
+ extends ProjectionBuilderCore<TChildReadModel, IChildrenBuilder<TParentReadModel, TChildReadModel>>
18
+ implements IChildrenBuilder<TParentReadModel, TChildReadModel> {
19
+ private _identifiedBy: string | undefined;
20
+ private _fromEventPropertyPath: string | undefined;
21
+
22
+ /** @inheritdoc */
23
+ identifiedBy(propertyAccessor: PropertyAccessor<TChildReadModel>): IChildrenBuilder<TParentReadModel, TChildReadModel> {
24
+ const handler = new PropertyPathResolverProxyHandler();
25
+ const proxy = new Proxy({}, handler);
26
+ propertyAccessor(proxy as TChildReadModel);
27
+ this._identifiedBy = handler.property;
28
+ return this;
29
+ }
30
+
31
+ /** @inheritdoc */
32
+ fromEventProperty<TEvent>(propertyAccessor: PropertyAccessor<TEvent>): IChildrenBuilder<TParentReadModel, TChildReadModel> {
33
+ const handler = new PropertyPathResolverProxyHandler();
34
+ const proxy = new Proxy({}, handler);
35
+ propertyAccessor(proxy as TEvent);
36
+ this._fromEventPropertyPath = handler.property;
37
+ return this;
38
+ }
39
+
40
+ /** @inheritdoc */
41
+ children<TGrandchildModel>(
42
+ targetPropertyAccessor: PropertyAccessor<TChildReadModel>,
43
+ builderCallback: (builder: IChildrenBuilder<TChildReadModel, TGrandchildModel>) => void
44
+ ): IChildrenBuilder<TParentReadModel, TChildReadModel> {
45
+ const handler = new PropertyPathResolverProxyHandler();
46
+ const proxy = new Proxy({}, handler);
47
+ targetPropertyAccessor(proxy as TChildReadModel);
48
+
49
+ const builder = new ChildrenBuilder<TChildReadModel, TGrandchildModel>();
50
+ builderCallback(builder);
51
+ this._children[handler.property] = builder.buildDefinition();
52
+ return this;
53
+ }
54
+
55
+ /** @inheritdoc */
56
+ nested<TNestedModel>(
57
+ targetPropertyAccessor: PropertyAccessor<TChildReadModel>,
58
+ builderCallback: (builder: INestedBuilder<TChildReadModel, TNestedModel>) => void
59
+ ): IChildrenBuilder<TParentReadModel, TChildReadModel> {
60
+ const handler = new PropertyPathResolverProxyHandler();
61
+ const proxy = new Proxy({}, handler);
62
+ targetPropertyAccessor(proxy as TChildReadModel);
63
+
64
+ const builder = new NestedBuilder<TChildReadModel, TNestedModel>();
65
+ builderCallback(builder);
66
+ this._nested[handler.property] = builder.buildDefinition();
67
+ return this;
68
+ }
69
+
70
+ /**
71
+ * Builds the {@link ChildrenDefinitionLike} representing this children collection.
72
+ * @returns The accumulated children definition.
73
+ */
74
+ buildDefinition(): ChildrenDefinitionLike {
75
+ return {
76
+ IdentifiedBy: this._identifiedBy ?? '$eventSourceId',
77
+ From: this._from,
78
+ Join: this._join,
79
+ Children: this._children,
80
+ All: this._all,
81
+ FromEventProperty: this._fromEventPropertyPath === undefined
82
+ ? undefined
83
+ : { Event: undefined, PropertyExpression: this._fromEventPropertyPath },
84
+ RemovedWith: this._removedWith,
85
+ RemovedWithJoin: this._removedWithJoin,
86
+ AutoMap: this._autoMap,
87
+ Nested: this._nested
88
+ };
89
+ }
90
+ }