@cratis/chronicle 6.47.0 → 6.49.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 (260) hide show
  1. package/ChronicleClient.ts +3 -0
  2. package/EventStore.ts +10 -3
  3. package/IEventStore.ts +8 -0
  4. package/artifacts/DefaultClientArtifactsProvider.ts +5 -0
  5. package/artifacts/IClientArtifactsProvider.ts +3 -0
  6. package/connection/ChronicleConnection.ts +6 -0
  7. package/connection/ChronicleServices.ts +2 -0
  8. package/connection/ConnectionLifecycle.ts +22 -0
  9. package/dist/ChronicleClient.d.ts.map +1 -1
  10. package/dist/ChronicleClient.js +3 -0
  11. package/dist/ChronicleClient.js.map +1 -1
  12. package/dist/EventStore.d.ts +2 -0
  13. package/dist/EventStore.d.ts.map +1 -1
  14. package/dist/EventStore.js +9 -3
  15. package/dist/EventStore.js.map +1 -1
  16. package/dist/IEventStore.d.ts +7 -0
  17. package/dist/IEventStore.d.ts.map +1 -1
  18. package/dist/artifacts/DefaultClientArtifactsProvider.d.ts +2 -0
  19. package/dist/artifacts/DefaultClientArtifactsProvider.d.ts.map +1 -1
  20. package/dist/artifacts/DefaultClientArtifactsProvider.js +4 -0
  21. package/dist/artifacts/DefaultClientArtifactsProvider.js.map +1 -1
  22. package/dist/artifacts/IClientArtifactsProvider.d.ts +2 -0
  23. package/dist/artifacts/IClientArtifactsProvider.d.ts.map +1 -1
  24. package/dist/connection/ChronicleConnection.d.ts +1 -0
  25. package/dist/connection/ChronicleConnection.d.ts.map +1 -1
  26. package/dist/connection/ChronicleConnection.js +5 -1
  27. package/dist/connection/ChronicleConnection.js.map +1 -1
  28. package/dist/connection/ChronicleServices.d.ts +2 -1
  29. package/dist/connection/ChronicleServices.d.ts.map +1 -1
  30. package/dist/connection/ConnectionLifecycle.d.ts +8 -0
  31. package/dist/connection/ConnectionLifecycle.d.ts.map +1 -1
  32. package/dist/connection/ConnectionLifecycle.js +21 -0
  33. package/dist/connection/ConnectionLifecycle.js.map +1 -1
  34. package/dist/eventSequences/AppendOptions.d.ts +12 -0
  35. package/dist/eventSequences/AppendOptions.d.ts.map +1 -1
  36. package/dist/eventSequences/EventForEventSourceId.d.ts +11 -0
  37. package/dist/eventSequences/EventForEventSourceId.d.ts.map +1 -1
  38. package/dist/eventSequences/EventLog.d.ts +2 -1
  39. package/dist/eventSequences/EventLog.d.ts.map +1 -1
  40. package/dist/eventSequences/EventLog.js +2 -2
  41. package/dist/eventSequences/EventLog.js.map +1 -1
  42. package/dist/eventSequences/EventSequence.d.ts +4 -1
  43. package/dist/eventSequences/EventSequence.d.ts.map +1 -1
  44. package/dist/eventSequences/EventSequence.js +32 -6
  45. package/dist/eventSequences/EventSequence.js.map +1 -1
  46. package/dist/eventSequences/ITransactionalEventSequence.d.ts +5 -2
  47. package/dist/eventSequences/ITransactionalEventSequence.d.ts.map +1 -1
  48. package/dist/eventSequences/TransactionalEventSequence.d.ts +3 -2
  49. package/dist/eventSequences/TransactionalEventSequence.d.ts.map +1 -1
  50. package/dist/eventSequences/TransactionalEventSequence.js +4 -4
  51. package/dist/eventSequences/TransactionalEventSequence.js.map +1 -1
  52. package/dist/eventSequences/resolveEventSourceRouting.d.ts +39 -0
  53. package/dist/eventSequences/resolveEventSourceRouting.d.ts.map +1 -0
  54. package/dist/eventSequences/resolveEventSourceRouting.js +92 -0
  55. package/dist/eventSequences/resolveEventSourceRouting.js.map +1 -0
  56. package/dist/eventSources/ConcurrencyDimensions.d.ts +19 -0
  57. package/dist/eventSources/ConcurrencyDimensions.d.ts.map +1 -0
  58. package/dist/eventSources/ConcurrencyDimensions.js +19 -0
  59. package/dist/eventSources/ConcurrencyDimensions.js.map +1 -0
  60. package/dist/eventSources/DuplicateEventSourceName.d.ts +6 -0
  61. package/dist/eventSources/DuplicateEventSourceName.d.ts.map +1 -0
  62. package/dist/eventSources/DuplicateEventSourceName.js +12 -0
  63. package/dist/eventSources/DuplicateEventSourceName.js.map +1 -0
  64. package/dist/eventSources/DuplicateEventStreamName.d.ts +7 -0
  65. package/dist/eventSources/DuplicateEventStreamName.d.ts.map +1 -0
  66. package/dist/eventSources/DuplicateEventStreamName.js +14 -0
  67. package/dist/eventSources/DuplicateEventStreamName.js.map +1 -0
  68. package/dist/eventSources/EventRoutingContradictsEventSource.d.ts +9 -0
  69. package/dist/eventSources/EventRoutingContradictsEventSource.d.ts.map +1 -0
  70. package/dist/eventSources/EventRoutingContradictsEventSource.js +18 -0
  71. package/dist/eventSources/EventRoutingContradictsEventSource.js.map +1 -0
  72. package/dist/eventSources/EventSourceDefinition.d.ts +20 -0
  73. package/dist/eventSources/EventSourceDefinition.d.ts.map +1 -0
  74. package/dist/eventSources/EventSourceDefinition.js +4 -0
  75. package/dist/eventSources/EventSourceDefinition.js.map +1 -0
  76. package/dist/eventSources/EventSources.d.ts +31 -0
  77. package/dist/eventSources/EventSources.d.ts.map +1 -0
  78. package/dist/eventSources/EventSources.js +90 -0
  79. package/dist/eventSources/EventSources.js.map +1 -0
  80. package/dist/eventSources/EventStreamDefinition.d.ts +13 -0
  81. package/dist/eventSources/EventStreamDefinition.d.ts.map +1 -0
  82. package/dist/eventSources/EventStreamDefinition.js +4 -0
  83. package/dist/eventSources/EventStreamDefinition.js.map +1 -0
  84. package/dist/eventSources/EventStreamDoesNotBelongToEventSource.d.ts +7 -0
  85. package/dist/eventSources/EventStreamDoesNotBelongToEventSource.d.ts.map +1 -0
  86. package/dist/eventSources/EventStreamDoesNotBelongToEventSource.js +14 -0
  87. package/dist/eventSources/EventStreamDoesNotBelongToEventSource.js.map +1 -0
  88. package/dist/eventSources/EventStreamRequiresEventSource.d.ts +6 -0
  89. package/dist/eventSources/EventStreamRequiresEventSource.d.ts.map +1 -0
  90. package/dist/eventSources/EventStreamRequiresEventSource.js +12 -0
  91. package/dist/eventSources/EventStreamRequiresEventSource.js.map +1 -0
  92. package/dist/eventSources/IEventSources.d.ts +27 -0
  93. package/dist/eventSources/IEventSources.d.ts.map +1 -0
  94. package/dist/eventSources/IEventSources.js +4 -0
  95. package/dist/eventSources/IEventSources.js.map +1 -0
  96. package/dist/eventSources/ResolvedEventRouting.d.ts +37 -0
  97. package/dist/eventSources/ResolvedEventRouting.d.ts.map +1 -0
  98. package/dist/eventSources/ResolvedEventRouting.js +60 -0
  99. package/dist/eventSources/ResolvedEventRouting.js.map +1 -0
  100. package/dist/eventSources/UnknownEventSource.d.ts +6 -0
  101. package/dist/eventSources/UnknownEventSource.d.ts.map +1 -0
  102. package/dist/eventSources/UnknownEventSource.js +12 -0
  103. package/dist/eventSources/UnknownEventSource.js.map +1 -0
  104. package/dist/eventSources/eventSource.d.ts +60 -0
  105. package/dist/eventSources/eventSource.d.ts.map +1 -0
  106. package/dist/eventSources/eventSource.js +72 -0
  107. package/dist/eventSources/eventSource.js.map +1 -0
  108. package/dist/eventSources/index.d.ts +17 -0
  109. package/dist/eventSources/index.d.ts.map +1 -0
  110. package/dist/eventSources/index.js +13 -0
  111. package/dist/eventSources/index.js.map +1 -0
  112. package/dist/events/EventContext.d.ts +5 -0
  113. package/dist/events/EventContext.d.ts.map +1 -1
  114. package/dist/events/WireEventContext.d.ts +2 -0
  115. package/dist/events/WireEventContext.d.ts.map +1 -1
  116. package/dist/events/toClientEventContext.d.ts.map +1 -1
  117. package/dist/events/toClientEventContext.js +1 -0
  118. package/dist/events/toClientEventContext.js.map +1 -1
  119. package/dist/index.d.ts +3 -1
  120. package/dist/index.d.ts.map +1 -1
  121. package/dist/index.js +3 -1
  122. package/dist/index.js.map +1 -1
  123. package/dist/projections/eventContextPropertyExpression.d.ts.map +1 -1
  124. package/dist/projections/eventContextPropertyExpression.js +1 -0
  125. package/dist/projections/eventContextPropertyExpression.js.map +1 -1
  126. package/dist/readModels/IReadModelWatcher.d.ts +30 -0
  127. package/dist/readModels/IReadModelWatcher.d.ts.map +1 -0
  128. package/dist/readModels/IReadModelWatcher.js +4 -0
  129. package/dist/readModels/IReadModelWatcher.js.map +1 -0
  130. package/dist/readModels/IReadModels.d.ts +16 -2
  131. package/dist/readModels/IReadModels.d.ts.map +1 -1
  132. package/dist/readModels/ReadModelChangeContext.d.ts +9 -0
  133. package/dist/readModels/ReadModelChangeContext.d.ts.map +1 -0
  134. package/dist/readModels/ReadModelChangeContext.js +4 -0
  135. package/dist/readModels/ReadModelChangeContext.js.map +1 -0
  136. package/dist/readModels/ReadModelChangeType.d.ts +7 -0
  137. package/dist/readModels/ReadModelChangeType.d.ts.map +1 -0
  138. package/dist/readModels/ReadModelChangeType.js +10 -0
  139. package/dist/readModels/ReadModelChangeType.js.map +1 -0
  140. package/dist/readModels/ReadModelChangeset.d.ts +6 -0
  141. package/dist/readModels/ReadModelChangeset.d.ts.map +1 -1
  142. package/dist/readModels/ReadModelWatchOptions.d.ts +19 -0
  143. package/dist/readModels/ReadModelWatchOptions.d.ts.map +1 -0
  144. package/dist/readModels/ReadModelWatchOptions.js +4 -0
  145. package/dist/readModels/ReadModelWatchOptions.js.map +1 -0
  146. package/dist/readModels/ReadModelWatcher.d.ts +52 -0
  147. package/dist/readModels/ReadModelWatcher.d.ts.map +1 -0
  148. package/dist/readModels/ReadModelWatcher.js +276 -0
  149. package/dist/readModels/ReadModelWatcher.js.map +1 -0
  150. package/dist/readModels/ReadModels.d.ts +11 -1
  151. package/dist/readModels/ReadModels.d.ts.map +1 -1
  152. package/dist/readModels/ReadModels.js +47 -11
  153. package/dist/readModels/ReadModels.js.map +1 -1
  154. package/dist/readModels/for_ReadModelWatcher/given/a_kernel_stream.d.ts +15 -0
  155. package/dist/readModels/for_ReadModelWatcher/given/a_kernel_stream.d.ts.map +1 -0
  156. package/dist/readModels/for_ReadModelWatcher/given/a_kernel_stream.js +45 -0
  157. package/dist/readModels/for_ReadModelWatcher/given/a_kernel_stream.js.map +1 -0
  158. package/dist/readModels/for_ReadModelWatcher/given/a_watched_read_model.d.ts +21 -0
  159. package/dist/readModels/for_ReadModelWatcher/given/a_watched_read_model.d.ts.map +1 -0
  160. package/dist/readModels/for_ReadModelWatcher/given/a_watched_read_model.js +35 -0
  161. package/dist/readModels/for_ReadModelWatcher/given/a_watched_read_model.js.map +1 -0
  162. package/dist/readModels/index.d.ts +4 -0
  163. package/dist/readModels/index.d.ts.map +1 -1
  164. package/dist/readModels/index.js +1 -0
  165. package/dist/readModels/index.js.map +1 -1
  166. package/dist/testing/ReactorScenario.d.ts.map +1 -1
  167. package/dist/testing/ReactorScenario.js +2 -0
  168. package/dist/testing/ReactorScenario.js.map +1 -1
  169. package/dist/testing/projections/ProjectionReadModelProcessor.d.ts.map +1 -1
  170. package/dist/testing/projections/ProjectionReadModelProcessor.js +2 -1
  171. package/dist/testing/projections/ProjectionReadModelProcessor.js.map +1 -1
  172. package/dist/transactions/IUnitOfWork.d.ts +3 -1
  173. package/dist/transactions/IUnitOfWork.d.ts.map +1 -1
  174. package/dist/transactions/TransactionalEventRouting.d.ts +12 -0
  175. package/dist/transactions/TransactionalEventRouting.d.ts.map +1 -0
  176. package/dist/transactions/TransactionalEventRouting.js +4 -0
  177. package/dist/transactions/TransactionalEventRouting.js.map +1 -0
  178. package/dist/transactions/UnitOfWork.d.ts +2 -1
  179. package/dist/transactions/UnitOfWork.d.ts.map +1 -1
  180. package/dist/transactions/UnitOfWork.js +11 -2
  181. package/dist/transactions/UnitOfWork.js.map +1 -1
  182. package/dist/transactions/index.d.ts +1 -0
  183. package/dist/transactions/index.d.ts.map +1 -1
  184. package/dist/tsconfig.tsbuildinfo +1 -1
  185. package/dist/types/DecoratorType.d.ts +3 -1
  186. package/dist/types/DecoratorType.d.ts.map +1 -1
  187. package/dist/types/DecoratorType.js +2 -0
  188. package/dist/types/DecoratorType.js.map +1 -1
  189. package/eventSequences/AppendOptions.ts +14 -0
  190. package/eventSequences/EventForEventSourceId.ts +13 -0
  191. package/eventSequences/EventLog.ts +4 -2
  192. package/eventSequences/EventSequence.ts +33 -6
  193. package/eventSequences/ITransactionalEventSequence.ts +5 -2
  194. package/eventSequences/TransactionalEventSequence.ts +5 -4
  195. package/eventSequences/resolveEventSourceRouting.ts +125 -0
  196. package/eventSources/ConcurrencyDimensions.ts +26 -0
  197. package/eventSources/DuplicateEventSourceName.ts +10 -0
  198. package/eventSources/DuplicateEventStreamName.ts +10 -0
  199. package/eventSources/EventRoutingContradictsEventSource.ts +10 -0
  200. package/eventSources/EventSourceDefinition.ts +27 -0
  201. package/eventSources/EventSources.ts +93 -0
  202. package/eventSources/EventStreamDefinition.ts +18 -0
  203. package/eventSources/EventStreamDoesNotBelongToEventSource.ts +10 -0
  204. package/eventSources/EventStreamRequiresEventSource.ts +10 -0
  205. package/eventSources/IEventSources.ts +33 -0
  206. package/eventSources/ResolvedEventRouting.ts +78 -0
  207. package/eventSources/UnknownEventSource.ts +10 -0
  208. package/eventSources/eventSource.ts +116 -0
  209. package/eventSources/for_EventSources/against_a_kernel/when_appending_through_registered_sources.integration.spec.ts +106 -0
  210. package/eventSources/for_EventSources/when_appending_through_a_definition.spec.ts +190 -0
  211. package/eventSources/for_EventSources/when_batch_requires_differing_guards.spec.ts +139 -0
  212. package/eventSources/for_EventSources/when_discovering_and_registering.spec.ts +94 -0
  213. package/eventSources/index.ts +19 -0
  214. package/events/EventContext.ts +6 -0
  215. package/events/WireEventContext.ts +2 -0
  216. package/events/for_EventContext/with_event_source.spec.ts +22 -0
  217. package/events/toClientEventContext.ts +1 -0
  218. package/for_ChronicleClient/when_reconnecting/with_waiting_read_model_watchers.spec.ts +117 -0
  219. package/for_EventStore/when_implementing_a_legacy_event_store.spec.ts +19 -0
  220. package/index.ts +3 -0
  221. package/jobs/for_Jobs/against_a_kernel/when_managing_a_replay_job.integration.spec.ts +24 -4
  222. package/package.json +3 -3
  223. package/projections/eventContextPropertyExpression.ts +2 -1
  224. package/reactors/for_Reactors/against_a_kernel/when_replaying_reactors_with_once_only_handlers.integration.spec.ts +27 -8
  225. package/readModels/IReadModelWatcher.ts +35 -0
  226. package/readModels/IReadModels.ts +17 -2
  227. package/readModels/ReadModelChangeContext.ts +11 -0
  228. package/readModels/ReadModelChangeType.ts +9 -0
  229. package/readModels/ReadModelChangeset.ts +9 -0
  230. package/readModels/ReadModelWatchOptions.ts +23 -0
  231. package/readModels/ReadModelWatcher.ts +271 -0
  232. package/readModels/ReadModels.ts +60 -11
  233. package/readModels/for_ReadModelWatcher/against_a_kernel/when_watching_changes.integration.spec.ts +104 -0
  234. package/readModels/for_ReadModelWatcher/given/a_kernel_stream.ts +50 -0
  235. package/readModels/for_ReadModelWatcher/given/a_watched_read_model.ts +37 -0
  236. package/readModels/for_ReadModelWatcher/watchCompatibility.standard.spec.ts +29 -0
  237. package/readModels/for_ReadModelWatcher/when_a_stream_cannot_recover.spec.ts +86 -0
  238. package/readModels/for_ReadModelWatcher/when_applying_backpressure.spec.ts +80 -0
  239. package/readModels/for_ReadModelWatcher/when_failing_with_buffered_changes.spec.ts +39 -0
  240. package/readModels/for_ReadModelWatcher/when_not_opting_into_resume.spec.ts +62 -0
  241. package/readModels/for_ReadModelWatcher/when_notifying_resubscription.spec.ts +128 -0
  242. package/readModels/for_ReadModelWatcher/when_opting_into_overflow.spec.ts +39 -0
  243. package/readModels/for_ReadModelWatcher/when_receiving_changes.spec.ts +69 -0
  244. package/readModels/for_ReadModelWatcher/when_reconnecting.spec.ts +99 -0
  245. package/readModels/for_ReadModelWatcher/when_reconnecting_during_conversion.spec.ts +84 -0
  246. package/readModels/for_ReadModelWatcher/when_recovering_a_stream.spec.ts +106 -0
  247. package/readModels/for_ReadModelWatcher/when_stopping.spec.ts +85 -0
  248. package/readModels/for_ReadModelWatcher/when_subscribing.spec.ts +62 -0
  249. package/readModels/for_ReadModels/when_watching.spec.ts +85 -0
  250. package/readModels/index.ts +4 -0
  251. package/testing/EventScenario.event-sources.spec.ts +34 -0
  252. package/testing/ReactorScenario.spec.ts +18 -1
  253. package/testing/ReactorScenario.ts +1 -0
  254. package/testing/projections/ProjectionReadModelProcessor.ts +2 -1
  255. package/transactions/IUnitOfWork.ts +3 -1
  256. package/transactions/TransactionalEventRouting.ts +16 -0
  257. package/transactions/UnitOfWork.ts +13 -2
  258. package/transactions/for_UnitOfWork/when_committing_events_with_event_source_routing.spec.ts +41 -0
  259. package/transactions/index.ts +1 -0
  260. package/types/DecoratorType.ts +4 -1
