@cratis/chronicle 2.1.1 → 3.1.0

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 (320) hide show
  1. package/EventStore.ts +17 -1
  2. package/IEventStore.ts +16 -0
  3. package/auditing/CausationType.ts +10 -0
  4. package/compliance/ComplianceContracts.ts +84 -0
  5. package/compliance/IPIIManager.ts +17 -0
  6. package/compliance/PIIManager.ts +31 -0
  7. package/compliance/index.ts +2 -0
  8. package/connection/ChronicleConnection.ts +7 -1
  9. package/connection/ChronicleServices.ts +2 -0
  10. package/connection/Guid.ts +18 -0
  11. package/dist/EventStore.d.ts +8 -0
  12. package/dist/EventStore.d.ts.map +1 -1
  13. package/dist/EventStore.js +13 -1
  14. package/dist/EventStore.js.map +1 -1
  15. package/dist/IEventStore.d.ts +12 -0
  16. package/dist/IEventStore.d.ts.map +1 -1
  17. package/dist/auditing/CausationType.d.ts +8 -0
  18. package/dist/auditing/CausationType.d.ts.map +1 -1
  19. package/dist/auditing/CausationType.js +8 -0
  20. package/dist/auditing/CausationType.js.map +1 -1
  21. package/dist/compliance/ComplianceContracts.d.ts +40 -0
  22. package/dist/compliance/ComplianceContracts.d.ts.map +1 -1
  23. package/dist/compliance/ComplianceContracts.js +71 -0
  24. package/dist/compliance/ComplianceContracts.js.map +1 -1
  25. package/dist/compliance/IPIIManager.d.ts +15 -0
  26. package/dist/compliance/IPIIManager.d.ts.map +1 -0
  27. package/dist/compliance/IPIIManager.js +4 -0
  28. package/dist/compliance/IPIIManager.js.map +1 -0
  29. package/dist/compliance/PIIManager.d.ts +20 -0
  30. package/dist/compliance/PIIManager.d.ts.map +1 -0
  31. package/dist/compliance/PIIManager.js +30 -0
  32. package/dist/compliance/PIIManager.js.map +1 -0
  33. package/dist/compliance/index.d.ts +2 -0
  34. package/dist/compliance/index.d.ts.map +1 -1
  35. package/dist/compliance/index.js +1 -0
  36. package/dist/compliance/index.js.map +1 -1
  37. package/dist/connection/ChronicleConnection.d.ts +1 -0
  38. package/dist/connection/ChronicleConnection.d.ts.map +1 -1
  39. package/dist/connection/ChronicleConnection.js +6 -1
  40. package/dist/connection/ChronicleConnection.js.map +1 -1
  41. package/dist/connection/ChronicleServices.d.ts +2 -0
  42. package/dist/connection/ChronicleServices.d.ts.map +1 -1
  43. package/dist/connection/Guid.d.ts +5 -0
  44. package/dist/connection/Guid.d.ts.map +1 -1
  45. package/dist/connection/Guid.js +15 -0
  46. package/dist/connection/Guid.js.map +1 -1
  47. package/dist/eventSequences/AppendOperationsBroadcaster.d.ts +16 -0
  48. package/dist/eventSequences/AppendOperationsBroadcaster.d.ts.map +1 -0
  49. package/dist/eventSequences/AppendOperationsBroadcaster.js +77 -0
  50. package/dist/eventSequences/AppendOperationsBroadcaster.js.map +1 -0
  51. package/dist/eventSequences/AppendOptions.d.ts +7 -0
  52. package/dist/eventSequences/AppendOptions.d.ts.map +1 -1
  53. package/dist/eventSequences/AppendResult.d.ts +14 -0
  54. package/dist/eventSequences/AppendResult.d.ts.map +1 -1
  55. package/dist/eventSequences/AppendedEventWithResult.d.ts +13 -0
  56. package/dist/eventSequences/AppendedEventWithResult.d.ts.map +1 -0
  57. package/dist/eventSequences/AppendedEventWithResult.js +4 -0
  58. package/dist/eventSequences/AppendedEventWithResult.js.map +1 -0
  59. package/dist/eventSequences/CompleteStreamError.d.ts +17 -0
  60. package/dist/eventSequences/CompleteStreamError.d.ts.map +1 -0
  61. package/dist/eventSequences/CompleteStreamError.js +20 -0
  62. package/dist/eventSequences/CompleteStreamError.js.map +1 -0
  63. package/dist/eventSequences/CompleteStreamResult.d.ts +14 -0
  64. package/dist/eventSequences/CompleteStreamResult.d.ts.map +1 -0
  65. package/dist/eventSequences/CompleteStreamResult.js +4 -0
  66. package/dist/eventSequences/CompleteStreamResult.js.map +1 -0
  67. package/dist/eventSequences/ConcurrencyViolation.d.ts +13 -0
  68. package/dist/eventSequences/ConcurrencyViolation.d.ts.map +1 -0
  69. package/dist/eventSequences/ConcurrencyViolation.js +4 -0
  70. package/dist/eventSequences/ConcurrencyViolation.js.map +1 -0
  71. package/dist/eventSequences/EventForEventSourceId.d.ts +8 -0
  72. package/dist/eventSequences/EventForEventSourceId.d.ts.map +1 -1
  73. package/dist/eventSequences/EventSequence.d.ts +35 -1
  74. package/dist/eventSequences/EventSequence.d.ts.map +1 -1
  75. package/dist/eventSequences/EventSequence.js +350 -19
  76. package/dist/eventSequences/EventSequence.js.map +1 -1
  77. package/dist/eventSequences/IEventSequence.d.ts +85 -1
  78. package/dist/eventSequences/IEventSequence.d.ts.map +1 -1
  79. package/dist/eventSequences/WaitForCompletionResult.d.ts +11 -0
  80. package/dist/eventSequences/WaitForCompletionResult.d.ts.map +1 -0
  81. package/dist/eventSequences/WaitForCompletionResult.js +4 -0
  82. package/dist/eventSequences/WaitForCompletionResult.js.map +1 -0
  83. package/dist/eventSequences/index.d.ts +5 -0
  84. package/dist/eventSequences/index.d.ts.map +1 -1
  85. package/dist/eventSequences/index.js +1 -0
  86. package/dist/eventSequences/index.js.map +1 -1
  87. package/dist/eventStoreSubscriptions/EventStoreSubscriptions.d.ts +3 -0
  88. package/dist/eventStoreSubscriptions/EventStoreSubscriptions.d.ts.map +1 -1
  89. package/dist/eventStoreSubscriptions/EventStoreSubscriptions.js +9 -0
  90. package/dist/eventStoreSubscriptions/EventStoreSubscriptions.js.map +1 -1
  91. package/dist/eventStoreSubscriptions/IEventStoreSubscriptions.d.ts +6 -0
  92. package/dist/eventStoreSubscriptions/IEventStoreSubscriptions.d.ts.map +1 -1
  93. package/dist/eventStoreSubscriptions/contracts.d.ts +18 -0
  94. package/dist/eventStoreSubscriptions/contracts.d.ts.map +1 -1
  95. package/dist/eventStoreSubscriptions/contracts.js +106 -0
  96. package/dist/eventStoreSubscriptions/contracts.js.map +1 -1
  97. package/dist/events/constraints/Constraints.js +1 -1
  98. package/dist/events/constraints/Constraints.js.map +1 -1
  99. package/dist/externalServices/ExternalServiceBuilder.d.ts +42 -0
  100. package/dist/externalServices/ExternalServiceBuilder.d.ts.map +1 -0
  101. package/dist/externalServices/ExternalServiceBuilder.js +110 -0
  102. package/dist/externalServices/ExternalServiceBuilder.js.map +1 -0
  103. package/dist/externalServices/ExternalServices.d.ts +19 -0
  104. package/dist/externalServices/ExternalServices.d.ts.map +1 -0
  105. package/dist/externalServices/ExternalServices.js +30 -0
  106. package/dist/externalServices/ExternalServices.js.map +1 -0
  107. package/dist/externalServices/ExternalServicesContracts.d.ts +137 -0
  108. package/dist/externalServices/ExternalServicesContracts.d.ts.map +1 -0
  109. package/dist/externalServices/ExternalServicesContracts.js +654 -0
  110. package/dist/externalServices/ExternalServicesContracts.js.map +1 -0
  111. package/dist/externalServices/IExternalServiceBuilder.d.ts +67 -0
  112. package/dist/externalServices/IExternalServiceBuilder.d.ts.map +1 -0
  113. package/dist/externalServices/IExternalServiceBuilder.js +4 -0
  114. package/dist/externalServices/IExternalServiceBuilder.js.map +1 -0
  115. package/dist/externalServices/IExternalServices.d.ts +13 -0
  116. package/dist/externalServices/IExternalServices.d.ts.map +1 -0
  117. package/dist/externalServices/IExternalServices.js +4 -0
  118. package/dist/externalServices/IExternalServices.js.map +1 -0
  119. package/dist/externalServices/index.d.ts +5 -0
  120. package/dist/externalServices/index.d.ts.map +1 -0
  121. package/dist/externalServices/index.js +5 -0
  122. package/dist/externalServices/index.js.map +1 -0
  123. package/dist/identities/IIdentityManager.d.ts +17 -0
  124. package/dist/identities/IIdentityManager.d.ts.map +1 -0
  125. package/dist/identities/IIdentityManager.js +4 -0
  126. package/dist/identities/IIdentityManager.js.map +1 -0
  127. package/dist/identities/IdentityManager.d.ts +20 -0
  128. package/dist/identities/IdentityManager.d.ts.map +1 -0
  129. package/dist/identities/IdentityManager.js +31 -0
  130. package/dist/identities/IdentityManager.js.map +1 -0
  131. package/dist/identities/index.d.ts +3 -0
  132. package/dist/identities/index.d.ts.map +1 -0
  133. package/dist/identities/index.js +4 -0
  134. package/dist/identities/index.js.map +1 -0
  135. package/dist/index.d.ts +5 -1
  136. package/dist/index.d.ts.map +1 -1
  137. package/dist/index.js +5 -1
  138. package/dist/index.js.map +1 -1
  139. package/dist/observation/FailedPartition.d.ts +15 -0
  140. package/dist/observation/FailedPartition.d.ts.map +1 -0
  141. package/dist/observation/FailedPartition.js +4 -0
  142. package/dist/observation/FailedPartition.js.map +1 -0
  143. package/dist/observation/FailedPartitionAttempt.d.ts +15 -0
  144. package/dist/observation/FailedPartitionAttempt.d.ts.map +1 -0
  145. package/dist/observation/FailedPartitionAttempt.js +4 -0
  146. package/dist/observation/FailedPartitionAttempt.js.map +1 -0
  147. package/dist/observation/FailedPartitions.d.ts +24 -0
  148. package/dist/observation/FailedPartitions.d.ts.map +1 -0
  149. package/dist/observation/FailedPartitions.js +39 -0
  150. package/dist/observation/FailedPartitions.js.map +1 -0
  151. package/dist/observation/ICanBeNotifiedWhenPartitionReplayed.d.ts +19 -0
  152. package/dist/observation/ICanBeNotifiedWhenPartitionReplayed.d.ts.map +1 -0
  153. package/dist/observation/ICanBeNotifiedWhenPartitionReplayed.js +4 -0
  154. package/dist/observation/ICanBeNotifiedWhenPartitionReplayed.js.map +1 -0
  155. package/dist/observation/ICanBeNotifiedWhenReplay.d.ts +17 -0
  156. package/dist/observation/ICanBeNotifiedWhenReplay.d.ts.map +1 -0
  157. package/dist/observation/ICanBeNotifiedWhenReplay.js +4 -0
  158. package/dist/observation/ICanBeNotifiedWhenReplay.js.map +1 -0
  159. package/dist/observation/IFailedPartitions.d.ts +18 -0
  160. package/dist/observation/IFailedPartitions.d.ts.map +1 -0
  161. package/dist/observation/IFailedPartitions.js +4 -0
  162. package/dist/observation/IFailedPartitions.js.map +1 -0
  163. package/dist/observation/index.d.ts +6 -0
  164. package/dist/observation/index.d.ts.map +1 -1
  165. package/dist/observation/index.js +1 -0
  166. package/dist/observation/index.js.map +1 -1
  167. package/dist/observation/notifyReplayLifecycle.d.ts +13 -0
  168. package/dist/observation/notifyReplayLifecycle.d.ts.map +1 -0
  169. package/dist/observation/notifyReplayLifecycle.js +54 -0
  170. package/dist/observation/notifyReplayLifecycle.js.map +1 -0
  171. package/dist/observation/toClientFailedPartition.d.ts +9 -0
  172. package/dist/observation/toClientFailedPartition.d.ts.map +1 -0
  173. package/dist/observation/toClientFailedPartition.js +23 -0
  174. package/dist/observation/toClientFailedPartition.js.map +1 -0
  175. package/dist/projections/Projections.d.ts +0 -4
  176. package/dist/projections/Projections.d.ts.map +1 -1
  177. package/dist/projections/Projections.js +25 -103
  178. package/dist/projections/Projections.js.map +1 -1
  179. package/dist/projections/declarative/AddBuilder.d.ts +17 -0
  180. package/dist/projections/declarative/AddBuilder.d.ts.map +1 -0
  181. package/dist/projections/declarative/AddBuilder.js +28 -0
  182. package/dist/projections/declarative/AddBuilder.js.map +1 -0
  183. package/dist/projections/declarative/AddChildBuilder.d.ts +56 -0
  184. package/dist/projections/declarative/AddChildBuilder.d.ts.map +1 -0
  185. package/dist/projections/declarative/AddChildBuilder.js +72 -0
  186. package/dist/projections/declarative/AddChildBuilder.js.map +1 -0
  187. package/dist/projections/declarative/ChildrenBuilder.d.ts +28 -0
  188. package/dist/projections/declarative/ChildrenBuilder.d.ts.map +1 -0
  189. package/dist/projections/declarative/ChildrenBuilder.js +72 -0
  190. package/dist/projections/declarative/ChildrenBuilder.js.map +1 -0
  191. package/dist/projections/declarative/CompositeKeyBuilder.d.ts +16 -0
  192. package/dist/projections/declarative/CompositeKeyBuilder.d.ts.map +1 -0
  193. package/dist/projections/declarative/CompositeKeyBuilder.js +29 -0
  194. package/dist/projections/declarative/CompositeKeyBuilder.js.map +1 -0
  195. package/dist/projections/declarative/FromBuilder.d.ts +8 -6
  196. package/dist/projections/declarative/FromBuilder.d.ts.map +1 -1
  197. package/dist/projections/declarative/FromBuilder.js +44 -14
  198. package/dist/projections/declarative/FromBuilder.js.map +1 -1
  199. package/dist/projections/declarative/ICompositeKeyBuilder.d.ts +5 -0
  200. package/dist/projections/declarative/ICompositeKeyBuilder.d.ts.map +1 -1
  201. package/dist/projections/declarative/JoinBuilder.d.ts +7 -5
  202. package/dist/projections/declarative/JoinBuilder.d.ts.map +1 -1
  203. package/dist/projections/declarative/JoinBuilder.js +50 -16
  204. package/dist/projections/declarative/JoinBuilder.js.map +1 -1
  205. package/dist/projections/declarative/NestedBuilder.d.ts +24 -0
  206. package/dist/projections/declarative/NestedBuilder.d.ts.map +1 -0
  207. package/dist/projections/declarative/NestedBuilder.js +60 -0
  208. package/dist/projections/declarative/NestedBuilder.js.map +1 -0
  209. package/dist/projections/declarative/ProjectionBuilderCore.d.ts +127 -0
  210. package/dist/projections/declarative/ProjectionBuilderCore.d.ts.map +1 -0
  211. package/dist/projections/declarative/ProjectionBuilderCore.js +180 -0
  212. package/dist/projections/declarative/ProjectionBuilderCore.js.map +1 -0
  213. package/dist/projections/declarative/ProjectionBuilderFor.d.ts +6 -32
  214. package/dist/projections/declarative/ProjectionBuilderFor.d.ts.map +1 -1
  215. package/dist/projections/declarative/ProjectionBuilderFor.js +24 -118
  216. package/dist/projections/declarative/ProjectionBuilderFor.js.map +1 -1
  217. package/dist/projections/declarative/SubtractBuilder.d.ts +17 -0
  218. package/dist/projections/declarative/SubtractBuilder.d.ts.map +1 -0
  219. package/dist/projections/declarative/SubtractBuilder.js +28 -0
  220. package/dist/projections/declarative/SubtractBuilder.js.map +1 -0
  221. package/dist/projections/declarative/index.d.ts +9 -0
  222. package/dist/projections/declarative/index.d.ts.map +1 -1
  223. package/dist/projections/declarative/index.js +8 -0
  224. package/dist/projections/declarative/index.js.map +1 -1
  225. package/dist/projections/modelBound/childrenAndNestedBuilder.d.ts +90 -0
  226. package/dist/projections/modelBound/childrenAndNestedBuilder.d.ts.map +1 -0
  227. package/dist/projections/modelBound/childrenAndNestedBuilder.js +290 -0
  228. package/dist/projections/modelBound/childrenAndNestedBuilder.js.map +1 -0
  229. package/dist/reactors/ReactorSideEffects.d.ts +29 -0
  230. package/dist/reactors/ReactorSideEffects.d.ts.map +1 -0
  231. package/dist/reactors/ReactorSideEffects.js +70 -0
  232. package/dist/reactors/ReactorSideEffects.js.map +1 -0
  233. package/dist/reactors/Reactors.d.ts +4 -1
  234. package/dist/reactors/Reactors.d.ts.map +1 -1
  235. package/dist/reactors/Reactors.js +23 -4
  236. package/dist/reactors/Reactors.js.map +1 -1
  237. package/dist/reducers/Reducers.d.ts.map +1 -1
  238. package/dist/reducers/Reducers.js +15 -3
  239. package/dist/reducers/Reducers.js.map +1 -1
  240. package/dist/reducers/reducer.d.ts +9 -1
  241. package/dist/reducers/reducer.d.ts.map +1 -1
  242. package/dist/reducers/reducer.js +4 -2
  243. package/dist/reducers/reducer.js.map +1 -1
  244. package/dist/transactions/IUnitOfWork.d.ts +18 -0
  245. package/dist/transactions/IUnitOfWork.d.ts.map +1 -1
  246. package/dist/transactions/UnitOfWork.d.ts +9 -0
  247. package/dist/transactions/UnitOfWork.d.ts.map +1 -1
  248. package/dist/transactions/UnitOfWork.js +14 -0
  249. package/dist/transactions/UnitOfWork.js.map +1 -1
  250. package/dist/tsconfig.tsbuildinfo +1 -1
  251. package/eventSequences/AppendOperationsBroadcaster.ts +83 -0
  252. package/eventSequences/AppendOptions.ts +8 -0
  253. package/eventSequences/AppendResult.ts +16 -0
  254. package/eventSequences/AppendedEventWithResult.ts +17 -0
  255. package/eventSequences/CompleteStreamError.ts +20 -0
  256. package/eventSequences/CompleteStreamResult.ts +13 -0
  257. package/eventSequences/ConcurrencyViolation.ts +18 -0
  258. package/eventSequences/EventForEventSourceId.ts +12 -0
  259. package/eventSequences/EventSequence.spec.ts +588 -0
  260. package/eventSequences/EventSequence.ts +406 -20
  261. package/eventSequences/IEventSequence.ts +109 -1
  262. package/eventSequences/WaitForCompletionResult.ts +15 -0
  263. package/eventSequences/index.ts +5 -0
  264. package/eventStoreSubscriptions/EventStoreSubscriptions.spec.ts +52 -0
  265. package/eventStoreSubscriptions/EventStoreSubscriptions.ts +19 -1
  266. package/eventStoreSubscriptions/IEventStoreSubscriptions.ts +7 -0
  267. package/eventStoreSubscriptions/contracts.ts +126 -0
  268. package/events/constraints/Constraints.ts +1 -1
  269. package/externalServices/ExternalServiceBuilder.ts +128 -0
  270. package/externalServices/ExternalServices.ts +34 -0
  271. package/externalServices/ExternalServicesContracts.ts +709 -0
  272. package/externalServices/IExternalServiceBuilder.ts +76 -0
  273. package/externalServices/IExternalServices.ts +16 -0
  274. package/externalServices/index.ts +7 -0
  275. package/fundamentalsPeerRange.spec.ts +31 -0
  276. package/identities/IIdentityManager.ts +19 -0
  277. package/identities/IdentityManager.ts +32 -0
  278. package/identities/index.ts +5 -0
  279. package/index.ts +6 -0
  280. package/observation/FailedPartition.ts +21 -0
  281. package/observation/FailedPartitionAttempt.ts +21 -0
  282. package/observation/FailedPartitions.spec.ts +75 -0
  283. package/observation/FailedPartitions.ts +44 -0
  284. package/observation/ICanBeNotifiedWhenPartitionReplayed.ts +22 -0
  285. package/observation/ICanBeNotifiedWhenReplay.ts +20 -0
  286. package/observation/IFailedPartitions.ts +22 -0
  287. package/observation/index.ts +6 -0
  288. package/observation/notifyReplayLifecycle.spec.ts +98 -0
  289. package/observation/notifyReplayLifecycle.ts +61 -0
  290. package/observation/toClientFailedPartition.ts +26 -0
  291. package/package.json +16 -3
  292. package/projections/Projections.childrenAndNested.spec.ts +188 -0
  293. package/projections/Projections.spec.ts +131 -0
  294. package/projections/Projections.ts +37 -119
  295. package/projections/declarative/AddBuilder.ts +28 -0
  296. package/projections/declarative/AddChildBuilder.ts +97 -0
  297. package/projections/declarative/ChildrenBuilder.spec.ts +157 -0
  298. package/projections/declarative/ChildrenBuilder.ts +90 -0
  299. package/projections/declarative/CompositeKeyBuilder.spec.ts +117 -0
  300. package/projections/declarative/CompositeKeyBuilder.ts +38 -0
  301. package/projections/declarative/FromBuilder.ts +60 -15
  302. package/projections/declarative/ICompositeKeyBuilder.ts +6 -0
  303. package/projections/declarative/JoinBuilder.ts +66 -17
  304. package/projections/declarative/NestedBuilder.ts +76 -0
  305. package/projections/declarative/ProjectionBuilderCore.ts +257 -0
  306. package/projections/declarative/ProjectionBuilderFor.spec.ts +115 -0
  307. package/projections/declarative/ProjectionBuilderFor.ts +29 -168
  308. package/projections/declarative/SubtractBuilder.ts +28 -0
  309. package/projections/declarative/index.ts +9 -0
  310. package/projections/modelBound/childrenAndNestedBuilder.ts +354 -0
  311. package/reactors/ReactorSideEffects.spec.ts +182 -0
  312. package/reactors/ReactorSideEffects.ts +97 -0
  313. package/reactors/Reactors.ts +29 -4
  314. package/reducers/Reducers.spec.ts +87 -0
  315. package/reducers/Reducers.ts +15 -3
  316. package/reducers/reducer.spec.ts +40 -0
  317. package/reducers/reducer.ts +11 -2
  318. package/transactions/IUnitOfWork.ts +21 -0
  319. package/transactions/UnitOfWork.spec.ts +82 -0
  320. package/transactions/UnitOfWork.ts +20 -0
