@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
@@ -0,0 +1,182 @@
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 { describe, expect, it, vi } from 'vitest';
5
+ import type { IEventLog } from '../eventSequences/IEventLog';
6
+ import { EventSequenceNumber } from '../eventSequences/EventSequenceNumber';
7
+ import { eventType } from '../events/eventTypeDecorator';
8
+ import { appendReactorSideEffects } from './ReactorSideEffects';
9
+
10
+ class SomethingHappened {
11
+ constructor(readonly value: string = '') {}
12
+ }
13
+ eventType('c3f6a2f0-6f2f-4a3c-9d3f-6f2f4a3c9d3h')(SomethingHappened);
14
+
15
+ class SomethingElseHappened {
16
+ constructor(readonly value: string = '') {}
17
+ }
18
+ eventType('d3f6a2f0-6f2f-4a3c-9d3f-6f2f4a3c9d3i')(SomethingElseHappened);
19
+
20
+ function createEventLog(appendManyResult?: unknown) {
21
+ const appendMany = vi.fn().mockResolvedValue(appendManyResult ?? [
22
+ { sequenceNumber: new EventSequenceNumber(1n), constraintViolations: [], errors: [], isSuccess: true }
23
+ ]);
24
+ const eventLog = { appendMany } as unknown as IEventLog;
25
+ return { eventLog, appendMany };
26
+ }
27
+
28
+ const successResultFor = (count: number) =>
29
+ Array.from({ length: count }, (_, index) => ({
30
+ sequenceNumber: new EventSequenceNumber(BigInt(index + 1)),
31
+ constraintViolations: [],
32
+ errors: [],
33
+ isSuccess: true
34
+ }));
35
+
36
+ describe('appendReactorSideEffects', () => {
37
+ describe('when the handler returns undefined', () => {
38
+ const { eventLog, appendMany } = createEventLog();
39
+
40
+ it('should not append anything', async () => {
41
+ const result = await appendReactorSideEffects(eventLog, undefined, 'triggering-source', 'Default', 'triggering-source');
42
+
43
+ expect(appendMany).not.toHaveBeenCalled();
44
+ expect(result.isSuccess).toBe(true);
45
+ expect(result.errors).toEqual([]);
46
+ });
47
+ });
48
+
49
+ describe('when the handler returns a single bare event', () => {
50
+ const { eventLog, appendMany } = createEventLog(successResultFor(1));
51
+
52
+ it('should append it targeting the triggering event\'s event source, stream, and subject', async () => {
53
+ const result = await appendReactorSideEffects(
54
+ eventLog,
55
+ new SomethingHappened('a'),
56
+ 'triggering-source',
57
+ 'custom-stream-type',
58
+ 'custom-stream-id');
59
+
60
+ expect(appendMany).toHaveBeenCalledTimes(1);
61
+ const [events] = appendMany.mock.calls[0];
62
+ expect(events).toEqual([{
63
+ eventSourceId: 'triggering-source',
64
+ event: expect.any(SomethingHappened),
65
+ eventStreamType: 'custom-stream-type',
66
+ eventStreamId: 'custom-stream-id',
67
+ subject: 'triggering-source'
68
+ }]);
69
+ expect(result.isSuccess).toBe(true);
70
+ });
71
+ });
72
+
73
+ describe('when the handler returns an array of bare events', () => {
74
+ const { eventLog, appendMany } = createEventLog(successResultFor(2));
75
+
76
+ it('should append all of them in a single atomic AppendMany call', async () => {
77
+ await appendReactorSideEffects(
78
+ eventLog,
79
+ [new SomethingHappened('a'), new SomethingElseHappened('b')],
80
+ 'triggering-source',
81
+ 'Default',
82
+ 'triggering-source');
83
+
84
+ expect(appendMany).toHaveBeenCalledTimes(1);
85
+ const [events] = appendMany.mock.calls[0];
86
+ expect(events).toHaveLength(2);
87
+ expect(events[0].event).toBeInstanceOf(SomethingHappened);
88
+ expect(events[1].event).toBeInstanceOf(SomethingElseHappened);
89
+ });
90
+ });
91
+
92
+ describe('when the handler returns a single EventForEventSourceId', () => {
93
+ const { eventLog, appendMany } = createEventLog(successResultFor(1));
94
+
95
+ it('should append it using its own target rather than the triggering event\'s', async () => {
96
+ const wrapper = {
97
+ eventSourceId: 'other-source',
98
+ event: new SomethingHappened('a'),
99
+ eventStreamType: 'other-stream-type',
100
+ eventStreamId: 'other-stream-id',
101
+ subject: 'other-subject'
102
+ };
103
+
104
+ await appendReactorSideEffects(eventLog, wrapper, 'triggering-source', 'Default', 'triggering-source');
105
+
106
+ expect(appendMany).toHaveBeenCalledTimes(1);
107
+ const [events] = appendMany.mock.calls[0];
108
+ expect(events).toEqual([wrapper]);
109
+ });
110
+ });
111
+
112
+ describe('when the handler returns an array of EventForEventSourceId', () => {
113
+ const { eventLog, appendMany } = createEventLog(successResultFor(2));
114
+
115
+ it('should append all of them, each keeping its own target, in one atomic call', async () => {
116
+ const wrappers = [
117
+ { eventSourceId: 'source-1', event: new SomethingHappened('a') },
118
+ { eventSourceId: 'source-2', event: new SomethingElseHappened('b') }
119
+ ];
120
+
121
+ await appendReactorSideEffects(eventLog, wrappers, 'triggering-source', 'Default', 'triggering-source');
122
+
123
+ expect(appendMany).toHaveBeenCalledTimes(1);
124
+ const [events] = appendMany.mock.calls[0];
125
+ expect(events).toEqual(wrappers);
126
+ });
127
+ });
128
+
129
+ describe('when the handler returns a mix of bare events and EventForEventSourceId', () => {
130
+ const { eventLog, appendMany } = createEventLog(successResultFor(2));
131
+
132
+ it('should append the bare event using the triggering context and the wrapper using its own, in one atomic call', async () => {
133
+ const wrapper = { eventSourceId: 'other-source', event: new SomethingElseHappened('b') };
134
+ const bareEvent = new SomethingHappened('a');
135
+
136
+ await appendReactorSideEffects(eventLog, [bareEvent, wrapper], 'triggering-source', 'Default', 'triggering-source');
137
+
138
+ expect(appendMany).toHaveBeenCalledTimes(1);
139
+ const [events] = appendMany.mock.calls[0];
140
+ expect(events).toEqual([
141
+ {
142
+ eventSourceId: 'triggering-source',
143
+ event: bareEvent,
144
+ eventStreamType: 'Default',
145
+ eventStreamId: 'triggering-source',
146
+ subject: 'triggering-source'
147
+ },
148
+ wrapper
149
+ ]);
150
+ });
151
+ });
152
+
153
+ describe('when a side-effect event fails to append', () => {
154
+ const { eventLog, appendMany } = createEventLog([
155
+ {
156
+ sequenceNumber: EventSequenceNumber.unset,
157
+ constraintViolations: [{ constraintId: 'unique', message: 'Value must be unique', details: {} }],
158
+ errors: [],
159
+ isSuccess: false
160
+ }
161
+ ]);
162
+
163
+ it('should report failure with the constraint violation message', async () => {
164
+ const result = await appendReactorSideEffects(eventLog, new SomethingHappened('a'), 'triggering-source', 'Default', 'triggering-source');
165
+
166
+ expect(appendMany).toHaveBeenCalledTimes(1);
167
+ expect(result.isSuccess).toBe(false);
168
+ expect(result.errors).toEqual(['Value must be unique']);
169
+ });
170
+ });
171
+
172
+ describe('when the handler returns a plain object that is not a known event type or EventForEventSourceId', () => {
173
+ const { eventLog, appendMany } = createEventLog();
174
+
175
+ it('should ignore it and not append anything', async () => {
176
+ const result = await appendReactorSideEffects(eventLog, { some: 'value' }, 'triggering-source', 'Default', 'triggering-source');
177
+
178
+ expect(appendMany).not.toHaveBeenCalled();
179
+ expect(result.isSuccess).toBe(true);
180
+ });
181
+ });
182
+ });
@@ -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 { hasEventType } from '../events/eventTypeDecorator';
5
+ import type { EventForEventSourceId } from '../eventSequences/EventForEventSourceId';
6
+ import type { IEventLog } from '../eventSequences/IEventLog';
7
+
8
+ /**
9
+ * Represents the outcome of appending a reactor handler's returned side-effect events.
10
+ */
11
+ export interface ReactorSideEffectResult {
12
+ /** Whether every side-effect event appended successfully. */
13
+ readonly isSuccess: boolean;
14
+
15
+ /** Constraint violation and error messages, populated when {@link isSuccess} is false. */
16
+ readonly errors: string[];
17
+ }
18
+
19
+ const noSideEffects: ReactorSideEffectResult = { isSuccess: true, errors: [] };
20
+
21
+ /**
22
+ * Determines whether a value is shaped like an {@link EventForEventSourceId} — a plain object
23
+ * carrying its own `eventSourceId` and `event` — rather than an event object returned directly.
24
+ */
25
+ function isEventForEventSourceId(value: unknown): value is EventForEventSourceId {
26
+ if (typeof value !== 'object' || value === null) {
27
+ return false;
28
+ }
29
+
30
+ const candidate = value as Record<string, unknown>;
31
+ return typeof candidate.eventSourceId === 'string' &&
32
+ typeof candidate.event === 'object' && candidate.event !== null &&
33
+ hasEventType((candidate.event as object).constructor as Function);
34
+ }
35
+
36
+ /**
37
+ * Appends the events a `@reactor`-decorated class's handler method returned as side effects, in
38
+ * a single atomic `AppendMany` call — never one append per item, which would neither be atomic
39
+ * nor efficient.
40
+ * @param eventLog - The {@link IEventLog} to append the side effects to.
41
+ * @param handlerResult - Whatever the reactor handler method returned: `undefined`/`void`, a
42
+ * single event object, an array of events, a single {@link EventForEventSourceId}, an array of
43
+ * them, or a mix of bare events and {@link EventForEventSourceId} entries.
44
+ * @param triggeringEventSourceId - The event source id of the event that triggered the reactor,
45
+ * used as the target for bare event returns.
46
+ * @param triggeringEventStreamType - The event stream type of the triggering event, used for bare
47
+ * event returns.
48
+ * @param triggeringEventStreamId - The event stream identifier of the triggering event, used for
49
+ * bare event returns.
50
+ * @returns The outcome of the append. Each {@link EventForEventSourceId} entry keeps its own
51
+ * target (event source id, stream type/id, subject); bare events use the triggering event's.
52
+ */
53
+ export async function appendReactorSideEffects(
54
+ eventLog: IEventLog,
55
+ handlerResult: unknown,
56
+ triggeringEventSourceId: string,
57
+ triggeringEventStreamType: string,
58
+ triggeringEventStreamId: string
59
+ ): Promise<ReactorSideEffectResult> {
60
+ if (handlerResult === undefined || handlerResult === null) {
61
+ return noSideEffects;
62
+ }
63
+
64
+ const items = Array.isArray(handlerResult) ? handlerResult : [handlerResult];
65
+ const events: EventForEventSourceId[] = [];
66
+
67
+ for (const item of items) {
68
+ if (isEventForEventSourceId(item)) {
69
+ events.push(item);
70
+ } else if (typeof item === 'object' && item !== null && hasEventType(item.constructor as Function)) {
71
+ events.push({
72
+ eventSourceId: triggeringEventSourceId,
73
+ event: item,
74
+ eventStreamType: triggeringEventStreamType,
75
+ eventStreamId: triggeringEventStreamId,
76
+ subject: triggeringEventSourceId
77
+ });
78
+ }
79
+ // Anything else — undefined array entries, plain objects that are neither a registered
80
+ // event type nor EventForEventSourceId-shaped — is not a recognized side-effect and is
81
+ // silently ignored, the same as a handler that returns Task/void.
82
+ }
83
+
84
+ if (events.length === 0) {
85
+ return noSideEffects;
86
+ }
87
+
88
+ const results = await eventLog.appendMany(events);
89
+ const errors = results
90
+ .filter(result => !result.isSuccess)
91
+ .flatMap(result => [
92
+ ...result.constraintViolations.map(violation => violation.message),
93
+ ...result.errors.map(error => error.message)
94
+ ]);
95
+
96
+ return { isSuccess: errors.length === 0, errors };
97
+ }
@@ -13,7 +13,10 @@ import { EventContext } from '../events/EventContext';
13
13
  import { EventTypeId } from '../events/EventTypeId';