@@ -0,0 +1,117 @@
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 { afterEach, beforeEach, chai, describe, it, vi } from 'vitest';
5
+ import { ChronicleClient } from '../../ChronicleClient.js';
6
+ import { ChronicleOptions } from '../../ChronicleOptions.js';
7
+ import { EventStore } from '../../EventStore.js';
8
+ import type { IEventStore } from '../../IEventStore.js';
9
+ import { IncompatibleChronicleServer } from '../../connection/IncompatibleChronicleServer.js';
10
+ import { RejectedChronicleCredentials } from '../../connection/RejectedChronicleCredentials.js';
11
+ import { projection } from '../../projections/declarative/projection.js';
12
+ import type { IReadModelWatcher } from '../../readModels/IReadModelWatcher.js';
13
+ import { a_kernel_stream } from '../../readModels/for_ReadModelWatcher/given/a_kernel_stream.js';
14
+
15
+ chai.should();
16
+
17
+ const transport = vi.hoisted(() => ({
18
+ resetChannel: vi.fn(), connect: vi.fn(), disconnect: vi.fn(), getVersionInfo: vi.fn(),
19
+ ensureEventStore: vi.fn(), watch: vi.fn(),
20
+ lost: undefined as ((reason: string, error: unknown) => void) | undefined
21
+ }));
22
+ vi.mock('../../connection/ChronicleConnection', () => ({
23
+ ChronicleConnection: class {
24
+ resetChannel = transport.resetChannel;
25
+ connect = transport.connect;
26
+ disconnect = transport.disconnect;
27
+ server = { getVersionInfo: transport.getVersionInfo };
28
+ eventStores = { ensureEventStore: transport.ensureEventStore };
29
+ readModels = { watch: transport.watch };
30
+ }
31
+ }));
32
+ vi.mock('../../connection/KernelKeepAlive', () => ({
33
+ KernelKeepAlive: class {
34
+ constructor(_connections: unknown, onLost: (reason: string, error: unknown) => void) { transport.lost = onLost; }
35
+ async start() {}
36
+ }
37
+ }));
38
+
39
+ class Model { id = ''; }
40
+ class ModelProjection { define() {} }
41
+ projection('model', Model)(ModelProjection);
42
+
43
+ describe('when reconnecting with waiting read model watchers', () => {
44
+ let client: ChronicleClient;
45
+ let store: IEventStore;
46
+ let watcher: IReadModelWatcher<Model>;
47
+ let stream: a_kernel_stream;
48
+ let rejectReconnect: (error: Error) => void;
49
+
50
+ beforeEach(async () => {
51
+ vi.useFakeTimers();
52
+ vi.clearAllMocks();
53
+ // Registration is outside this connection-lifetime specification; retain the real store and watchers.
54
+ vi.spyOn(EventStore.prototype, 'registerArtifacts').mockResolvedValue(undefined);
55
+ transport.resetChannel.mockResolvedValue(undefined);
56
+ transport.connect.mockResolvedValue(undefined);
57
+ transport.getVersionInfo.mockResolvedValue({});
58
+ transport.ensureEventStore.mockResolvedValue({ IsSuccess: true, IsAuthorized: true });
59
+ stream = new a_kernel_stream();
60
+ transport.watch.mockImplementation((_request, options: { signal: AbortSignal }) => {
61
+ stream.signal = options.signal;
62
+ options.signal.addEventListener('abort', () => stream.end(), { once: true });
63
+ return stream;
64
+ });
65
+ client = new ChronicleClient(ChronicleOptions.fromConnectionString('chronicle://localhost:35000', {
66
+ discoveryPatterns: [],
67
+ clientArtifactsProvider: {
68
+ readModels: [Model], projections: [ModelProjection], eventTypes: [], reactors: [], reducers: [],
69
+ seeders: [], constraints: [], webhooks: [], eventTypeMigrations: [], globalForHandlers: []
70
+ }
71
+ }));
72
+ store = await client.getEventStore('store');
73
+ watcher = store.readModels.createWatcher(Model, { resume: true });
74
+ stream.send({ Subscribed: true });
75
+ await watcher.subscribed;
76
+ transport.connect.mockImplementation(() => new Promise<void>((_resolve, reject) => { rejectReconnect = reject; }));
77
+ transport.lost!('keep-alive-ended', { code: 14 });
78
+ await vi.advanceTimersByTimeAsync(0);
79
+ stream.signal.aborted.should.be.true;
80
+ });
81
+ afterEach(() => {
82
+ client.dispose();
83
+ vi.restoreAllMocks();
84
+ vi.useRealTimers();
85
+ });
86
+
87
+ it.each([
88
+ ['incompatible server', IncompatibleChronicleServer],
89
+ ['rejected credentials', RejectedChronicleCredentials]
90
+ ] as const)('should reject readiness and iteration on terminal %s', async (_reason, Failure) => {
91
+ const failure = new Failure('Replacement connection rejected');
92
+ const readiness = watcher.subscribed.catch(error => error);
93
+ const next = watcher[Symbol.asyncIterator]().next().catch(error => error);
94
+ rejectReconnect(failure);
95
+ (await readiness).should.equal(failure);
96
+ (await next).should.equal(failure);
97
+ await vi.advanceTimersByTimeAsync(15000);
98
+ transport.watch.mock.calls.should.have.lengthOf(1);
99
+ transport.connect.mock.calls.should.have.lengthOf(2);
100
+ // A cached store cannot create a watcher that waits forever after the terminal verdict.
101
+ const later = store.readModels.createWatcher(Model);
102
+ (await later.subscribed.catch(error => error)).should.equal(failure);
103
+ (await later[Symbol.asyncIterator]().next().catch(error => error)).should.equal(failure);
104
+ });
105
+
106
+ it('should reject pending readiness but complete iteration cleanly if disposed during recovery', async () => {
107
+ const readiness = watcher.subscribed.catch(error => error);
108
+ const next = watcher[Symbol.asyncIterator]().next();
109
+ client.dispose();
110
+ (await readiness).name.should.equal('AbortError');
111
+ (await next).done!.should.be.true;
112
+ rejectReconnect(new IncompatibleChronicleServer('Late reconnect failure'));
113
+ await vi.advanceTimersByTimeAsync(15000);
114
+ (await watcher[Symbol.asyncIterator]().next()).done!.should.be.true;
115
+ transport.watch.mock.calls.should.have.lengthOf(1);
116
+ });
117
+ });
@@ -0,0 +1,19 @@
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 { chai, describe, it } from 'vitest';
5
+ import type { IEventStore } from '../IEventStore.js';
6
+
7
+ const should = chai.should();
8
+
9
+ /** An implementation written before event sources existed: it has no `eventSources` member. */
10
+ type LegacyEventStore = Omit<IEventStore, 'eventSources'>;
11
+
12
+ describe('when implementing an event store written before event sources', () => {
13
+ it('should still satisfy the IEventStore contract', () => {
14
+ // Compile-time regression: assigning would fail type-checking if `eventSources` became required.
15
+ const legacy = {} as LegacyEventStore;
16
+ const store: IEventStore = legacy;
17
+ should.not.exist(store.eventSources);
18
+ });
19
+ });
package/index.ts CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  import * as events from './events/index.js';
5
5
  import * as eventSequences from './eventSequences/index.js';