@@ -2,6 +2,9 @@
2
2
  // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
3
 
4
4
  import { PropertyAccessor, PropertyPathResolverProxyHandler } from '@cratis/fundamentals';
5
+ import { AddBuilder } from './AddBuilder';
6
+ import { AddChildBuilder, ChildAdditionEntry } from './AddChildBuilder';
7
+ import { CompositeKeyBuilder } from './CompositeKeyBuilder';
5
8
  import { IAddBuilder } from './IAddBuilder';
6
9
  import { IAddChildBuilder } from './IAddChildBuilder';
7
10
  import { ICompositeKeyBuilder } from './ICompositeKeyBuilder';
@@ -9,6 +12,7 @@ import { IJoinBuilder } from './IJoinBuilder';
9
12
  import { ISetBuilder } from './ISetBuilder';
10
13
  import { ISubtractBuilder } from './ISubtractBuilder';
11
14
  import { SetBuilder } from './SetBuilder';
15
+ import { SubtractBuilder } from './SubtractBuilder';
12
16
 
13
17
  /**
14
18
  * Accumulated property mapping for a join clause.
@@ -17,6 +21,7 @@ export interface JoinEntry {
17
21
  on: string;
18
22
  properties: Record<string, string>;
19
23
  key: string;
24
+ children: ChildAdditionEntry[];
20
25
  }
21
26
 
22
27
  /**
@@ -28,7 +33,8 @@ export class JoinBuilder<TReadModel, TEvent> implements IJoinBuilder<TReadModel,
28
33
  readonly entry: JoinEntry = {
29
34
  on: '',
30
35
  properties: {},
31
- key: '$eventSourceId'
36
+ key: '$eventSourceId',
37
+ children: []
32
38
  };
33
39
 
34
40
  /** @inheritdoc */
