@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
@@ -0,0 +1,588 @@
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 { ChronicleConnection } from '../connection';
6
+ import type { IUnitOfWorkManager } from '../transactions/IUnitOfWorkManager';
7
+ import { eventType } from '../events/eventTypeDecorator';
8
+ import { CompleteStreamError } from './CompleteStreamError';
9
+ import { EventSequence } from './EventSequence';
10
+ import { EventSequenceId } from './EventSequenceId';
11
+ import { EventSequenceNumber } from './EventSequenceNumber';
12
+
13
+ class SomethingHappened {
14
+ constructor(readonly value: string = '') {}
15
+ }
16
+ eventType('a3f6a2f0-6f2f-4a3c-9d3f-6f2f4a3c9d3f')(SomethingHappened);
17
+
18
+ class SomethingElseHappened {
19
+ constructor(readonly value: string = '') {}
20
+ }
21
+ eventType('b3f6a2f0-6f2f-4a3c-9d3f-6f2f4a3c9d3g')(SomethingElseHappened);
22
+
23
+ function createEventSequence(
24
+ overrides: Record<string, ReturnType<typeof vi.fn>> = {},
25
+ observerOverrides: Record<string, ReturnType<typeof vi.fn>> = {}
26
+ ) {
27
+ const eventSequences = {
28
+ redact: vi.fn().mockResolvedValue({}),
29
+ redactForEventSource: vi.fn().mockResolvedValue({}),
30
+ getForEventSourceIdAndEventTypes: vi.fn().mockResolvedValue({ Events: [] }),
31
+ getEventsFromEventSequenceNumber: vi.fn().mockResolvedValue({ Events: [] }),
32
+ getTailSequenceNumber: vi.fn().mockResolvedValue({ SequenceNumber: EventSequenceNumber.unset.value }),
33
+ completeStream: vi.fn().mockResolvedValue({ IsSuccess: true, SequenceNumber: 3n, Error: 0 }),
34
+ appendMany: vi.fn().mockResolvedValue({ SequenceNumbers: [], ConstraintViolations: [], Errors: [] }),
35
+ append: vi.fn().mockResolvedValue({ SequenceNumber: 0n, ConstraintViolations: [], Errors: [] }),
36
+ ...overrides
37
+ };
38
+ const observers = {
39
+ waitForCompletion: vi.fn().mockResolvedValue({ IsSuccess: true, FailedPartitions: [] }),
40
+ ...observerOverrides
41
+ };
42
+ const connection = { eventSequences, observers } as unknown as ChronicleConnection;
43
+ const unitOfWorkManager = {} as IUnitOfWorkManager;
44
+
45
+ const eventSequence = new EventSequence(
46
+ EventSequenceId.eventLog,
47
+ 'my-event-store',
48
+ 'my-namespace',
49
+ connection,
50
+ unitOfWorkManager);
51
+
52
+ return {
53
+ eventSequence,
54
+ redact: eventSequences.redact,
55
+ redactForEventSource: eventSequences.redactForEventSource,
56
+ getForEventSourceIdAndEventTypes: eventSequences.getForEventSourceIdAndEventTypes,
57
+ getEventsFromEventSequenceNumber: eventSequences.getEventsFromEventSequenceNumber,
58
+ getTailSequenceNumber: eventSequences.getTailSequenceNumber,
59
+ completeStream: eventSequences.completeStream,
60
+ appendMany: eventSequences.appendMany,
61
+ append: eventSequences.append,
62
+ waitForCompletion: observers.waitForCompletion
63
+ };
64
+ }
65
+
66
+ /** Reads exactly one value from an async iterable, then stops iterating it. */
67
+ async function takeOne<T>(iterable: AsyncIterable<T>): Promise<T> {
68
+ const iterator = iterable[Symbol.asyncIterator]();
69
+ try {
70
+ const { value } = await iterator.next();
71
+ return value as T;
72
+ } finally {
73
+ await iterator.return?.();
74
+ }
75
+ }
76
+
77
+ function wireAppendedEvent() {
78
+ return {
79
+ Context: {
80
+ EventType: { Id: 'a3f6a2f0-6f2f-4a3c-9d3f-6f2f4a3c9d3f', Generation: 1, Tombstone: false },
81
+ EventSourceType: 'Default',
82
+ EventSourceId: 'some-event-source',
83
+ SequenceNumber: 7n,
84
+ EventStreamType: 'Default',
85
+ EventStreamId: 'some-event-source',
86
+ Occurred: { Value: '2024-01-15T12:30:00.0000000+00:00' },
87
+ EventStore: 'my-event-store',
88
+ Namespace: 'my-namespace',
89
+ CorrelationId: undefined,
90
+ Causation: [{ Occurred: { Value: '2024-01-15T12:30:00.0000000+00:00' }, Type: 'SomeCausation', Properties: { key: 'value' } }],
91
+ CausedBy: undefined,
92
+ ObservationState: 0,
93
+ Tags: [],
94
+ Hash: ''
95
+ },
96
+ Content: JSON.stringify({ value: 'hello' }),
97
+ OriginalContent: JSON.stringify({ value: 'hello' }),
98
+ Revisions: [],
99
+ GenerationalContent: {}
100
+ };
101
+ }
102
+
103
+ describe('EventSequence', () => {
104
+ describe('when redacting a single event', () => {
105
+ const { eventSequence, redact } = createEventSequence();
106
+ const sequenceNumber = new EventSequenceNumber(42n);
107
+
108
+ it('should call the Redact RPC with the correct payload', async () => {
109
+ await eventSequence.redact(sequenceNumber, 'GDPR erasure request');
110
+
111
+ expect(redact).toHaveBeenCalledTimes(1);
112
+ const request = redact.mock.calls[0][0];
113
+ expect(request.EventStore).toEqual('my-event-store');
114
+ expect(request.Namespace).toEqual('my-namespace');
115
+ expect(request.EventSequenceId).toEqual(EventSequenceId.eventLog.value);
116
+ expect(request.SequenceNumber).toEqual(42n);
117
+ expect(request.Reason).toEqual('GDPR erasure request');
118
+ });
119
+ });
120
+
121
+ describe('when redacting all events for an event source without event type filters', () => {
122
+ const { eventSequence, redactForEventSource } = createEventSequence();
123
+
124
+ it('should call the RedactForEventSource RPC with an empty event type filter', async () => {
125
+ await eventSequence.redactForEventSource('some-event-source', 'GDPR erasure request');
126
+
127
+ expect(redactForEventSource).toHaveBeenCalledTimes(1);
128
+ const request = redactForEventSource.mock.calls[0][0];
129
+ expect(request.EventStore).toEqual('my-event-store');
130
+ expect(request.Namespace).toEqual('my-namespace');
131
+ expect(request.EventSequenceId).toEqual(EventSequenceId.eventLog.value);
132
+ expect(request.EventSourceId).toEqual('some-event-source');
133
+ expect(request.Reason).toEqual('GDPR erasure request');
134
+ expect(request.EventTypes).toEqual([]);
135
+ });
136
+ });
137
+
138
+ describe('when redacting all events for an event source filtered to specific event types', () => {
139
+ const { eventSequence, redactForEventSource } = createEventSequence();
140
+
141
+ it('should call the RedactForEventSource RPC with the resolved event type filter', async () => {
142
+ await eventSequence.redactForEventSource('some-event-source', 'GDPR erasure request', [SomethingHappened, SomethingElseHappened]);
143
+
144
+ expect(redactForEventSource).toHaveBeenCalledTimes(1);
145
+ const request = redactForEventSource.mock.calls[0][0];
146
+ expect(request.EventTypes).toEqual([
147
+ { Id: 'a3f6a2f0-6f2f-4a3c-9d3f-6f2f4a3c9d3f', Generation: 1, Tombstone: false },
148
+ { Id: 'b3f6a2f0-6f2f-4a3c-9d3f-6f2f4a3c9d3g', Generation: 1, Tombstone: false }
149
+ ]);
150
+ });
151
+ });
152
+
153
+ describe('when getting events for an event source and event types', () => {
154
+ const { eventSequence, getForEventSourceIdAndEventTypes } = createEventSequence({
155
+ getForEventSourceIdAndEventTypes: vi.fn().mockResolvedValue({ Events: [wireAppendedEvent()] })
156
+ });
157
+
158
+ it('should call the RPC with the resolved event type filter and map the response', async () => {
159
+ const result = await eventSequence.getForEventSourceIdAndEventTypes('some-event-source', [SomethingHappened]);
160
+
161
+ expect(getForEventSourceIdAndEventTypes).toHaveBeenCalledTimes(1);
162
+ const request = getForEventSourceIdAndEventTypes.mock.calls[0][0];
163
+ expect(request.EventSourceId).toEqual('some-event-source');
164
+ expect(request.EventTypes).toEqual([{ Id: 'a3f6a2f0-6f2f-4a3c-9d3f-6f2f4a3c9d3f', Generation: 1, Tombstone: false }]);
165
+
166
+ expect(result).toHaveLength(1);
167
+ expect(result[0].context.sequenceNumber).toEqual(7n);
168
+ expect(result[0].context.eventSourceId).toEqual('some-event-source');
169
+ expect(result[0].context.causation).toEqual([{ type: 'SomeCausation', properties: { key: 'value' } }]);
170
+ expect(result[0].eventType.id.value).toEqual('a3f6a2f0-6f2f-4a3c-9d3f-6f2f4a3c9d3f');
171
+ expect(result[0].content).toEqual({ value: 'hello' });
172
+ });
173
+ });
174
+
175
+ describe('when getting events from a sequence number', () => {
176
+ const { eventSequence, getEventsFromEventSequenceNumber } = createEventSequence({
177
+ getEventsFromEventSequenceNumber: vi.fn().mockResolvedValue({ Events: [wireAppendedEvent()] })
178
+ });
179
+
180
+ it('should call the RPC starting from the given sequence number and map the response', async () => {
181
+ const result = await eventSequence.getFromSequenceNumber(new EventSequenceNumber(7n), 'some-event-source', [SomethingHappened]);
182
+
183
+ expect(getEventsFromEventSequenceNumber).toHaveBeenCalledTimes(1);
184
+ const request = getEventsFromEventSequenceNumber.mock.calls[0][0];
185
+ expect(request.FromEventSequenceNumber).toEqual(7n);
186
+ expect(request.EventSourceId).toEqual('some-event-source');
187
+ expect(request.EventTypes).toEqual([{ Id: 'a3f6a2f0-6f2f-4a3c-9d3f-6f2f4a3c9d3f', Generation: 1, Tombstone: false }]);
188
+
189
+ expect(result).toHaveLength(1);
190
+ expect(result[0].content).toEqual({ value: 'hello' });
191
+ });
192
+ });
193
+
194
+ describe('when getting the next sequence number and the sequence is empty', () => {
195
+ const { eventSequence, getTailSequenceNumber } = createEventSequence({
196
+ getTailSequenceNumber: vi.fn().mockResolvedValue({ SequenceNumber: EventSequenceNumber.unset.value })
197
+ });
198
+
199
+ it('should return the first sequence number', async () => {
200
+ const result = await eventSequence.getNextSequenceNumber();
201
+
202
+ expect(getTailSequenceNumber).toHaveBeenCalledTimes(1);
203
+ expect(result.value).toEqual(EventSequenceNumber.first.value);
204
+ });
205
+ });
206
+
207
+ describe('when getting the next sequence number and events already exist', () => {
208
+ const { eventSequence } = createEventSequence({
209
+ getTailSequenceNumber: vi.fn().mockResolvedValue({ SequenceNumber: 41n })
210
+ });
211
+
212
+ it('should return one past the tail sequence number', async () => {
213
+ const result = await eventSequence.getNextSequenceNumber();
214
+
215
+ expect(result.value).toEqual(42n);
216
+ });
217
+ });
218
+
219
+ describe('when getting the tail sequence number for an observer type', () => {
220
+ class SomeReactor {
221
+ async somethingHappened(): Promise<void> {}
222
+ }
223
+
224
+ const { eventSequence, getTailSequenceNumber } = createEventSequence({
225
+ getTailSequenceNumber: vi.fn().mockResolvedValue({ SequenceNumber: 5n })
226
+ });
227
+
228
+ it('should filter the tail sequence number lookup to the event types the observer handles', async () => {
229
+ await eventSequence.getTailSequenceNumberForObserver(SomeReactor);
230
+
231
+ expect(getTailSequenceNumber).toHaveBeenCalledTimes(1);
232
+ const request = getTailSequenceNumber.mock.calls[0][0];
233
+ expect(request.EventTypes).toContainEqual({ Id: 'a3f6a2f0-6f2f-4a3c-9d3f-6f2f4a3c9d3f', Generation: 1, Tombstone: false });
234
+ expect(request.EventTypes).not.toContainEqual({ Id: 'b3f6a2f0-6f2f-4a3c-9d3f-6f2f4a3c9d3g', Generation: 1, Tombstone: false });
235
+ });
236
+ });
237
+
238
+ describe('when completing a non-default stream successfully', () => {
239
+ const { eventSequence, completeStream } = createEventSequence({
240
+ completeStream: vi.fn().mockResolvedValue({ IsSuccess: true, SequenceNumber: 9n, Error: 0 })
241
+ });
242
+
243
+ it('should call the RPC and return the tail sequence number', async () => {
244
+ const result = await eventSequence.completeStream('my-stream-type', 'my-stream-id');
245
+
246
+ expect(completeStream).toHaveBeenCalledTimes(1);
247
+ const request = completeStream.mock.calls[0][0];
248
+ expect(request.EventStreamType).toEqual('my-stream-type');
249
+ expect(request.EventStreamId).toEqual('my-stream-id');
250
+
251
+ expect(result.isSuccess).toBe(true);
252
+ if (result.isSuccess) {
253
+ expect(result.sequenceNumber.value).toEqual(9n);
254
+ }
255
+ });
256
+ });
257
+
258
+ describe('when completing the default stream', () => {
259
+ const { eventSequence } = createEventSequence({
260
+ completeStream: vi.fn().mockResolvedValue({ IsSuccess: false, SequenceNumber: 0n, Error: 1 })
261
+ });
262
+
263
+ it('should return the DefaultStreamCannotBeCompleted error', async () => {
264
+ const result = await eventSequence.completeStream('Default', '');
265
+
266
+ expect(result.isSuccess).toBe(false);
267
+ if (!result.isSuccess) {
268
+ expect(result.error).toEqual(CompleteStreamError.DefaultStreamCannotBeCompleted);
269
+ }
270
+ });
271
+ });
272
+
273
+ describe('when completing an already-completed stream', () => {
274
+ const { eventSequence } = createEventSequence({
275
+ completeStream: vi.fn().mockResolvedValue({ IsSuccess: false, SequenceNumber: 0n, Error: 0 })
276
+ });
277
+
278
+ it('should return the AlreadyCompleted error', async () => {
279
+ const result = await eventSequence.completeStream('my-stream-type', 'my-stream-id');
280
+
281
+ expect(result.isSuccess).toBe(false);
282
+ if (!result.isSuccess) {
283
+ expect(result.error).toEqual(CompleteStreamError.AlreadyCompleted);
284
+ }
285
+ });
286
+ });
287
+
288
+ describe('when appending many events for distinct event sources with a shared concurrency scope option', () => {
289
+ const { eventSequence, appendMany } = createEventSequence();
290
+
291
+ it('should apply the same concurrency scope to every distinct event source id', async () => {
292
+ await eventSequence.appendMany(
293
+ [
294
+ { eventSourceId: 'source-1', event: new SomethingHappened('a') },
295
+ { eventSourceId: 'source-2', event: new SomethingHappened('b') }
296
+ ],
297
+ { concurrencyScope: { sequenceNumber: 5n } });
298
+
299
+ expect(appendMany).toHaveBeenCalledTimes(1);
300
+ const request = appendMany.mock.calls[0][0];
301
+ expect(request.ConcurrencyScopes['source-1'].SequenceNumber).toEqual(5n);
302
+ expect(request.ConcurrencyScopes['source-2'].SequenceNumber).toEqual(5n);
303
+ });
304
+ });
305
+
306
+ describe('when appending many events for distinct event sources with a per-event-source-id concurrency scope map', () => {
307
+ const { eventSequence, appendMany } = createEventSequence();
308
+
309
+ it('should apply the distinct concurrency scope for each event source id', async () => {
310
+ await eventSequence.appendMany(
311
+ [
312
+ { eventSourceId: 'source-1', event: new SomethingHappened('a') },
313
+ { eventSourceId: 'source-2', event: new SomethingHappened('b') }
314
+ ],
315
+ {
316
+ concurrencyScopes: {
317
+ 'source-1': { sequenceNumber: 5n },
318
+ 'source-2': { sequenceNumber: 9n }
319
+ }
320
+ });
321
+
322
+ expect(appendMany).toHaveBeenCalledTimes(1);
323
+ const request = appendMany.mock.calls[0][0];
324
+ expect(request.ConcurrencyScopes['source-1'].SequenceNumber).toEqual(5n);
325
+ expect(request.ConcurrencyScopes['source-2'].SequenceNumber).toEqual(9n);
326
+ });
327
+ });
328
+
329
+ describe('when appending many events with a per-event-source-id map that only covers some sources', () => {
330
+ const { eventSequence, appendMany } = createEventSequence();
331
+
332
+ it('should fall back to the shared concurrency scope for sources without a map entry', async () => {
333
+ await eventSequence.appendMany(
334
+ [
335
+ { eventSourceId: 'source-1', event: new SomethingHappened('a') },
336
+ { eventSourceId: 'source-2', event: new SomethingHappened('b') }
337
+ ],
338
+ {
339
+ concurrencyScope: { sequenceNumber: 1n },
340
+ concurrencyScopes: {
341
+ 'source-1': { sequenceNumber: 5n }
342
+ }
343
+ });
344
+
345
+ expect(appendMany).toHaveBeenCalledTimes(1);
346
+ const request = appendMany.mock.calls[0][0];
347
+ expect(request.ConcurrencyScopes['source-1'].SequenceNumber).toEqual(5n);
348
+ expect(request.ConcurrencyScopes['source-2'].SequenceNumber).toEqual(1n);
349
+ });
350
+ });
351
+
352
+ describe('when an EventForEventSourceId specifies its own stream targeting', () => {
353
+ const { eventSequence, appendMany } = createEventSequence();
354
+
355
+ it('should use the wrapper\'s own event stream type, id, source type, and subject', async () => {
356
+ await eventSequence.appendMany([
357
+ {
358
+ eventSourceId: 'source-1',
359
+ event: new SomethingHappened('a'),
360
+ eventStreamType: 'custom-stream-type',
361
+ eventStreamId: 'custom-stream-id',
362
+ eventSourceType: 'custom-source-type',
363
+ subject: 'custom-subject'
364
+ }
365
+ ]);
366
+
367
+ expect(appendMany).toHaveBeenCalledTimes(1);
368
+ const request = appendMany.mock.calls[0][0];
369
+ const [event] = request.Events;
370
+ expect(event.EventStreamType).toEqual('custom-stream-type');
371
+ expect(event.EventStreamId).toEqual('custom-stream-id');
372
+ expect(event.EventSourceType).toEqual('custom-source-type');
373
+ expect(event.Subject).toEqual('custom-subject');
374
+ });
375
+ });
376
+
377
+ describe('when an EventForEventSourceId does not specify stream targeting', () => {
378
+ const { eventSequence, appendMany } = createEventSequence();
379
+
380
+ it('should default to the default stream type, the event source id as stream id, and the event source id as subject', async () => {
381
+ await eventSequence.appendMany([
382
+ { eventSourceId: 'source-1', event: new SomethingHappened('a') }
383
+ ]);
384
+
385
+ expect(appendMany).toHaveBeenCalledTimes(1);
386
+ const request = appendMany.mock.calls[0][0];
387
+ const [event] = request.Events;
388
+ expect(event.EventStreamType).toEqual('Default');
389
+ expect(event.EventStreamId).toEqual('source-1');
390
+ expect(event.EventSourceType).toEqual('Default');
391
+ expect(event.Subject).toEqual('source-1');
392
+ });
393
+ });
394
+
395
+ describe('when subscribing to appendOperations and appending a single event', () => {
396
+ const { eventSequence } = createEventSequence({
397
+ append: vi.fn().mockResolvedValue({ SequenceNumber: 42n, ConstraintViolations: [], Errors: [] })
398
+ });
399
+
400
+ it('should publish the appended event and its result to the subscriber', async () => {
401
+ const operations = takeOne(eventSequence.appendOperations);
402
+
403
+ // Give the subscriber a chance to attach its iterator before the append fires,
404
+ // since appendOperations is a hot stream with no replay buffer.
405
+ await new Promise(resolve => setTimeout(resolve, 0));
406
+ await eventSequence.append('some-event-source', new SomethingHappened('a'));
407
+
408
+ const published = await operations;
409
+ expect(published).toHaveLength(1);
410
+ expect(published[0].event.context.sequenceNumber).toEqual(42n);
411
+ expect(published[0].event.context.eventSourceId).toEqual('some-event-source');
412
+ expect(published[0].result.sequenceNumber.value).toEqual(42n);
413
+ expect(published[0].result.isSuccess).toBe(true);
414
+ });
415
+ });
416
+
417
+ describe('when subscribing to appendOperations and appending many events', () => {
418
+ const { eventSequence } = createEventSequence({
419
+ appendMany: vi.fn().mockResolvedValue({ SequenceNumbers: [10n, 11n], ConstraintViolations: [], Errors: [] })
420
+ });
421
+
422
+ it('should publish the full batch to the subscriber', async () => {
423
+ const operations = takeOne(eventSequence.appendOperations);
424
+
425
+ await new Promise(resolve => setTimeout(resolve, 0));
426
+ await eventSequence.appendMany([
427
+ { eventSourceId: 'source-1', event: new SomethingHappened('a') },
428
+ { eventSourceId: 'source-2', event: new SomethingElseHappened('b') }
429
+ ]);
430
+
431
+ const published = await operations;
432
+ expect(published).toHaveLength(2);
433
+ expect(published[0].event.context.eventSourceId).toEqual('source-1');
434
+ expect(published[0].result.sequenceNumber.value).toEqual(10n);
435
+ expect(published[1].event.context.eventSourceId).toEqual('source-2');
436
+ expect(published[1].result.sequenceNumber.value).toEqual(11n);
437
+ });
438
+ });
439
+
440
+ describe('when two subscribers are iterating appendOperations at the same time', () => {
441
+ const { eventSequence } = createEventSequence({
442
+ append: vi.fn().mockResolvedValue({ SequenceNumber: 1n, ConstraintViolations: [], Errors: [] })
443
+ });
444
+
445
+ it('should multicast the same append to every subscriber', async () => {
446
+ const first = takeOne(eventSequence.appendOperations);
447
+ const second = takeOne(eventSequence.appendOperations);
448
+
449
+ await new Promise(resolve => setTimeout(resolve, 0));
450
+ await eventSequence.append('some-event-source', new SomethingHappened('a'));
451
+
452
+ const [firstResult, secondResult] = await Promise.all([first, second]);
453
+ expect(firstResult[0].result.sequenceNumber.value).toEqual(1n);
454
+ expect(secondResult[0].result.sequenceNumber.value).toEqual(1n);
455
+ });
456
+ });
457
+
458
+ describe('when nobody is subscribed to appendOperations', () => {
459
+ const { eventSequence } = createEventSequence({
460
+ append: vi.fn().mockResolvedValue({ SequenceNumber: 1n, ConstraintViolations: [], Errors: [] })
461
+ });
462
+
463
+ it('should still complete the append normally', async () => {
464
+ const result = await eventSequence.append('some-event-source', new SomethingHappened('a'));
465
+
466
+ expect(result.isSuccess).toBe(true);
467
+ expect(eventSequence.appendOperations.hasSubscribers).toBe(false);
468
+ });
469
+ });
470
+
471
+ describe('when waiting for completion after a successful append', () => {
472
+ const { eventSequence, waitForCompletion } = createEventSequence(
473
+ { append: vi.fn().mockResolvedValue({ SequenceNumber: 42n, ConstraintViolations: [], Errors: [] }) },
474
+ { waitForCompletion: vi.fn().mockResolvedValue({ IsSuccess: true, FailedPartitions: [] }) });
475
+
476
+ it('should call the WaitForCompletion RPC with the appended tail sequence number', async () => {
477
+ const appendResult = await eventSequence.append('some-event-source', new SomethingHappened('a'));
478
+ const result = await appendResult.waitForCompletion();
479
+
480
+ expect(waitForCompletion).toHaveBeenCalledTimes(1);
481
+ const request = waitForCompletion.mock.calls[0][0];
482
+ expect(request.EventStore).toEqual('my-event-store');
483
+ expect(request.Namespace).toEqual('my-namespace');
484
+ expect(request.EventSequenceId).toEqual(EventSequenceId.eventLog.value);
485
+ expect(request.TailEventSequenceNumber).toEqual(42n);
486
+ expect(result.isSuccess).toBe(true);
487
+ expect(result.failedPartitions).toEqual([]);
488
+ });
489
+
490
+ it('should pass the timeout as an abort signal', async () => {
491
+ const appendResult = await eventSequence.append('some-event-source', new SomethingHappened('a'));
492
+ await appendResult.waitForCompletion(1234);
493
+
494
+ const options = waitForCompletion.mock.calls[0][1];
495
+ expect(options.signal).toBeInstanceOf(AbortSignal);
496
+ });
497
+ });
498
+
499
+ describe('when waiting for completion and observers report a failed partition', () => {
500
+ const { eventSequence } = createEventSequence(
501
+ { append: vi.fn().mockResolvedValue({ SequenceNumber: 42n, ConstraintViolations: [], Errors: [] }) },
502
+ {
503
+ waitForCompletion: vi.fn().mockResolvedValue({
504
+ IsSuccess: false,
505
+ FailedPartitions: [{
506
+ Id: { lo: 1n, hi: 0n },
507
+ ObserverId: 'some-observer',
508
+ Partition: 'some-partition',
509
+ Attempts: []
510
+ }]
511
+ })
512
+ });
513
+
514
+ it('should report failure with the failed partitions', async () => {
515
+ const appendResult = await eventSequence.append('some-event-source', new SomethingHappened('a'));
516
+ const result = await appendResult.waitForCompletion();
517
+
518
+ expect(result.isSuccess).toBe(false);
519
+ expect(result.failedPartitions).toHaveLength(1);
520
+ expect(result.failedPartitions[0].observerId).toEqual('some-observer');
521
+ expect(result.failedPartitions[0].partition).toEqual('some-partition');
522
+ });
523
+ });
524
+
525
+ describe('when waiting for completion after an append that itself failed', () => {
526
+ const { eventSequence, waitForCompletion } = createEventSequence({
527
+ append: vi.fn().mockResolvedValue({
528
+ SequenceNumber: 0n,
529
+ ConstraintViolations: [{ ConstraintId: 'unique', Message: 'Value must be unique', Details: {} }],
530
+ Errors: []
531
+ })
532
+ });
533
+
534
+ it('should not call the RPC and resolve immediately as successful', async () => {
535
+ const appendResult = await eventSequence.append('some-event-source', new SomethingHappened('a'));
536
+ const result = await appendResult.waitForCompletion();
537
+
538
+ expect(waitForCompletion).not.toHaveBeenCalled();
539
+ expect(result.isSuccess).toBe(true);
540
+ expect(result.failedPartitions).toEqual([]);
541
+ });
542
+ });
543
+
544
+ describe('when a single append fails with a concurrency violation', () => {
545
+ const { eventSequence } = createEventSequence({
546
+ append: vi.fn().mockResolvedValue({
547
+ SequenceNumber: 18446744073709551615n,
548
+ ConstraintViolations: [],
549
+ Errors: [],
550
+ ConcurrencyViolation: { EventSourceId: 'some-event-source', ExpectedSequenceNumber: 1n, ActualSequenceNumber: 2n }
551
+ })
552
+ });
553
+
554
+ it('should map the concurrency violation and report the append as unsuccessful', async () => {
555
+ const result = await eventSequence.append('some-event-source', new SomethingHappened('a'));
556
+
557
+ expect(result.isSuccess).toBe(false);
558
+ expect(result.concurrencyViolation).toEqual({
559
+ eventSourceId: 'some-event-source',
560
+ expectedSequenceNumber: new EventSequenceNumber(1n),
561
+ actualSequenceNumber: new EventSequenceNumber(2n)
562
+ });
563
+ });
564
+ });
565
+
566
+ describe('when appendMany fails with a concurrency violation', () => {
567
+ const { eventSequence } = createEventSequence({
568
+ appendMany: vi.fn().mockResolvedValue({
569
+ SequenceNumbers: [18446744073709551615n],
570
+ ConstraintViolations: [],
571
+ Errors: [],
572
+ ConcurrencyViolations: [{ EventSourceId: 'some-event-source', ExpectedSequenceNumber: 1n, ActualSequenceNumber: 2n }]
573
+ })
574
+ });
575
+
576
+ it('should map the first concurrency violation onto every per-event result', async () => {
577
+ const results = await eventSequence.appendMany('some-event-source', [new SomethingHappened('a')]);
578
+
579
+ expect(results).toHaveLength(1);
580
+ expect(results[0].isSuccess).toBe(false);
581
+ expect(results[0].concurrencyViolation).toEqual({
582
+ eventSourceId: 'some-event-source',
583
+ expectedSequenceNumber: new EventSequenceNumber(1n),
584
+ actualSequenceNumber: new EventSequenceNumber(2n)
585
+ });
586
+ });
587
+ });
588
+ });