14
14
  import { EventTypeGeneration } from '../events/EventTypeGeneration';
15
15
  import { EventSequenceId } from '../eventSequences/EventSequenceId';
16
+ import type { IEventLog } from '../eventSequences/IEventLog';
17
+ import { notifyReplayLifecycle } from '../observation/notifyReplayLifecycle';
16
18
  import { IReactors } from './IReactors';
19
+ import { appendReactorSideEffects } from './ReactorSideEffects';
17
20
  import { getReactorMetadata } from './reactor';
18
21
 
19
22
  /** Expression used to partition reactor observations by event source ID. */
@@ -107,13 +110,15 @@ export class Reactors implements IReactors {
107
110
  * @param _eventStoreName - The name of the event store.
108
111
  * @param _namespace - The namespace within the event store.
109
112
  * @param lifecycle - The connection lifecycle used to react to disconnect events.
113
+ * @param _eventLog - The event log used to append side-effect events a reactor handler returns.
110
114
  */
111
115
  constructor(
112
116
  private readonly _clientArtifacts: IClientArtifactsProvider,
113
117
  private readonly _connection: ChronicleConnection,
114
118
  private readonly _eventStoreName: string,
115
119
  private readonly _namespace: string,
116
- lifecycle: ConnectionLifecycle
120
+ lifecycle: ConnectionLifecycle,
121
+ private readonly _eventLog: IEventLog
117
122
  ) {
118
123
  this._lifecycle = lifecycle;
119
124
  lifecycle.onDisconnected(async () => {
@@ -258,10 +263,20 @@ export class Reactors implements IReactors {
258
263
  this._logger.debug('Received events to observe', {
259
264
  reactorId: id,
260
265
  partition: eventsToObserve.Partition,
261
- count: eventsToObserve.Events.length
266
+ count: eventsToObserve.Events.length,
267
+ replayState: eventsToObserve.ReplayState
262
268
  });
263
269
 
264
- for (const event of eventsToObserve.Events) {
270
+ try {
271
+ await notifyReplayLifecycle(reactorInstance, eventsToObserve.ReplayState, eventsToObserve.Partition);
272
+ } catch (err) {
273
+ this._logger.error('Error notifying reactor of replay lifecycle transition', { reactorId: id, error: String(err) });
274
+ exceptionMessages.push(String(err));
275
+ exceptionStackTrace = err instanceof Error ? (err.stack ?? '') : '';
276
+ state = ObservationState.Failed;
277
+ }
278
+
279
+ for (const event of state === ObservationState.Failed ? [] : eventsToObserve.Events) {
265
280
  try {
266
281
  const eventTypeId = event.Context?.EventType?.Id;
267
282
  if (!eventTypeId) {
@@ -298,7 +313,17 @@ export class Reactors implements IReactors {
298
313
  eventTypeId
299
314
  });
300
315
 
301
- await reactorInstance[entry.methodName](content, context);
316
+ const handlerResult = await reactorInstance[entry.methodName](content, context);
317
+ const sideEffectResult = await appendReactorSideEffects(
318
+ this._eventLog,
319
+ handlerResult,
320
+ event.Context!.EventSourceId,
321
+ event.Context!.EventStreamType,
322
+ event.Context!.EventStreamId);
323
+ if (!sideEffectResult.isSuccess) {
324
+ throw new Error(`Reactor side effect failed to append: ${sideEffectResult.errors.join('; ')}`);
325
+ }
326
+
302
327
  lastSuccessfullyObservedEvent = event.Context!.SequenceNumber;
303
328
  } catch (err) {
304
329
  this._logger.error('Error handling event in reactor', { reactorId: id, error: String(err) });
@@ -0,0 +1,87 @@
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 { describe, expect, it, vi } from 'vitest';
5
+ import type { Constructor } from '@cratis/fundamentals';
6
+ import type { IClientArtifactsProvider } from '../artifacts';
7
+ import type { ChronicleConnection } from '../connection';
8
+ import { ConnectionLifecycle } from '../connection/ConnectionLifecycle';
9
+ import { reducer } from './reducer';
10
+ import { Reducers } from './Reducers';
11
+
12
+ const flush = () => new Promise(resolve => setTimeout(resolve, 0));
13
+
14
+ /**
15
+ * A minimal client artifacts provider exposing only the reducer types under test —
16
+ * Reducers.ts only reads .reducers and .eventTypes off this during discovery.
17
+ */
18
+ function createArtifacts(reducers: Constructor[]): IClientArtifactsProvider {
19
+ return {
20
+ eventTypes: [],
21
+ readModels: [],
22
+ reactors: [],
23
+ reducers,
24
+ seeders: [],
25
+ constraints: [],
26
+ projections: [],
27
+ webhooks: [],
28
+ eventTypeMigrations: []
29
+ };
30
+ }
31
+
32
+ /**
33
+ * Captures the first message a reducer observation pushes onto its queue (the registration
34
+ * message), by having the mocked `observe` call itself consume the queue's async iterator.
35
+ */
36
+ function createConnection() {
37
+ const registrationMessages: unknown[] = [];
38
+ const observe = vi.fn().mockImplementation(async function* (queue: AsyncIterable<unknown>) {
39
+ for await (const message of queue) {
40
+ registrationMessages.push(message);
41
+ break;
42
+ }
43
+ });
44
+ const registerMany = vi.fn().mockResolvedValue({});
45
+ const connection = {
46
+ reducers: { observe },
47
+ readModels: { registerMany }
48
+ } as unknown as ChronicleConnection;
49
+
50
+ return { connection, registrationMessages };
51
+ }
52
+
53
+ describe('Reducers', () => {
54
+ describe('when registering an active reducer (the default)', () => {
55
+ class SomeReducer {}
56
+ reducer('some-active-reducer')(SomeReducer);
57
+
58
+ it('should send a registration message with IsActive true', async () => {
59
+ const { connection, registrationMessages } = createConnection();
60
+ const reducers = new Reducers(createArtifacts([SomeReducer]), connection, 'my-event-store', 'my-namespace', new ConnectionLifecycle(), 'default-sink');
61
+
62
+ await reducers.register();
63
+ await flush();
64
+
65
+ expect(registrationMessages).toHaveLength(1);
66
+ const message = registrationMessages[0] as { Content: { Value0: { Reducer: { IsActive: boolean } } } };
67
+ expect(message.Content.Value0.Reducer.IsActive).toBe(true);
68
+ });
69
+ });
70
+
71
+ describe('when registering a passive (isActive: false) reducer', () => {
72
+ class SomePassiveReducer {}
73
+ reducer('some-passive-reducer', undefined, undefined, false)(SomePassiveReducer);
74
+
75
+ it('should send a registration message with IsActive false', async () => {
76
+ const { connection, registrationMessages } = createConnection();
77
+ const reducers = new Reducers(createArtifacts([SomePassiveReducer]), connection, 'my-event-store', 'my-namespace', new ConnectionLifecycle(), 'default-sink');
78
+
79
+ await reducers.register();
80
+ await flush();
81
+
82
+ expect(registrationMessages).toHaveLength(1);
83
+ const message = registrationMessages[0] as { Content: { Value0: { Reducer: { IsActive: boolean } } } };
84
+ expect(message.Content.Value0.Reducer.IsActive).toBe(false);
85
+ });
86
+ });
87
+ });
@@ -11,6 +11,7 @@ import { toContractsGuid } from '../connection/Guid';
11
11
  import { ConnectionLifecycle } from '../connection/ConnectionLifecycle';
12
12
  import { getEventTypeMetadata } from '../events/eventTypeDecorator';
13
13
  import { EventSequenceId } from '../eventSequences/EventSequenceId';
14
+ import { notifyReplayLifecycle } from '../observation/notifyReplayLifecycle';
14
15
  import { IReducers } from './IReducers';
15
16
  import { getReducerMetadata } from './reducer';
16
17
  import { getReadModelMetadata } from '../readModels';
@@ -292,6 +293,7 @@ export class Reducers implements IReducers {
292
293
  ): Promise<void> {
293
294
  const queue = new AsyncQueue<ReducerMessage>();
294
295
  this._queues.set(id, queue);
296
+ const isActive = getReducerMetadata(reducerType)?.isActive ?? true;
295
297
 
296
298
  queue.send({
297
299
  Content: {
@@ -307,7 +309,7 @@ export class Reducers implements IReducers {
307
309
  Key: EVENT_SOURCE_ID_KEY
308
310
  })),
309
311
  ReadModel: readModelName,
310
- IsActive: true,
312
+ IsActive: isActive,
311
313
  Tags: [],
312
314
  Filters: {
313
315
  FilterTags: [],
@@ -334,14 +336,24 @@ export class Reducers implements IReducers {
334
336
  reducerId: id,
335
337
  partition: operation.Partition,
336
338
  count: operation.Events.length,
337
- hasInitialState: operation.InitialState !== ''
339
+ hasInitialState: operation.InitialState !== '',
340
+ replayState: operation.ReplayState
338
341
  });
339
342
 
343
+ try {
344
+ await notifyReplayLifecycle(reducerInstance, operation.ReplayState, operation.Partition);
345
+ } catch (err) {
346
+ this._logger.error('Error notifying reducer of replay lifecycle transition', { reducerId: id, error: String(err) });
347
+ exceptionMessages.push(String(err));
348
+ exceptionStackTrace = err instanceof Error ? (err.stack ?? '') : '';
349
+ state = ObservationState.Failed;
350
+ }
351
+
340
352
  let currentState: unknown = operation.InitialState
341
353
  ? JSON.parse(operation.InitialState) as unknown
342
354
  : undefined;
343
355
 
344
- for (const event of operation.Events) {
356
+ for (const event of state === ObservationState.Failed ? [] : operation.Events) {
345
357
  try {
346
358
  const eventTypeId = event.Context?.EventType?.Id;
347
359
  if (!eventTypeId) {
@@ -0,0 +1,40 @@
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 { describe, expect, it } from 'vitest';
5
+ import { getReducerMetadata, reducer } from './reducer';
6
+
7
+ describe('reducer', () => {
8
+ describe('when decorating a class without specifying isActive', () => {
9
+ class SomeReducer {}
10
+ reducer('some-reducer')(SomeReducer);
11
+
12
+ it('should default isActive to true', () => {
13
+ const metadata = getReducerMetadata(SomeReducer);
14
+
15
+ expect(metadata?.isActive).toBe(true);
16
+ });
17
+ });
18
+
19
+ describe('when decorating a class with isActive explicitly true', () => {
20
+ class SomeActiveReducer {}
21
+ reducer('some-active-reducer', undefined, undefined, true)(SomeActiveReducer);
22
+
23
+ it('should set isActive to true', () => {
24
+ const metadata = getReducerMetadata(SomeActiveReducer);
25
+
26
+ expect(metadata?.isActive).toBe(true);
27
+ });
28
+ });
29
+
30
+ describe('when decorating a class with isActive false', () => {
31
+ class SomePassiveReducer {}
32
+ reducer('some-passive-reducer', undefined, undefined, false)(SomePassiveReducer);
33
+
34
+ it('should set isActive to false', () => {
35
+ const metadata = getReducerMetadata(SomePassiveReducer);
36
+
37
+ expect(metadata?.isActive).toBe(false);
38
+ });
39
+ });
40
+ });
@@ -21,6 +21,13 @@ export interface ReducerMetadata {
21
21
 
22
22
  /** The optional read model type produced by the reducer. */
23
23
  readonly readModel: Constructor | undefined;
24
+
25
+ /**
26
+ * Whether the reducer is active. An inactive (passive) reducer is registered with the
27
+ * Chronicle Kernel but does not automatically observe events — it can still be invoked
28
+ * on demand. Defaults to true.
29
+ */
30
+ readonly isActive: boolean;
24
31
  }
25
32
 
26
33
  /**
@@ -34,6 +41,8 @@ export interface ReducerMetadata {
34
41
  * @param id - The unique identifier for the reducer. Defaults to the class name if omitted.
35
42
  * @param eventSequenceId - Optional explicit event sequence identifier.
36
43
  * @param readModel - Optional read model type produced by the reducer.
44
+ * @param isActive - Whether the reducer is active (automatically observes events). Defaults to true.
45
+ * A passive reducer is registered but must be invoked on demand rather than observing automatically.
37
46
  * @returns A class decorator.
38
47
  *
39
48
  * @example
@@ -46,11 +55,11 @@ export interface ReducerMetadata {
46
55
  * }
47
56
  * ```
48
57
  */
49
- export function reducer(id: string = '', eventSequenceId?: string, readModel?: Constructor): ClassDecorator {
58
+ export function reducer(id: string = '', eventSequenceId?: string, readModel?: Constructor, isActive: boolean = true): ClassDecorator {
50
59
  return (target: object) => {
51
60
  const constructor = target as Function;
52
61
  const reducerId = new ReducerId(id || constructor.name);
53
- const metadata: ReducerMetadata = { id: reducerId, eventSequenceId, readModel };
62
+ const metadata: ReducerMetadata = { id: reducerId, eventSequenceId, readModel, isActive };
54
63
  Reflect.defineMetadata(REDUCER_METADATA_KEY, metadata, target);
55
64
  TypeDiscoverer.default.register(
56
65
  DecoratorType.Reducer,
@@ -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 { Guid } from '@cratis/fundamentals';
5
+ import { AppendError } from '../eventSequences/AppendError';
6
+ import { ConcurrencyViolation } from '../eventSequences/ConcurrencyViolation';
7
+ import { ConstraintViolation } from '../eventSequences/ConstraintViolation';
5
8
  import { EventSequenceId } from '../eventSequences/EventSequenceId';
6
9
  import { AppendResult } from '../eventSequences/AppendResult';
7
10
 
@@ -38,6 +41,24 @@ export interface IUnitOfWork {
38
41
  */
39
42
  getAppendResults(): ReadonlyArray<AppendResult>;
40
43
 
44
+ /**
45
+ * Gets any constraint violations that occurred across the append results from the latest commit.
46
+ * @returns A thin filter over {@link getAppendResults}.
47
+ */
48
+ getConstraintViolations(): ReadonlyArray<ConstraintViolation>;
49
+
50
+ /**
51
+ * Gets any concurrency violations that occurred across the append results from the latest commit.
52
+ * @returns A thin filter over {@link getAppendResults}.
53
+ */
54
+ getConcurrencyViolations(): ReadonlyArray<ConcurrencyViolation>;
55
+
56
+ /**
57
+ * Gets any errors that occurred while attempting to commit.
58
+ * @returns A thin filter over {@link getAppendResults}.
59
+ */
60
+ getAppendErrors(): ReadonlyArray<AppendError>;
61
+
41
62
  /**
42
63
  * Commits all buffered events.
43
64
  */