@@ -71,22 +77,31 @@ export class JoinBuilder<TReadModel, TEvent> implements IJoinBuilder<TReadModel,
71
77
 
72
78
  /** @inheritdoc */
73
79
  usingParentKey(_keyAccessor: PropertyAccessor<TEvent>): this {
74
- throw new Error('usingParentKey is not supported for join mappings.');
80
+ // A join's wire definition (JoinDefinition) has no ParentKey slot - the C# client
81
+ // accepts this call too (it is inherited from the shared key-builder base) but its
82
+ // JoinBuilder.Build() only ever reads On/Properties/Key, so the value is silently
83
+ // unused there as well. Matching that: accept the call, but it has no effect.
84
+ return this;
75
85
  }
76
86
 
77
87
  /** @inheritdoc */
78
88
  usingParentKeyFromContext(_contextPropertyName: string): this {
79
- throw new Error('usingParentKeyFromContext is not supported for join mappings.');
89
+ // See usingParentKey - accepted for interface parity with From, no wire effect for Join.
90
+ return this;
80
91
  }
81
92
 
82
93
  /** @inheritdoc */
83
- usingCompositeKey<TKeyType>(_builderCallback: (builder: ICompositeKeyBuilder<TKeyType, TEvent>) => void): this {
84
- throw new Error('usingCompositeKey is not implemented yet.');
94
+ usingCompositeKey<TKeyType>(builderCallback: (builder: ICompositeKeyBuilder<TKeyType, TEvent>) => void): this {
95
+ const compositeKeyBuilder = new CompositeKeyBuilder<TKeyType, TEvent>();
96
+ builderCallback(compositeKeyBuilder);
97
+ this.entry.key = compositeKeyBuilder.build();
98
+ return this;
85
99
  }
86
100
 
87
101
  /** @inheritdoc */
88
102
  usingParentCompositeKey<TKeyType>(_builderCallback: (builder: ICompositeKeyBuilder<TKeyType, TEvent>) => void): this {
89
- throw new Error('usingParentCompositeKey is not supported for join mappings.');
103
+ // See usingParentKey - accepted for interface parity with From, no wire effect for Join.
104
+ return this;
90
105
  }
91
106
 
92
107
  /** @inheritdoc */
@@ -97,7 +112,8 @@ export class JoinBuilder<TReadModel, TEvent> implements IJoinBuilder<TReadModel,
97
112
 
98
113
  /** @inheritdoc */
99
114
  usingConstantParentKey(_value: string): this {
100
- throw new Error('usingConstantParentKey is not supported for join mappings.');
115
+ // See usingParentKey - accepted for interface parity with From, no wire effect for Join.
116
+ return this;
101
117
  }
102
118
 
103
119
  /** @inheritdoc */
@@ -119,30 +135,63 @@ export class JoinBuilder<TReadModel, TEvent> implements IJoinBuilder<TReadModel,
119
135
  }