6
+ import * as eventSources from './eventSources/index.js';
6
7
  import * as reactors from './reactors/index.js';
7
8
  import * as reducers from './reducers/index.js';
8
9
  import * as seeding from './seeding/index.js';
@@ -48,6 +49,7 @@ export { DiagChronicleLogger } from './logging/DiagChronicleLogger.js';
48
49
 
49
50
  export * from './events/index.js';
50
51
  export * from './eventSequences/index.js';
52
+ export * from './eventSources/index.js';
51
53
  export * from './reactors/index.js';
52
54
  export * from './reducers/index.js';
53
55
  export * from './seeding/index.js';
@@ -74,6 +76,7 @@ export * from './confidentiality/index.js';
74
76
  export {
75
77
  events,
76
78
  eventSequences,
79
+ eventSources,
77
80
  reactors,
78
81
  reducers,
79
82
  seeding,
@@ -3,12 +3,13 @@
3
3
 
4
4
  import { randomUUID } from 'node:crypto';
5
5
  import { afterAll, beforeAll, chai, describe, it } from 'vitest';
6
- import { JobStatus, JobStepStatus, type JobStepSummaryResponse, type JobSummaryResponse } from '@cratis/chronicle.contracts';
6
+ import { JobStatus, JobStepStatus, ObserverRunningState, type JobStepSummaryResponse, type JobSummaryResponse } from '@cratis/chronicle.contracts';
7
7
  import { field, Guid } from '@cratis/fundamentals';
8
8
  import { ChronicleClient } from '../../../ChronicleClient.js';
9
9
  import { ChronicleOptions } from '../../../ChronicleOptions.js';
10
10
  import { fromContractsGuid } from '../../../connection/Guid.js';
11
11
  import type { IClientArtifactsProvider } from '../../../artifacts/index.js';
12
+ import type { ChronicleConnection } from '../../../connection/index.js';
12
13
  import { eventType } from '../../../events/index.js';
13
14
  import type { IEventStore } from '../../../IEventStore.js';
14
15
  import { fromEvent } from '../../../projections/index.js';
@@ -55,8 +56,10 @@ async function eventually<T>(read: () => Promise<T>, accept: (value: T) => boole
55
56
  }
56
57
 
57
58
  describe.skipIf(!connectionString && !process.env.CI)('when managing a replay job against a kernel', () => {
59
+ const storeName = `Jobs${randomUUID().replaceAll('-', '').slice(0, 12)}`;
58
60
  let client: ChronicleClient;
59
61
  let store: IEventStore;
62
+ let connection: ChronicleConnection;
60
63
  let jobId: JobId;
61
64
  let listed: JobSummaryResponse[];
62
65
  let stopped: JobSummaryResponse | undefined;
@@ -65,19 +68,36 @@ describe.skipIf(!connectionString && !process.env.CI)('when managing a replay jo
65
68
  let stopUnknownJob: unknown;
66
69
  let deleteUnknownJob: unknown;
67
70
 
71
+ const waitForActiveObserver = (lastHandledSequenceNumber?: bigint) => eventually(
72
+ () => connection.observers.getObserverInformation({
73
+ EventStore: storeName,
74
+ Namespace: 'Default',
75
+ ObserverId: 'Item',
76
+ EventSequenceId: 'event-log'
77
+ }),
78
+ observer => observer.IsSubscribed && observer.RunningState === ObserverRunningState.Active &&
79
+ (lastHandledSequenceNumber === undefined || observer.LastHandledEventSequenceNumber === lastHandledSequenceNumber),
80
+ 15_000);
81
+
82
+ // Readiness (30s) and the existing job-state waits (90s) leave 30s for RPC overhead.
68
83
  beforeAll(async () => {
69
84
  client = new ChronicleClient(ChronicleOptions.fromConnectionString(connectionString!, {
70
85
  discoveryPatterns: [],
71
86
  clientArtifactsProvider: artifacts
72
87
  }));
73
- store = await client.getEventStore(`Jobs${randomUUID().replaceAll('-', '').slice(0, 12)}`);
88
+ store = await client.getEventStore(storeName);
89
+ connection = (store as unknown as { _connection: ChronicleConnection })._connection;
74
90
 
91
+ // Append only after initial catch-up completes, so it cannot race the explicit replay.
92
+ await waitForActiveObserver();
75
93
  // Enough events that the replay is still running when it is stopped; a stopped job stays until deleted,
76
94
  // where a completed one is removed and could not be listed reliably.
77
- await store.eventLog.appendMany(Array.from({ length: 500 }, (_, index) => ({
95
+ const appended = await store.eventLog.appendMany(Array.from({ length: 500 }, (_, index) => ({
78
96
  eventSourceId: randomUUID(),
79
97
  event: Object.assign(new ItemAdded(), { name: `Item ${index}` })
80
98
  })));
99
+ // ReplayForModel uses Observer.Replay too; wait for acknowledged progress before requesting it.
100
+ await waitForActiveObserver(appended.at(-1)!.sequenceNumber.value);
81
101
 
82
102
  jobId = await store.projections.replayForModel(Item);
83
103
 
@@ -98,7 +118,7 @@ describe.skipIf(!connectionString && !process.env.CI)('when managing a replay jo
98
118
 
99
119
  stopUnknownJob = await store.jobs.stop(randomUUID()).catch((error: unknown) => error);
100
120
  deleteUnknownJob = await store.jobs.delete(randomUUID()).catch((error: unknown) => error);
101
- });
121
+ }, 150_000);
102
122
 
103
123
  afterAll(() => client?.dispose());
104
124
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cratis/chronicle",
3
- "version": "6.47.0",
3
+ "version": "6.49.0",
4
4
  "description": "Event sourcing for TypeScript and Node.js — the idiomatic client for Cratis Chronicle, the open-source event-sourcing database",
5
5
  "author": "Cratis",
6
6
  "license": "MIT",
@@ -173,12 +173,12 @@
173
173
  "clean": "rm -rf dist",
174
174
  "test": "vitest run",
175
175
  "test:integration": "vitest run --config vitest.integration.config.ts",
176
- "test:standard-types": "tsc --ignoreConfig --noEmit --target es2022 --module es2022 --moduleResolution bundler --lib es2022,esnext.decorators --types node --skipLibCheck --strict schemas/decorators.standard.spec.ts projections/declarative/addChildCompatibility.standard.spec.ts projections/declarative/for_CompositeKeyBuilder/when_typing_constant_parts.standard.spec.ts projections/modelBound/decorators.standard.spec.ts events/classDecorators.standard.spec.ts reactors/decorators.standard.spec.ts readModels/standardFallbacks.standard.spec.ts readModels/registration.standard.spec.ts for_ChronicleClient/when_registering_artifacts/with_standard_property_mappings.standard.spec.ts artifacts/validateArtifactSchemas.spec.ts testing/for_EventScenario/when_appending_concept_fields/with_standard_decorators.standard.spec.ts testing/for_EventScenario/when_appending_scoped_constraints/with_standard_decorators.standard.spec.ts schemas/for_JsonSchemaGenerator/when_generating_children/with_standard_decorators.standard.spec.ts events/for_handles/when_decorating_a_method.standard.spec.ts testing/for_ReadModelScenario/when_evaluating_arithmetic/with_standard_decorators.standard.spec.ts testing/for_ReadModelScenario/when_evaluating_joins/with_standard_decorators.standard.spec.ts testing/for_EventScenario/when_appending_field_types/with_standard_decorators.standard.spec.ts eventSequences/for_EventSequence/when_appending/with_subject_annotations.standard.spec.ts projections/for_ProjectionDefinitionCompiler/when_compiling_from_all.standard.spec.ts projections/for_ProjectionDefinitionCompiler/when_compiling_tags/with_standard_decorators.standard.spec.ts telemetry/for_ChronicleTelemetry/when_exposing_names/with_convention_names.spec.ts projections/for_ProjectionDefinitionCompiler/when_compiling_from_all_events.standard.spec.ts events/for_observerFilters/when_registering/with_standard_decorators.standard.spec.ts reactors/for_Reactors/against_a_kernel/when_filtering_source_and_stream_types.integration.spec.ts testing/for_observerScenarios/when_selecting_filters/with_source_and_stream_types.spec.ts",
176
+ "test:standard-types": "tsc --ignoreConfig --noEmit --target es2022 --module es2022 --moduleResolution bundler --lib es2022,esnext.decorators --types node --skipLibCheck --strict schemas/decorators.standard.spec.ts projections/declarative/addChildCompatibility.standard.spec.ts projections/declarative/for_CompositeKeyBuilder/when_typing_constant_parts.standard.spec.ts projections/modelBound/decorators.standard.spec.ts events/classDecorators.standard.spec.ts reactors/decorators.standard.spec.ts readModels/standardFallbacks.standard.spec.ts readModels/registration.standard.spec.ts for_ChronicleClient/when_registering_artifacts/with_standard_property_mappings.standard.spec.ts artifacts/validateArtifactSchemas.spec.ts testing/for_EventScenario/when_appending_concept_fields/with_standard_decorators.standard.spec.ts testing/for_EventScenario/when_appending_scoped_constraints/with_standard_decorators.standard.spec.ts schemas/for_JsonSchemaGenerator/when_generating_children/with_standard_decorators.standard.spec.ts events/for_handles/when_decorating_a_method.standard.spec.ts testing/for_ReadModelScenario/when_evaluating_arithmetic/with_standard_decorators.standard.spec.ts testing/for_ReadModelScenario/when_evaluating_joins/with_standard_decorators.standard.spec.ts testing/for_EventScenario/when_appending_field_types/with_standard_decorators.standard.spec.ts eventSequences/for_EventSequence/when_appending/with_subject_annotations.standard.spec.ts projections/for_ProjectionDefinitionCompiler/when_compiling_from_all.standard.spec.ts projections/for_ProjectionDefinitionCompiler/when_compiling_tags/with_standard_decorators.standard.spec.ts telemetry/for_ChronicleTelemetry/when_exposing_names/with_convention_names.spec.ts projections/for_ProjectionDefinitionCompiler/when_compiling_from_all_events.standard.spec.ts events/for_observerFilters/when_registering/with_standard_decorators.standard.spec.ts reactors/for_Reactors/against_a_kernel/when_filtering_source_and_stream_types.integration.spec.ts testing/for_observerScenarios/when_selecting_filters/with_source_and_stream_types.spec.ts readModels/for_ReadModelWatcher/watchCompatibility.standard.spec.ts for_EventStore/when_implementing_a_legacy_event_store.spec.ts",
177
177
  "test:native": "node ./scripts/check-native-esm.mjs"
178
178
  },
179
179
  "dependencies": {
180
180
  "@bufbuild/protobuf": "^2.16.0",
181
- "@cratis/chronicle.contracts": "19.26.2",
181
+ "@cratis/chronicle.contracts": "19.30.0",
182
182
  "@grpc/grpc-js": "^1.14.5",
183
183
  "@opentelemetry/api": "^1.9.1",
184
184
  "glob": "^13.0.6",
@@ -23,7 +23,8 @@ const contextProperties = {
23
23
  correlationId: true,
24
24
  causation: true,
25
25
  tags: true
26
- } satisfies Record<keyof EventContext, true>;
26
+ // eventSource is deliberately not projectable: its kernel resolution is not part of the supported expression set.
27
+ } satisfies Record<Exclude<keyof EventContext, 'eventSource'>, true>;
27
28
 
28
29
  // Mirror the kernel's DerivedPropertyFunctions registry: Week() applies to dates only.
29
30
  const derivedFunctions: Record<string, string> = { week: 'Week' };
@@ -3,6 +3,7 @@
3
3
 
4
4
  import { randomUUID } from 'node:crypto';
5
5
  import { afterAll, beforeAll, chai, describe, it } from 'vitest';
6
+ import { ObserverRunningState } from '@cratis/chronicle.contracts';
6
7
  import { field } from '@cratis/fundamentals';
7
8
  import { ChronicleClient } from '../../../ChronicleClient.js';
8
9
  import { ChronicleOptions } from '../../../ChronicleOptions.js';
@@ -65,12 +66,11 @@ const artifacts: IClientArtifactsProvider = {
65
66
  globalForHandlers: []
66
67
  };
67
68
 
68
- // 30s was occasionally too tight for this reactor's initial catch-up under concurrent
69
- // kernel load in CI (observed timing out at 31.2s on a run that passed cleanly on retry
70
- // with no code change); 45s keeps the polling assertion honest while tolerating that jitter.
71
- async function eventually(accept: () => boolean, timeoutMs = 45_000): Promise<void> {
69
+ // Keep the existing 45s replay polling budget for concurrent kernel load in CI.
70
+ // Initial observation is gated separately by the bounded readiness waits below.
71
+ async function eventually(accept: () => boolean | Promise<boolean>, timeoutMs = 45_000): Promise<void> {
72
72
  const deadline = Date.now() + timeoutMs;
73
- while (!accept()) {
73
+ while (!await accept()) {
74
74
  if (Date.now() > deadline) throw new Error('Timed out waiting for the kernel');
75
75
  await new Promise(resolve => setTimeout(resolve, 250));
76
76
  }
@@ -86,6 +86,17 @@ describe.skipIf(!connectionString && !process.env.CI)('when replaying a reactor
86
86
  let jobId: string | undefined;
87
87
  let invocationsBeforeReplay: string[];
88
88
 
89
+ const waitForActiveObserver = (lastHandledSequenceNumber?: bigint, timeoutMs = 15_000) => eventually(async () => {
90
+ const observer = await connection.observers.getObserverInformation({
91
+ EventStore: storeName,
92
+ Namespace: 'Default',
93
+ ObserverId: 'MixedOnceOnlyReactor',
94
+ EventSequenceId: 'event-log'
95
+ });
96
+ return observer.IsSubscribed && observer.RunningState === ObserverRunningState.Active &&
97
+ (lastHandledSequenceNumber === undefined || observer.LastHandledEventSequenceNumber === lastHandledSequenceNumber);
98
+ }, timeoutMs);
99
+
89
100
  beforeAll(async () => {
90
101
  client = new ChronicleClient(ChronicleOptions.fromConnectionString(connectionString!, {
91
102
  discoveryPatterns: [],
@@ -94,10 +105,18 @@ describe.skipIf(!connectionString && !process.env.CI)('when replaying a reactor
94
105
  store = await client.getEventStore(storeName);
95
106
  connection = (store as unknown as { _connection: ChronicleConnection })._connection;
96
107
 
108
+ // Registration starts observation in the background. Append only after the subscription
109
+ // is active, so initial catch-up cannot race the explicit replay's state transition.
110
+ // Initial registration and subscription took over 30s under concurrent CI kernel load before.
111
+ await waitForActiveObserver(undefined, 30_000);
97
112
  (await store.eventLog.append(randomUUID(), Object.assign(new MethodOnceOnlyHappened(), { name: 'a' }))).isSuccess.should.be.true;
98
113
  (await store.eventLog.append(randomUUID(), Object.assign(new MethodReplayableHappened(), { name: 'b' }))).isSuccess.should.be.true;
99
- (await store.eventLog.append(randomUUID(), Object.assign(new MethodReplayOnceOnlyHappened(), { name: 'c' }))).isSuccess.should.be.true;
100
- await eventually(() => invocations.length === 3);
114
+ const appended = await store.eventLog.append(randomUUID(), Object.assign(new MethodReplayOnceOnlyHappened(), { name: 'c' }));
115
+ appended.isSuccess.should.be.true;
116
+ // Worst case 30s + 30s + 15s + 45s + 3s stays inside the explicit 150s hook timeout below.
117
+ await eventually(() => invocations.length === 3, 30_000);
118
+ // The handlers update their sink before the kernel acknowledges delivery and records progress.
119
+ await waitForActiveObserver(appended.sequenceNumber.value);
101
120
  invocationsBeforeReplay = [...invocations];
102
121
 
103
122
  jobId = (await connection.observers.replay({
@@ -108,7 +127,7 @@ describe.skipIf(!connectionString && !process.env.CI)('when replaying a reactor
108
127
  })).JobId;
109
128
  await eventually(() => invocations.includes('replayable:b') && invocations.filter(_ => _ === 'replayable:b').length === 2);
110
129
  await new Promise(resolve => setTimeout(resolve, 3_000));
111
- });
130
+ }, 150_000);
112
131
 
113
132
  afterAll(() => client?.dispose());
114
133
 
@@ -0,0 +1,35 @@
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 type { ReadModelChangeset } from './ReadModelChangeset.js';
5
+
6
+ /**
7
+ * A single-consumer stream of read model changes, started when createWatcher() is called.
8
+ * Terminal stream or connection failures retain buffered changes: iteration drains them
9
+ * before rejecting with the original error. Overflow and disposal discard buffered changes.
10
+ */
11
+ export interface IReadModelWatcher<TReadModel> extends AsyncIterable<ReadModelChangeset<TReadModel>> {
12
+ /**
13
+ * Resolves on the kernel's subscription acknowledgment, without requiring iteration.
14
+ * When resumption is opted into, read this property again after disconnect for the next subscription.
15
+ * An already pending promise carries across lifecycle reconnects; a previously resolved
16
+ * promise cannot be revoked. Rejects if the watcher stops before acknowledgment.
17
+ */
18
+ readonly subscribed: Promise<void>;
19
+
20
+ /**
21
+ * Registers a callback after each subscription acknowledgment following the first.
22
+ * Having at least one registered callback opts into resumption on transport failures and
23
+ * completion. Without callbacks or the resume option, transport errors reject iteration.
24
+ * Refresh your query here: changes during an outage are not replayed. Register before
25
+ * awaiting subscribed; callbacks are not replayed for late registrations.
26
+ * Callbacks run in registration order and are awaited before reading more changes on
27
+ * the resumed stream. Do not wait for iteration inside a callback. A thrown error or
28
+ * rejected promise fails iteration. Reconnects retain already received changes.
29
+ * @returns An idempotent function that unregisters the callback. Disposal also unregisters it.
30
+ */
31
+ onResubscribed(callback: () => void | Promise<void>): () => void;
32
+
33
+ /** Stops the stream and completes pending iteration. Safe to call more than once. */
34
+ dispose(): void;
35
+ }
@@ -3,7 +3,9 @@
3
3
 
4
4
  import type { Constructor } from '@cratis/fundamentals';
5
5
  import type { IMaterializedReadModels } from './IMaterializedReadModels.js';
6
+ import type { IReadModelWatcher } from './IReadModelWatcher.js';
6
7
  import type { ReadModelChangeset } from './ReadModelChangeset.js';
8
+ import type { ReadModelWatchOptions } from './ReadModelWatchOptions.js';
7
9
  import type { ReadModelSnapshot } from './ReadModelSnapshot.js';
8
10
 
9
11
  /**
@@ -65,12 +67,25 @@ export interface IReadModels {
65
67
  getSnapshotsById<TReadModel>(readModelType: Constructor<TReadModel>, key: string): Promise<ReadModelSnapshot<TReadModel>[]>;
66
68
 
67
69
  /**
68
- * Watches changes for a specific read model type.
70
+ * Watches changes for a specific read model type, opening the stream on the first next().
71
+ * Pulls changes only as the consumer requests them, without a client-side buffer or overflow limit.
69
72
  * @param readModelType - The read model type to observe.
70
- * @returns An async iterable of read model changes.
73
+ * @returns An async iterable of read model changes. Transport failures reject iteration.
74
+ * @remarks Keeps its original return type so existing watch implementations and mocks can
75
+ * return plain async iterables. Use createWatcher for readiness and lifetime controls.
71
76
  */
72
77
  watch<TReadModel>(readModelType: Constructor<TReadModel>): AsyncIterable<ReadModelChangeset<TReadModel>>;
73
78
 
79
+ /**
80
+ * Starts a single-consumer watcher immediately, without requiring iteration for readiness.
81
+ * @param readModelType - The read model type to observe.
82
+ * @param options - Cancellation, opt-in resumption, and an optional overflow-failure limit.
83
+ * @returns A watcher with subscription readiness, resubscription callbacks, and explicit disposal.
84
+ * Transport failures reject iteration unless resume is true or an onResubscribed callback
85
+ * is registered. Resumed watches retain received changes but do not replay outage gaps.
86
+ */
87
+ createWatcher<TReadModel>(readModelType: Constructor<TReadModel>, options?: ReadModelWatchOptions): IReadModelWatcher<TReadModel>;
88
+
74
89
  /**
75
90
  * Dehydrates a read model session.
76
91
  * @param sessionId - The session identifier to dehydrate.
@@ -0,0 +1,11 @@
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
+ /** The triggering event metadata available on the kernel's read model change stream. */
5
+ export interface ReadModelChangeContext {
6
+ readonly eventStore: string;
7
+ readonly namespace: string;
8
+ readonly sequenceNumber: bigint;
9
+ readonly correlationId?: string;
10
+ readonly occurred?: Date;
11
+ }
@@ -0,0 +1,9 @@
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
+ /** The kind of change to a read model instance. */
5
+ export enum ReadModelChangeType {
6
+ Added = 0,
7
+ Modified = 1,
8
+ Removed = 2
9
+ }
@@ -1,6 +1,9 @@
1
1
  // Copyright (c) Cratis. All rights reserved.
2
2
  // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
3
 
4
+ import type { ReadModelChangeType } from './ReadModelChangeType.js';
5
+ import type { ReadModelChangeContext } from './ReadModelChangeContext.js';
6
+
4
7
  /**
5
8
  * Represents a change observed for a read model.
6
9
  */
@@ -16,4 +19,10 @@ export interface ReadModelChangeset<TReadModel> {
16
19
 
17
20
  /** Whether the read model was removed. */
18
21
  readonly removed: boolean;
22
+
23
+ /** The kind of change. Optional for compatibility with user-created changesets. */
24
+ readonly changeType?: ReadModelChangeType;
25
+
26
+ /** Triggering event metadata supplied by the kernel, not a complete event context. */
27
+ readonly changeContext?: ReadModelChangeContext;
19
28
  }
@@ -0,0 +1,23 @@
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
+ /** Options for creating a read model watcher. */
5
+ export interface ReadModelWatchOptions {
6
+ /** Aborting completes iteration and rejects pending readiness. */
7
+ readonly signal?: AbortSignal;
8
+
9
+ /**
10
+ * Resume after transport failure or completion. Defaults to false; registering an
11
+ * onResubscribed callback also opts in while at least one callback is registered.
12
+ * Changes missed during an outage are not replayed; refresh your query after resubscription.
13
+ */
14
+ readonly resume?: boolean;
15
+
16
+ /**
17
+ * Opt into failing on overflow instead of applying backpressure. Must be a positive safe integer.
18
+ * By default, pause reads at 1,024 buffered changes after subscription acknowledgment.
19
+ * Before each acknowledgment, keep reading to reach the marker; exceeding 1,024 changes
20
+ * (or this explicit limit) fails iteration and pending readiness rather than dropping changes.
21
+ */
22
+ readonly maxBuffered?: number;
23
+ }