120
136
 
121
137
  /** @inheritdoc */
122
- add(_readModelPropertyAccessor: PropertyAccessor<TReadModel>): IAddBuilder<TEvent, this> {
123
- throw new Error('add is not implemented yet.');
138
+ add(readModelPropertyAccessor: PropertyAccessor<TReadModel>): IAddBuilder<TEvent, this> {
139
+ const handler = new PropertyPathResolverProxyHandler();
140
+ const proxy = new Proxy({}, handler);
141
+ readModelPropertyAccessor(proxy as TReadModel);
142
+ return new AddBuilder<TEvent, this>(
143
+ handler.property,
144
+ (property, expression) => { this.entry.properties[property] = expression; },
145
+ this
146
+ );
124
147
  }
125
148
 
126
149
  /** @inheritdoc */
127
- subtract(_readModelPropertyAccessor: PropertyAccessor<TReadModel>): ISubtractBuilder<TEvent, this> {
128
- throw new Error('subtract is not implemented yet.');
150
+ subtract(readModelPropertyAccessor: PropertyAccessor<TReadModel>): ISubtractBuilder<TEvent, this> {
151
+ const handler = new PropertyPathResolverProxyHandler();
152
+ const proxy = new Proxy({}, handler);
153
+ readModelPropertyAccessor(proxy as TReadModel);
154
+ return new SubtractBuilder<TEvent, this>(
155
+ handler.property,
156
+ (property, expression) => { this.entry.properties[property] = expression; },
157
+ this
158
+ );
129
159
  }
130
160
 
131
161
  /** @inheritdoc */
132
- count(_readModelPropertyAccessor: PropertyAccessor<TReadModel>): this {
133
- throw new Error('count is not implemented yet.');
162
+ count(readModelPropertyAccessor: PropertyAccessor<TReadModel>): this {
163
+ const handler = new PropertyPathResolverProxyHandler();
164
+ const proxy = new Proxy({}, handler);
165
+ readModelPropertyAccessor(proxy as TReadModel);
166
+ this.entry.properties[handler.property] = '$count';
167
+ return this;
134
168
  }
135
169
 
136
170
  /** @inheritdoc */
137
171
  addChild<TChildModel>(
138
- _targetPropertyAccessor: PropertyAccessor<TReadModel>,
139
- _eventPropertyAccessorOrBuilderCallback: PropertyAccessor<TEvent> | ((builder: IAddChildBuilder<TChildModel, TEvent>) => void)
172
+ targetPropertyAccessor: PropertyAccessor<TReadModel>,
173
+ eventPropertyAccessorOrBuilderCallback: PropertyAccessor<TEvent> | ((builder: IAddChildBuilder<TChildModel, TEvent>) => void)
140
174
  ): this {
141
- throw new Error('addChild is not implemented yet.');
175
+ const targetHandler = new PropertyPathResolverProxyHandler();
176
+ const targetProxy = new Proxy({}, targetHandler);
177
+ targetPropertyAccessor(targetProxy as TReadModel);
178
+
179
+ const probe = new AddChildBuilder<TChildModel, TEvent>();
180
+ const probeProxy = new Proxy({}, probe);
181
+ (eventPropertyAccessorOrBuilderCallback as (value: unknown) => void)(probeProxy);
182
+
183
+ this.entry.children.push(probe.usedAsBuilder
184
+ ? { targetProperty: targetHandler.property, identifiedBy: probe.identifiedByProperty, usingKey: probe.usingKeyProperty }
185
+ : { targetProperty: targetHandler.property, fromEventProperty: probe.capturedEventProperty });
186
+ return this;
142
187
  }
143
188
 
144
189
  /** @inheritdoc */
145
190
  setThisValue(): ISetBuilder<TEvent, this> {
146
- throw new Error('setThisValue is not implemented yet.');
191
+ return new SetBuilder<TEvent, this>(
192
+ '$this',
193
+ (rp, expr) => { this.entry.properties[rp] = expr; },
194
+ this
195
+ );
147
196
  }
148
197
  }
@@ -0,0 +1,76 @@
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 { ChildrenBuilder } from './ChildrenBuilder';
9
+
10
+ /**
11
+ * Concrete implementation of {@link INestedBuilder}, building the `ChildrenDefinition` for a
12
+ * nested single-object sub-projection reached through `.nested()`.
13
+ * @template TParentReadModel - The parent read model type.
14
+ * @template TNestedReadModel - The nested object read model type.
15
+ */
16
+ export class NestedBuilder<TParentReadModel, TNestedReadModel>
17
+ extends ProjectionBuilderCore<TNestedReadModel, INestedBuilder<TParentReadModel, TNestedReadModel>>
18
+ implements INestedBuilder<TParentReadModel, TNestedReadModel> {
19
+ /** @inheritdoc */
20
+ clearWith(eventType: Function): INestedBuilder<TParentReadModel, TNestedReadModel> {
21
+ const contractType = this.toContractEventType(eventType);
22
+ this._removedWith.push({
23
+ Key: contractType,
24
+ Value: { Key: '$eventSourceId', ParentKey: '' }
25
+ });
26
+ return this;
27
+ }
28
+
29
+ /** @inheritdoc */
30
+ children<TChildModel>(
31
+ targetPropertyAccessor: PropertyAccessor<TNestedReadModel>,
32
+ builderCallback: (builder: IChildrenBuilder<TNestedReadModel, TChildModel>) => void
33
+ ): INestedBuilder<TParentReadModel, TNestedReadModel> {
34
+ const handler = new PropertyPathResolverProxyHandler();
35
+ const proxy = new Proxy({}, handler);
36
+ targetPropertyAccessor(proxy as TNestedReadModel);
37
+
38
+ const builder = new ChildrenBuilder<TNestedReadModel, TChildModel>();
39
+ builderCallback(builder);
40
+ this._children[handler.property] = builder.buildDefinition();
41
+ return this;
42
+ }
43
+
44
+ /** @inheritdoc */
45
+ nested<TGrandNestedModel>(
46
+ targetPropertyAccessor: PropertyAccessor<TNestedReadModel>,
47
+ builderCallback: (builder: INestedBuilder<TNestedReadModel, TGrandNestedModel>) => void
48
+ ): INestedBuilder<TParentReadModel, TNestedReadModel> {
49
+ const handler = new PropertyPathResolverProxyHandler();
50
+ const proxy = new Proxy({}, handler);
51
+ targetPropertyAccessor(proxy as TNestedReadModel);
52
+
53
+ const builder = new NestedBuilder<TNestedReadModel, TGrandNestedModel>();
54
+ builderCallback(builder);
55
+ this._nested[handler.property] = builder.buildDefinition();
56
+ return this;
57
+ }
58
+
59
+ /**
60
+ * Builds the {@link ChildrenDefinitionLike} representing this nested object.
61
+ * @returns The accumulated nested definition.
62
+ */
63
+ buildDefinition(): ChildrenDefinitionLike {
64
+ return {
65
+ IdentifiedBy: '',
66
+ From: this._from,
67
+ Join: this._join,
68
+ Children: this._children,
69
+ All: this._all,
70
+ RemovedWith: this._removedWith,
71
+ RemovedWithJoin: this._removedWithJoin,
72
+ AutoMap: this._autoMap,
73
+ Nested: this._nested
74
+ };
75
+ }
76
+ }
@@ -0,0 +1,257 @@
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 { AutoMap } from '@cratis/chronicle.contracts';
5
+ import { getEventTypeFor } from '../../events/eventTypeDecorator';
6
+ import { FromBuilder } from './FromBuilder';
7
+ import { IFromBuilder } from './IFromBuilder';
8
+ import { IFromEveryBuilder } from './IFromEveryBuilder';
9
+ import { IJoinBuilder } from './IJoinBuilder';
10
+ import { IRemovedWithBuilder } from './IRemovedWithBuilder';
11
+ import { IRemovedWithJoinBuilder } from './IRemovedWithJoinBuilder';
12
+ import { JoinBuilder } from './JoinBuilder';
13
+ import { FromEveryBuilder } from './FromEveryBuilder';
14
+ import { RemovedWithBuilder } from './RemovedWithBuilder';
15
+ import { RemovedWithJoinBuilder } from './RemovedWithJoinBuilder';
16
+ import { ChildAdditionEntry } from './AddChildBuilder';
17
+
18
+ /** The contract-level event type identifier shape used across projection definitions. */
19
+ export type ContractEventType = { Id: string; Generation: number; Tombstone: boolean };
20
+
21
+ /** Accumulated property mapping for a from clause. */
22
+ export interface FromRecord {
23
+ Key: ContractEventType;
24
+ Value: { Properties: Record<string, string>; Key: string; ParentKey: string };
25
+ }
26
+
27
+ /** Accumulated property mapping for a join clause. */
28
+ export interface JoinRecord {
29
+ Key: ContractEventType;
30
+ Value: { On: string; Properties: Record<string, string>; Key: string };
31
+ }
32
+
33
+ /** Accumulated removal mapping for a removedWith clause. */
34
+ export interface RemovedWithRecord {
35
+ Key: ContractEventType;
36
+ Value: { Key: string; ParentKey: string };
37
+ }
38
+
39
+ /** Accumulated removal mapping for a removedWithJoin clause. */
40
+ export interface RemovedWithJoinRecord {
41
+ Key: ContractEventType;
42
+ Value: { Key: string };
43
+ }
44
+
45
+ /**
46
+ * Plain object shape matching the wire-level `ChildrenDefinition` used for both children
47
+ * collections and nested single-object projections.
48
+ */
49
+ export interface ChildrenDefinitionLike {
50
+ IdentifiedBy: string;
51
+ From: FromRecord[];
52
+ Join: JoinRecord[];
53
+ Children: Record<string, ChildrenDefinitionLike>;
54
+ All: { Properties: Record<string, string>; IncludeChildren: boolean; AutoMap: AutoMap };
55
+ FromEventProperty?: { Event: ContractEventType | undefined; PropertyExpression: string };
56
+ RemovedWith: RemovedWithRecord[];
57
+ RemovedWithJoin: RemovedWithJoinRecord[];
58
+ AutoMap: AutoMap;
59
+ Nested: Record<string, ChildrenDefinitionLike>;
60
+ }
61
+
62
+ /**
63
+ * Base accumulator shared by every projection-shaped builder (the top-level
64
+ * {@link ProjectionBuilderFor}, and the child/nested builders reached through
65
+ * `.children()`/`.nested()`). Holds the from/join/fromEvery/removedWith/removedWithJoin/
66
+ * children/nested state and the mapping operations common to all of them, mirroring the
67
+ * shared `ProjectionBuilder<TReadModel, TBuilder>` base class on the C# client.
68
+ * @template TReadModel - The read model type this builder produces.
69
+ * @template TBuilder - The concrete builder type returned for fluent chaining.
70
+ */
71
+ export abstract class ProjectionBuilderCore<TReadModel, TBuilder> {
72
+ protected _autoMap: AutoMap = AutoMap.Inherit;
73
+ protected _initialState: string = '{}';
74
+ protected readonly _from: FromRecord[] = [];
75
+ protected readonly _join: JoinRecord[] = [];
76
+ protected readonly _removedWith: RemovedWithRecord[] = [];
77
+ protected readonly _removedWithJoin: RemovedWithJoinRecord[] = [];
78
+ protected readonly _children: Record<string, ChildrenDefinitionLike> = {};
79
+ protected readonly _nested: Record<string, ChildrenDefinitionLike> = {};
80
+ protected _all: { Properties: Record<string, string>; IncludeChildren: boolean; AutoMap: AutoMap } = {
81
+ Properties: {},
82
+ IncludeChildren: false,
83
+ AutoMap: AutoMap.Inherit
84
+ };
85
+
86
+ /** @inheritdoc */
87
+ autoMap(): TBuilder {
88
+ this._autoMap = AutoMap.Enabled;
89
+ return this as unknown as TBuilder;
90
+ }
91
+
92
+ /** @inheritdoc */
93
+ noAutoMap(): TBuilder {
94
+ this._autoMap = AutoMap.Disabled;
95
+ return this as unknown as TBuilder;
96
+ }
97
+
98
+ /** @inheritdoc */
99
+ withInitialValues(initialValueProvider: () => TReadModel): TBuilder {
100
+ this._initialState = JSON.stringify(initialValueProvider());
101
+ return this as unknown as TBuilder;
102
+ }
103
+
104
+ /** @inheritdoc */
105
+ from<TEvent>(
106
+ eventType: new (...args: any[]) => TEvent,
107
+ builderCallback?: (builder: IFromBuilder<TReadModel, TEvent>) => void
108
+ ): TBuilder {
109
+ const contractType = this.toContractEventType(eventType);
110
+ const fromBuilder = new FromBuilder<TReadModel, TEvent>();
111
+ builderCallback?.(fromBuilder);
112
+ this._from.push({
113
+ Key: contractType,
114
+ Value: {
115
+ Properties: fromBuilder.entry.properties,
116
+ Key: fromBuilder.entry.key,
117
+ ParentKey: fromBuilder.entry.parentKey
118
+ }
119
+ });
120
+ this.mergeChildAdditions(contractType, fromBuilder.entry.children);
121
+ return this as unknown as TBuilder;
122
+ }
123
+
124
+ /** @inheritdoc */
125
+ join<TEvent>(
126
+ eventType: new (...args: any[]) => TEvent,
127
+ builderCallback?: (builder: IJoinBuilder<TReadModel, TEvent>) => void
128
+ ): TBuilder {
129
+ const contractType = this.toContractEventType(eventType);
130
+ const joinBuilder = new JoinBuilder<TReadModel, TEvent>();
131
+ builderCallback?.(joinBuilder);
132
+ this._join.push({
133
+ Key: contractType,
134
+ Value: {
135
+ On: joinBuilder.entry.on,
136
+ Properties: joinBuilder.entry.properties,
137
+ Key: joinBuilder.entry.key
138
+ }
139
+ });
140
+ this.mergeChildAdditions(contractType, joinBuilder.entry.children);
141
+ return this as unknown as TBuilder;
142
+ }
143
+
144
+ /** @inheritdoc */
145
+ fromEvery(builderCallback: (builder: IFromEveryBuilder<TReadModel>) => void): TBuilder {
146
+ const builder = new FromEveryBuilder<TReadModel>();
147
+ builderCallback(builder);
148
+ this._all = {
149
+ Properties: {
150
+ ...this._all.Properties,
151
+ ...builder.entry.properties
152
+ },
153
+ IncludeChildren: builder.entry.includeChildren,
154
+ AutoMap: AutoMap.Inherit
155
+ };
156
+ return this as unknown as TBuilder;
157
+ }
158
+
159
+ /** @inheritdoc */
160
+ removedWith<TEvent>(
161
+ eventType: new (...args: any[]) => TEvent,
162
+ builderCallback?: (builder: IRemovedWithBuilder<TReadModel, TEvent>) => void
163
+ ): TBuilder {
164
+ const contractType = this.toContractEventType(eventType);
165
+ const removedWithBuilder = new RemovedWithBuilder<TReadModel, TEvent>();
166
+ builderCallback?.(removedWithBuilder);
167
+ this._removedWith.push({
168
+ Key: contractType,
169
+ Value: {
170
+ Key: removedWithBuilder.entry.key,
171
+ ParentKey: removedWithBuilder.entry.parentKey
172
+ }
173
+ });
174
+ return this as unknown as TBuilder;
175
+ }
176
+
177
+ /** @inheritdoc */
178
+ removedWithJoin<TEvent>(
179
+ eventType: new (...args: any[]) => TEvent,
180
+ builderCallback?: (builder: IRemovedWithJoinBuilder<TReadModel, TEvent>) => void
181
+ ): TBuilder {
182
+ const contractType = this.toContractEventType(eventType);
183
+ const removedWithJoinBuilder = new RemovedWithJoinBuilder<TReadModel, TEvent>();
184
+ builderCallback?.(removedWithJoinBuilder);
185
+ this._removedWithJoin.push({
186
+ Key: contractType,
187
+ Value: {
188
+ Key: removedWithJoinBuilder.entry.key
189
+ }
190
+ });
191
+ return this as unknown as TBuilder;
192
+ }
193
+
194
+ /**
195
+ * Creates an empty {@link ChildrenDefinitionLike} accumulator for a children/nested entry.
196
+ * @returns A fresh, empty children definition.
197
+ */
198
+ protected createEmptyChildrenDefinition(): ChildrenDefinitionLike {
199
+ return {
200
+ IdentifiedBy: '$eventSourceId',
201
+ From: [],
202
+ Join: [],
203
+ Children: {},
204
+ All: { Properties: {}, IncludeChildren: false, AutoMap: AutoMap.Inherit },
205
+ RemovedWith: [],
206
+ RemovedWithJoin: [],
207
+ AutoMap: AutoMap.Inherit,
208
+ Nested: {}
209
+ };
210
+ }
211
+
212
+ /**
213
+ * Merges `.addChild()` accumulations captured on a from/join builder into this builder's
214
+ * children definitions, creating the target child entry on first use.
215
+ * @param eventType - The contract event type the child additions were captured for.
216
+ * @param children - The accumulated child additions.
217
+ */
218
+ private mergeChildAdditions(eventType: ContractEventType, children: ChildAdditionEntry[]): void {
219
+ for (const child of children) {
220
+ const childDefinition = this._children[child.targetProperty] ??= this.createEmptyChildrenDefinition();
221
+
222
+ if (child.identifiedBy) {
223
+ childDefinition.IdentifiedBy = child.identifiedBy;
224
+ }
225
+
226
+ if (child.fromEventProperty) {
227
+ childDefinition.FromEventProperty = { Event: eventType, PropertyExpression: child.fromEventProperty };
228
+ }
229
+
230
+ childDefinition.From.push({
231
+ Key: eventType,
232
+ Value: {
233
+ Key: child.usingKey ?? '$eventSourceId',
234
+ ParentKey: '$eventSourceId',
235
+ Properties: {}
236
+ }
237
+ });
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Resolves the contract event type for a decorated event constructor.
243
+ * @param eventTypeConstructor - The event class constructor.
244
+ * @returns The contract-level event type identifier.
245
+ */
246
+ protected toContractEventType(eventTypeConstructor: Function): ContractEventType {
247
+ const eventType = getEventTypeFor(eventTypeConstructor);
248
+ if (eventType.id.value === '') {
249
+ throw new Error(`Event type '${eventTypeConstructor.name}' is not decorated with @eventType().`);
250
+ }
251
+ return {
252
+ Id: eventType.id.value,
253
+ Generation: eventType.generation.value,
254
+ Tombstone: false
255
+ };
256
+ }
257
+ }
@@ -0,0 +1,115 @@
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 ItemAdded {
13
+ quantity!: number;
14
+ }
15
+ eventType()(ItemAdded);
16
+
17
+ class ItemRemoved {
18
+ quantity!: number;
19
+ }
20
+ eventType()(ItemRemoved);
21
+
22
+ class ThingHappened {}
23
+ eventType()(ThingHappened);
24
+
25
+ class Inventory {
26
+ id!: string;
27
+ total!: number;
28
+ removedTotal!: number;
29
+ thingsHappenedCount!: number;
30
+ }
31
+
32
+ interface FromRecord {
33
+ Key: { Id: string };
34
+ Value: { Properties: Record<string, string>; Key: string };
35
+ }
36
+
37
+ interface JoinRecord {
38
+ Key: { Id: string };
39
+ Value: { Properties: Record<string, string> };
40
+ }
41
+
42
+ describe('ProjectionBuilderFor', () => {
43
+ describe('when using add on a from builder', () => {
44
+ it('should produce a $add expression targeting the event property', () => {
45
+ const builder = new ProjectionBuilderFor<Inventory>();
46
+ builder.from(ItemAdded, from => from.add(model => model.total).with(event => event.quantity));
47
+
48
+ const definition = builder.build('inventory', 'Inventory') as unknown as { From: FromRecord[] };
49
+ const entry = definition.From.find(candidate => candidate.Key.Id === 'ItemAdded')!;
50
+
51
+ expect(entry.Value.Properties.total).toBe('$add(quantity)');
52
+ });
53
+ });
54
+
55
+ describe('when using subtract on a from builder', () => {
56
+ it('should produce a $subtract expression targeting the event property', () => {
57
+ const builder = new ProjectionBuilderFor<Inventory>();
58
+ builder.from(ItemRemoved, from => from.subtract(model => model.removedTotal).with(event => event.quantity));
59
+
60
+ const definition = builder.build('inventory', 'Inventory') as unknown as { From: FromRecord[] };
61
+ const entry = definition.From.find(candidate => candidate.Key.Id === 'ItemRemoved')!;
62
+
63
+ expect(entry.Value.Properties.removedTotal).toBe('$subtract(quantity)');
64
+ });
65
+ });
66
+
67
+ describe('when using count on a from builder', () => {
68
+ it('should produce a $count expression and return the builder for chaining', () => {
69
+ const builder = new ProjectionBuilderFor<Inventory>();
70
+ builder.from(ThingHappened, from => from.count(model => model.thingsHappenedCount).usingConstantKey('singleton'));
71
+
72
+ const definition = builder.build('inventory', 'Inventory') as unknown as { From: FromRecord[] };
73
+ const entry = definition.From.find(candidate => candidate.Key.Id === 'ThingHappened')!;
74
+
75
+ expect(entry.Value.Properties.thingsHappenedCount).toBe('$count');
76
+ expect(entry.Value.Key).toBe('singleton');
77
+ });
78
+ });
79
+
80
+ describe('when using add on a join builder', () => {
81
+ it('should produce a $add expression targeting the event property', () => {
82
+ const builder = new ProjectionBuilderFor<Inventory>();
83
+ builder.join(ItemAdded, join => join.add(model => model.total).with(event => event.quantity));
84
+
85
+ const definition = builder.build('inventory', 'Inventory') as unknown as { Join: JoinRecord[] };
86
+ const entry = definition.Join.find(candidate => candidate.Key.Id === 'ItemAdded')!;
87
+
88
+ expect(entry.Value.Properties.total).toBe('$add(quantity)');
89
+ });
90
+ });
91
+
92
+ describe('when using subtract on a join builder', () => {
93
+ it('should produce a $subtract expression targeting the event property', () => {
94
+ const builder = new ProjectionBuilderFor<Inventory>();
95
+ builder.join(ItemRemoved, join => join.subtract(model => model.removedTotal).with(event => event.quantity));
96
+
97
+ const definition = builder.build('inventory', 'Inventory') as unknown as { Join: JoinRecord[] };
98
+ const entry = definition.Join.find(candidate => candidate.Key.Id === 'ItemRemoved')!;
99
+
100
+ expect(entry.Value.Properties.removedTotal).toBe('$subtract(quantity)');
101
+ });
102
+ });
103
+
104
+ describe('when using count on a join builder', () => {
105
+ it('should produce a $count expression', () => {
106
+ const builder = new ProjectionBuilderFor<Inventory>();
107
+ builder.join(ThingHappened, join => join.count(model => model.thingsHappenedCount));
108
+
109
+ const definition = builder.build('inventory', 'Inventory') as unknown as { Join: JoinRecord[] };
110
+ const entry = definition.Join.find(candidate => candidate.Key.Id === 'ThingHappened')!;
111
+
112
+ expect(entry.Value.Properties.thingsHappenedCount).toBe('$count');
113
+ });
114
+ });
115
+ });