@cratis/chronicle 6.50.0 → 6.51.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 (176) hide show
  1. package/ChronicleClient.ts +127 -99
  2. package/ChronicleOptions.ts +23 -0
  3. package/dist/ChronicleClient.d.ts +7 -0
  4. package/dist/ChronicleClient.d.ts.map +1 -1
  5. package/dist/ChronicleClient.js +115 -95
  6. package/dist/ChronicleClient.js.map +1 -1
  7. package/dist/ChronicleOptions.d.ts +14 -0
  8. package/dist/ChronicleOptions.d.ts.map +1 -1
  9. package/dist/ChronicleOptions.js +16 -0
  10. package/dist/ChronicleOptions.js.map +1 -1
  11. package/dist/eventSequences/EventSequence.d.ts +12 -0
  12. package/dist/eventSequences/EventSequence.d.ts.map +1 -1
  13. package/dist/eventSequences/EventSequence.js +202 -175
  14. package/dist/eventSequences/EventSequence.js.map +1 -1
  15. package/dist/eventSequences/ITransactionalEventSequence.d.ts +2 -2
  16. package/dist/eventSequences/PreparedBatchAppend.d.ts +4 -0
  17. package/dist/eventSequences/PreparedBatchAppend.d.ts.map +1 -0
  18. package/dist/eventSequences/PreparedBatchAppend.js +4 -0
  19. package/dist/eventSequences/PreparedBatchAppend.js.map +1 -0
  20. package/dist/eventSequences/PreparedSingleAppend.d.ts +4 -0
  21. package/dist/eventSequences/PreparedSingleAppend.d.ts.map +1 -0
  22. package/dist/eventSequences/PreparedSingleAppend.js +4 -0
  23. package/dist/eventSequences/PreparedSingleAppend.js.map +1 -0
  24. package/dist/eventSequences/WireBatchEvent.d.ts +6 -0
  25. package/dist/eventSequences/WireBatchEvent.d.ts.map +1 -0
  26. package/dist/eventSequences/WireBatchEvent.js +4 -0
  27. package/dist/eventSequences/WireBatchEvent.js.map +1 -0
  28. package/dist/eventSequences/toContractsCausation.d.ts +16 -0
  29. package/dist/eventSequences/toContractsCausation.d.ts.map +1 -0
  30. package/dist/eventSequences/toContractsCausation.js +15 -0
  31. package/dist/eventSequences/toContractsCausation.js.map +1 -0
  32. package/dist/events/constraints/ConstraintBuilder.d.ts +7 -0
  33. package/dist/events/constraints/ConstraintBuilder.d.ts.map +1 -1
  34. package/dist/events/constraints/ConstraintBuilder.js +13 -0
  35. package/dist/events/constraints/ConstraintBuilder.js.map +1 -1
  36. package/dist/events/constraints/ConstraintEventSequences.d.ts +19 -0
  37. package/dist/events/constraints/ConstraintEventSequences.d.ts.map +1 -0
  38. package/dist/events/constraints/ConstraintEventSequences.js +39 -0
  39. package/dist/events/constraints/ConstraintEventSequences.js.map +1 -0
  40. package/dist/events/constraints/Constraints.d.ts.map +1 -1
  41. package/dist/events/constraints/Constraints.js +24 -3
  42. package/dist/events/constraints/Constraints.js.map +1 -1
  43. package/dist/events/constraints/IConstraintBuilder.d.ts +18 -0
  44. package/dist/events/constraints/IConstraintBuilder.d.ts.map +1 -1
  45. package/dist/events/constraints/UniqueOptions.d.ts +15 -0
  46. package/dist/events/constraints/UniqueOptions.d.ts.map +1 -0
  47. package/dist/events/constraints/UniqueOptions.js +4 -0
  48. package/dist/events/constraints/UniqueOptions.js.map +1 -0
  49. package/dist/events/constraints/for_Constraints/when_scoping_to_event_sequences/given/registered_constraints.d.ts +9 -0
  50. package/dist/events/constraints/for_Constraints/when_scoping_to_event_sequences/given/registered_constraints.d.ts.map +1 -0
  51. package/dist/events/constraints/for_Constraints/when_scoping_to_event_sequences/given/registered_constraints.js +22 -0
  52. package/dist/events/constraints/for_Constraints/when_scoping_to_event_sequences/given/registered_constraints.js.map +1 -0
  53. package/dist/events/constraints/index.d.ts +2 -0
  54. package/dist/events/constraints/index.d.ts.map +1 -1
  55. package/dist/events/constraints/index.js.map +1 -1
  56. package/dist/events/constraints/unique.d.ts +10 -0
  57. package/dist/events/constraints/unique.d.ts.map +1 -1
  58. package/dist/events/constraints/unique.js +11 -10
  59. package/dist/events/constraints/unique.js.map +1 -1
  60. package/dist/reducers/ReducerObservation.d.ts +18 -0
  61. package/dist/reducers/ReducerObservation.d.ts.map +1 -0
  62. package/dist/reducers/ReducerObservation.js +4 -0
  63. package/dist/reducers/ReducerObservation.js.map +1 -0
  64. package/dist/reducers/ReducerObservationSession.d.ts +11 -0
  65. package/dist/reducers/ReducerObservationSession.d.ts.map +1 -0
  66. package/dist/reducers/ReducerObservationSession.js +4 -0
  67. package/dist/reducers/ReducerObservationSession.js.map +1 -0
  68. package/dist/reducers/Reducers.d.ts +9 -0
  69. package/dist/reducers/Reducers.d.ts.map +1 -1
  70. package/dist/reducers/Reducers.js +149 -154
  71. package/dist/reducers/Reducers.js.map +1 -1
  72. package/dist/reducers/ReductionOutcome.d.ts +25 -0
  73. package/dist/reducers/ReductionOutcome.d.ts.map +1 -0
  74. package/dist/reducers/ReductionOutcome.js +66 -0
  75. package/dist/reducers/ReductionOutcome.js.map +1 -0
  76. package/dist/testing/EventScenario.d.ts.map +1 -1
  77. package/dist/testing/EventScenario.js +8 -3
  78. package/dist/testing/EventScenario.js.map +1 -1
  79. package/dist/testing/InProcessConstraints.d.ts +4 -1
  80. package/dist/testing/InProcessConstraints.d.ts.map +1 -1
  81. package/dist/testing/InProcessConstraints.js +5 -1
  82. package/dist/testing/InProcessConstraints.js.map +1 -1
  83. package/dist/testing/InProcessEventSequence.d.ts.map +1 -1
  84. package/dist/testing/InProcessEventSequence.js +2 -1
  85. package/dist/testing/InProcessEventSequence.js.map +1 -1
  86. package/dist/transactions/ConflictingConcurrencyScopesInUnitOfWork.d.ts +14 -0
  87. package/dist/transactions/ConflictingConcurrencyScopesInUnitOfWork.d.ts.map +1 -0
  88. package/dist/transactions/ConflictingConcurrencyScopesInUnitOfWork.js +20 -0
  89. package/dist/transactions/ConflictingConcurrencyScopesInUnitOfWork.js.map +1 -0
  90. package/dist/transactions/IUnitOfWork.d.ts +2 -1
  91. package/dist/transactions/IUnitOfWork.d.ts.map +1 -1
  92. package/dist/transactions/TransactionalAppendOptions.d.ts +23 -2
  93. package/dist/transactions/TransactionalAppendOptions.d.ts.map +1 -1
  94. package/dist/transactions/UnitOfWork.d.ts +1 -0
  95. package/dist/transactions/UnitOfWork.d.ts.map +1 -1
  96. package/dist/transactions/UnitOfWork.js +28 -13
  97. package/dist/transactions/UnitOfWork.js.map +1 -1
  98. package/dist/transactions/concurrencyScopesAreEqual.d.ts +9 -0
  99. package/dist/transactions/concurrencyScopesAreEqual.d.ts.map +1 -0
  100. package/dist/transactions/concurrencyScopesAreEqual.js +19 -0
  101. package/dist/transactions/concurrencyScopesAreEqual.js.map +1 -0
  102. package/dist/transactions/for_UnitOfWork/given/a_unit_of_work_with_a_recording_store.d.ts +26 -0
  103. package/dist/transactions/for_UnitOfWork/given/a_unit_of_work_with_a_recording_store.d.ts.map +1 -0
  104. package/dist/transactions/for_UnitOfWork/given/a_unit_of_work_with_a_recording_store.js +31 -0
  105. package/dist/transactions/for_UnitOfWork/given/a_unit_of_work_with_a_recording_store.js.map +1 -0
  106. package/dist/transactions/index.d.ts +1 -0
  107. package/dist/transactions/index.d.ts.map +1 -1
  108. package/dist/transactions/index.js +1 -0
  109. package/dist/transactions/index.js.map +1 -1
  110. package/dist/transactions/toEventForEventSourceId.d.ts +13 -0
  111. package/dist/transactions/toEventForEventSourceId.d.ts.map +1 -0
  112. package/dist/transactions/toEventForEventSourceId.js +29 -0
  113. package/dist/transactions/toEventForEventSourceId.js.map +1 -0
  114. package/dist/tsconfig.tsbuildinfo +1 -1
  115. package/eventSequences/EventSequence.ts +265 -186
  116. package/eventSequences/ITransactionalEventSequence.ts +2 -2
  117. package/eventSequences/PreparedBatchAppend.ts +7 -0
  118. package/eventSequences/PreparedSingleAppend.ts +7 -0
  119. package/eventSequences/WireBatchEvent.ts +7 -0
  120. package/eventSequences/for_TransactionalEventSequence/when_appending_many_with_options.spec.ts +29 -0
  121. package/eventSequences/toContractsCausation.ts +17 -0
  122. package/events/constraints/ConstraintBuilder.ts +16 -0
  123. package/events/constraints/ConstraintEventSequences.ts +41 -0
  124. package/events/constraints/Constraints.ts +22 -3
  125. package/events/constraints/IConstraintBuilder.ts +20 -0
  126. package/events/constraints/UniqueOptions.ts +20 -0
  127. package/events/constraints/for_Constraints/against_a_kernel/when_appending_to_event_sequences_a_constraint_is_scoped_to.integration.spec.ts +118 -0
  128. package/events/constraints/for_Constraints/when_scoping_to_event_sequences/given/registered_constraints.ts +26 -0
  129. package/events/constraints/for_Constraints/when_scoping_to_event_sequences/with_fluent_builder.spec.ts +40 -0
  130. package/events/constraints/for_Constraints/when_scoping_to_event_sequences/with_legacy_decorators.legacy.spec.ts +47 -0
  131. package/events/constraints/for_Constraints/when_scoping_to_event_sequences/with_merged_declarations.spec.ts +36 -0
  132. package/events/constraints/for_Constraints/when_scoping_to_event_sequences/with_standard_decorators.standard.spec.ts +46 -0
  133. package/events/constraints/index.ts +2 -0
  134. package/events/constraints/unique.ts +22 -2
  135. package/for_ChronicleClient/messageSizes.spec.ts +37 -0
  136. package/for_ChronicleOptions/when_configuring_message_sizes/with_message_sizes.spec.ts +29 -0
  137. package/package.json +2 -2
  138. package/reducers/ReducerObservation.ts +25 -0
  139. package/reducers/ReducerObservationSession.ts +15 -0
  140. package/reducers/Reducers.ts +185 -171
  141. package/reducers/ReductionOutcome.ts +75 -0
  142. package/testing/EventScenario.fixtures.spec.ts +1 -1
  143. package/testing/EventScenario.ts +10 -4
  144. package/testing/InProcessConstraints.ts +5 -1
  145. package/testing/InProcessEventSequence.ts +2 -1
  146. package/testing/for_EventScenario/when_appending_event_sequence_constraints/event_sequence_scenario_behaviors.fixture.ts +101 -0
  147. package/testing/for_EventScenario/when_appending_event_sequence_constraints/other_sequence_extended_key.legacy.spec.ts +28 -0
  148. package/testing/for_EventScenario/when_appending_event_sequence_constraints/other_sequence_extended_key.standard.spec.ts +27 -0
  149. package/testing/for_EventScenario/when_appending_event_sequence_constraints/other_sequence_extended_key_behaviors.fixture.ts +40 -0
  150. package/testing/for_EventScenario/when_appending_event_sequence_constraints/with_legacy_decorators.legacy.spec.ts +55 -0
  151. package/testing/for_EventScenario/when_appending_event_sequence_constraints/with_standard_decorators.standard.spec.ts +54 -0
  152. package/transactions/ConflictingConcurrencyScopesInUnitOfWork.ts +19 -0
  153. package/transactions/IUnitOfWork.ts +2 -1
  154. package/transactions/TransactionalAppendOptions.ts +30 -2
  155. package/transactions/UnitOfWork.ts +29 -13
  156. package/transactions/concurrencyScopesAreEqual.ts +23 -0
  157. package/transactions/for_UnitOfWork/against_a_kernel/when_committing_with_append_metadata.integration.spec.ts +70 -0
  158. package/transactions/for_UnitOfWork/given/a_unit_of_work_with_a_recording_store.ts +54 -0
  159. package/transactions/for_UnitOfWork/when_adding_events_with_concurrency_scopes/and_the_event_type_filters_are_equivalent.spec.ts +32 -0
  160. package/transactions/for_UnitOfWork/when_adding_events_with_concurrency_scopes/and_the_event_type_filters_differ.spec.ts +40 -0
  161. package/transactions/for_UnitOfWork/when_adding_events_with_concurrency_scopes/and_the_scopes_are_equal_or_omitted.spec.ts +29 -0
  162. package/transactions/for_UnitOfWork/when_adding_events_with_concurrency_scopes/and_the_scopes_differ_for_one_event_source.spec.ts +29 -0
  163. package/transactions/for_UnitOfWork/when_committing_events_with_append_metadata/and_a_later_event_sequence_fails.spec.ts +30 -0
  164. package/transactions/for_UnitOfWork/when_committing_events_with_append_metadata/and_events_span_event_sequences.spec.ts +34 -0
  165. package/transactions/for_UnitOfWork/when_committing_events_with_append_metadata/and_options_differ_per_event.spec.ts +45 -0
  166. package/transactions/for_UnitOfWork/when_committing_events_with_append_metadata/and_the_append_is_rejected.spec.ts +26 -0
  167. package/transactions/for_UnitOfWork/when_rolling_back_events_with_append_metadata.spec.ts +24 -0
  168. package/transactions/index.ts +1 -0
  169. package/transactions/toEventForEventSourceId.ts +32 -0
  170. package/vitest.integration.config.ts +2 -0
  171. package/webhooks/for_Webhooks/against_a_kernel/given/ReceivedWebhookRequest.fixture.ts +21 -0
  172. package/webhooks/for_Webhooks/against_a_kernel/given/WebhookReceiver.fixture.ts +99 -0
  173. package/webhooks/for_Webhooks/against_a_kernel/when_delivering_events_to_a_failing_webhook.integration.spec.ts +101 -0
  174. package/webhooks/for_Webhooks/against_a_kernel/when_delivering_events_to_a_webhook.integration.spec.ts +130 -0
  175. package/webhooks/for_Webhooks/against_a_kernel/when_registering_a_webhook_that_is_not_replayable_and_not_active.integration.spec.ts +101 -0
  176. package/webhooks/for_Webhooks/when_registering_a_webhook_that_is_not_replayable_and_not_active.spec.ts +43 -0
@@ -27,6 +27,25 @@ import { TypeDiscoverer } from './types/index.js';
27
27
  import { takeUnregisteredModelBoundMappings } from './types/modelBoundPropertyMetadata.js';
28
28
  import { reachableReadModelTypes } from './readModels/rootReadModelTypes.js';
29
29
 
30
+ const connectionIndicators = [
31
+ 'deadline exceeded',
32
+ 'unavailable',
33
+ 'connection',
34
+ 'connectivity',
35
+ 'channel',
36
+ 'socket',
37
+ 'econnrefused',
38
+ 'etimedout'
39
+ ];
40
+
41
+ const errorCodeOf = (error: unknown): number | undefined => (error as { code?: number })?.code;
42
+
43
+ const isTerminalServerError = (error: unknown): error is IncompatibleChronicleServer | RejectedChronicleCredentials =>
44
+ error instanceof IncompatibleChronicleServer || error instanceof RejectedChronicleCredentials;
45
+
46
+ /** gRPC status codes DEADLINE_EXCEEDED (4), INTERNAL (13) and UNAVAILABLE (14) are worth a reconnect. */
47
+ const hasReconnectableStatusCode = (error: unknown): boolean => [4, 13, 14].includes(Number(errorCodeOf(error) ?? -1));
48
+
30
49
  /**
31
50
  * Implements {@link IChronicleClient} by managing a gRPC connection to the
32
51
  * Chronicle Kernel via {@link ChronicleConnection}.
@@ -74,7 +93,12 @@ export class ChronicleClient implements IChronicleClient {
74
93
  const connectionOptions = options.connectionString.disableTls
75
94
  ? { connectionString: options.connectionString, credentials: options.connectionString.createCredentials() }
76
95
  : { connectionString: options.connectionString };
77
- this._connection = new ChronicleConnection({ ...connectionOptions, logger: options.logger });
96
+ this._connection = new ChronicleConnection({
97
+ ...connectionOptions,
98
+ logger: options.logger,
99
+ maxReceiveMessageSize: options.maxReceiveMessageSize ?? undefined,
100
+ maxSendMessageSize: options.maxSendMessageSize ?? undefined
101
+ });
78
102
 
79
103
  this._logger.info('Created Chronicle client', {
80
104
  serverAddress: `${options.connectionString.serverAddress.host}:${options.connectionString.serverAddress.port}`,
@@ -118,46 +142,13 @@ export class ChronicleClient implements IChronicleClient {
118
142
  /** @inheritdoc */
119
143
  async getEventStore(name: string | EventStoreName, namespace?: string | EventStoreNamespaceName): Promise<IEventStore> {
120
144
  const storeName = typeof name === 'string' ? new EventStoreName(name) : name;
121
- const namespaceName = namespace === undefined
122
- ? EventStoreNamespaceName.default
123
- : typeof namespace === 'string'
124
- ? new EventStoreNamespaceName(namespace)
125
- : namespace;
145
+ const namespaceName = this.toNamespaceName(namespace);
126
146
 
127
147
  return observeOperation('getEventStore', async span => {
128
148
  setTelemetryAttribute(span, 'eventStore', storeName.value);
129
149
  setTelemetryAttribute(span, 'namespace', namespaceName.value);
130
150
  try {
131
- const store = await this.withReconnect('get_event_store', async () => {
132
- await this.ensureConnected();
133
-
134
- const key = `${storeName.value}/${namespaceName.value}`;
135
- const existing = this._stores.get(key);
136
- if (existing) {
137
- this._logger.verbose('Returning cached event store', {
138
- eventStore: storeName.value,
139
- namespace: namespaceName.value
140
- });
141
- return existing;
142
- }
143
-
144
- this._logger.debug('Ensuring event store exists in kernel', {
145
- eventStore: storeName.value
146
- });
147
- ensureCommandSuccess('ensure event store', await this._connection.eventStores.ensureEventStore({ Name: storeName.value }));
148
-
149
- const created = new EventStore(storeName, namespaceName, this._connection, this._lifecycle, this.options.defaultSinkTypeId, this.options.clientArtifactsProvider, this.options.reactorResultHandler, this.options.artifactActivator, this.options.readModelNamingPolicy, this.options.telemetry);
150
- this._stores.set(key, created);
151
-
152
- try {
153
- await this.registerArtifactsForStore(created, 'new-store');
154
- return created;
155
- } catch (error) {
156
- if (this._stores.get(key) === created) this._stores.delete(key);
157
- created.disposeObservations();
158
- throw error;
159
- }
160
- });
151
+ const store = await this.withReconnect('get_event_store', () => this.getOrCreateEventStore(storeName, namespaceName));
161
152
 
162
153
  ChronicleConventionMetrics.eventStoreRetrievals.add(1, {
163
154
  [names.attributes.eventStore]: storeName.value,
@@ -179,6 +170,42 @@ export class ChronicleClient implements IChronicleClient {
179
170
  });
180
171
  }
181
172
 
173
+ private toNamespaceName(namespace: string | EventStoreNamespaceName | undefined): EventStoreNamespaceName {
174
+ if (namespace === undefined) return EventStoreNamespaceName.default;
175
+ return typeof namespace === 'string' ? new EventStoreNamespaceName(namespace) : namespace;
176
+ }
177
+
178
+ private async getOrCreateEventStore(storeName: EventStoreName, namespaceName: EventStoreNamespaceName): Promise<EventStore> {
179
+ await this.ensureConnected();
180
+
181
+ const key = `${storeName.value}/${namespaceName.value}`;
182
+ const existing = this._stores.get(key);
183
+ if (existing) {
184
+ this._logger.verbose('Returning cached event store', {
185
+ eventStore: storeName.value,
186
+ namespace: namespaceName.value
187
+ });
188
+ return existing;
189
+ }
190
+
191
+ this._logger.debug('Ensuring event store exists in kernel', {
192
+ eventStore: storeName.value
193
+ });
194
+ ensureCommandSuccess('ensure event store', await this._connection.eventStores.ensureEventStore({ Name: storeName.value }));
195
+
196
+ const created = new EventStore(storeName, namespaceName, this._connection, this._lifecycle, this.options.defaultSinkTypeId, this.options.clientArtifactsProvider, this.options.reactorResultHandler, this.options.artifactActivator, this.options.readModelNamingPolicy, this.options.telemetry);
197
+ this._stores.set(key, created);
198
+
199
+ try {
200
+ await this.registerArtifactsForStore(created, 'new-store');
201
+ return created;
202
+ } catch (error) {
203
+ if (this._stores.get(key) === created) this._stores.delete(key);
204
+ created.disposeObservations();
205
+ throw error;
206
+ }
207
+ }
208
+
182
209
  /** @inheritdoc */
183
210
  async getEventStores(): Promise<EventStoreName[]> {
184
211
  return observeOperation('getEventStores', async span => {
@@ -300,46 +327,7 @@ export class ChronicleClient implements IChronicleClient {
300
327
  if (this._connectionFailure) throw this._connectionFailure;
301
328
 
302
329
  if (!this._reconnectOperation) {
303
- this._reconnectOperation = runInBackgroundContext(async () => {
304
- this._logger.warn('Reconnecting to Chronicle kernel', {
305
- reason,
306
- error
307
- });
308
-
309
- if (this._lifecycle.isConnected) {
310
- await this._lifecycle.disconnected(disconnectError => {
311
- this._logger.error('Disconnected lifecycle callback failed', {
312
- error: disconnectError
313
- });
314
- });
315
- }
316
-
317
- let attempt = 0;
318
- while (!this._isDisposed) {
319
- try {
320
- await this._connection.resetChannel();
321
- await this._connection.connect();
322
- await this._connection.server.getVersionInfo({}, { signal: AbortSignal.timeout(10_000) });
323
- this._consecutiveCredentialRejections = 0;
324
- this._logger.info('Reconnected to Chronicle kernel', { attempt: attempt + 1 });
325
- await this.startKernelKeepAlive();
326
- await this._lifecycle.connected(connectedError => {
327
- this._logger.error('Connected lifecycle callback failed after reconnect', {
328
- error: connectedError
329
- });
330
- });
331
- return;
332
- } catch (reconnectError) {
333
- const terminal = this.terminalConnectionError(reconnectError);
334
- if (terminal) {
335
- this.failConnection(terminal);
336
- throw terminal;
337
- }
338
- attempt++;
339
- await this.backOff(attempt, 'Reconnect attempt failed, retrying', reconnectError);
340
- }
341
- }
342
- }).finally(() => {
330
+ this._reconnectOperation = runInBackgroundContext(() => this.recoverConnection(reason, error)).finally(() => {
343
331
  this._reconnectOperation = undefined;
344
332
  });
345
333
  }
@@ -347,6 +335,51 @@ export class ChronicleClient implements IChronicleClient {
347
335
  await this._reconnectOperation;
348
336
  }
349
337
 
338
+ private async recoverConnection(reason: string, error: unknown): Promise<void> {
339
+ this._logger.warn('Reconnecting to Chronicle kernel', {
340
+ reason,
341
+ error
342
+ });
343
+
344
+ if (this._lifecycle.isConnected) {
345
+ await this._lifecycle.disconnected(disconnectError => {
346
+ this._logger.error('Disconnected lifecycle callback failed', {
347
+ error: disconnectError
348
+ });
349
+ });
350
+ }
351
+
352
+ await this.reconnectWithRetry();
353
+ }
354
+
355
+ private async reconnectWithRetry(): Promise<void> {
356
+ let attempt = 0;
357
+ while (!this._isDisposed) {
358
+ try {
359
+ await this._connection.resetChannel();
360
+ await this._connection.connect();
361
+ await this._connection.server.getVersionInfo({}, { signal: AbortSignal.timeout(10_000) });
362
+ this._consecutiveCredentialRejections = 0;
363
+ this._logger.info('Reconnected to Chronicle kernel', { attempt: attempt + 1 });
364
+ await this.startKernelKeepAlive();
365
+ await this._lifecycle.connected(connectedError => {
366
+ this._logger.error('Connected lifecycle callback failed after reconnect', {
367
+ error: connectedError
368
+ });
369
+ });
370
+ return;
371
+ } catch (reconnectError) {
372
+ const terminal = this.terminalConnectionError(reconnectError);
373
+ if (terminal) {
374
+ this.failConnection(terminal);
375
+ throw terminal;
376
+ }
377
+ attempt++;
378
+ await this.backOff(attempt, 'Reconnect attempt failed, retrying', reconnectError);
379
+ }
380
+ }
381
+ }
382
+
350
383
  /**
351
384
  * Waits out the exponential backoff for a failed attempt and reports why.
352
385
  * The delay is jittered so a fleet of clients that lost the same kernel does
@@ -392,7 +425,7 @@ export class ChronicleClient implements IChronicleClient {
392
425
  }
393
426
 
394
427
  private terminalConnectionError(error: unknown): Error | undefined {
395
- if (error instanceof IncompatibleChronicleServer || error instanceof RejectedChronicleCredentials) return error;
428
+ if (isTerminalServerError(error)) return error;
396
429
  const rejection = (error as { code?: number })?.code === 16 &&
397
430
  (this.options.connectionString.apiKey || this.hasOAuthCredentialRejection(error));
398
431
  this._consecutiveCredentialRejections = rejection ? this._consecutiveCredentialRejections + 1 : 0;
@@ -410,29 +443,20 @@ export class ChronicleClient implements IChronicleClient {
410
443
  }
411
444
 
412
445
  private shouldReconnect(error: unknown): boolean {
413
- if (error instanceof IncompatibleChronicleServer || error instanceof RejectedChronicleCredentials) return false;
414
- if ((error as { code?: number })?.code === 16 &&
415
- (this.options.connectionString.apiKey || !!(error as { tokenFailure?: Error })?.tokenFailure)) return true;
416
- const code = Number((error as { code?: number })?.code ?? -1);
417
- const details = String((error as { details?: string })?.details ?? '');
418
- const message = this.toErrorMessage(error);
446
+ if (isTerminalServerError(error)) return false;
447
+ return this.isRejectedAuthentication(error) ||
448
+ hasReconnectableStatusCode(error) ||
449
+ this.mentionsConnectionProblem(error);
450
+ }
419
451
 
420
- if (code === 4 || code === 13 || code === 14) {
421
- return true;
422
- }
452
+ private isRejectedAuthentication(error: unknown): boolean {
453
+ if (errorCodeOf(error) !== 16) return false;
454
+ return !!this.options.connectionString.apiKey || !!(error as { tokenFailure?: Error })?.tokenFailure;
455
+ }
423
456
 
424
- const connectionIndicators = [
425
- 'deadline exceeded',
426
- 'unavailable',
427
- 'connection',
428
- 'connectivity',
429
- 'channel',
430
- 'socket',
431
- 'econnrefused',
432
- 'etimedout'
433
- ];
434
-
435
- const combined = `${details} ${message}`.toLowerCase();
457
+ private mentionsConnectionProblem(error: unknown): boolean {
458
+ const details = String((error as { details?: string })?.details ?? '');
459
+ const combined = `${details} ${this.toErrorMessage(error)}`.toLowerCase();
436
460
  return connectionIndicators.some(indicator => combined.includes(indicator));
437
461
  }
438
462
 
@@ -452,10 +476,14 @@ export class ChronicleClient implements IChronicleClient {
452
476
  this._watchdogHandle.unref?.();
453
477
  }
454
478
 
479
+ private get cannotRunHealthCheck(): boolean {
480
+ return this._isDisposed || !this._lifecycle.isConnected || this._healthCheckInFlight;
481
+ }
482
+
455
483
  private async runHealthCheck(): Promise<void> {
456
484
  // A black-holed connection makes the probe itself hang, so without the
457
485
  // in-flight guard every tick would stack another one that never returns.
458
- if (this._isDisposed || !this._lifecycle.isConnected || this._healthCheckInFlight) {
486
+ if (this.cannotRunHealthCheck) {
459
487
  return;
460
488
  }
461
489
 
@@ -24,6 +24,8 @@ type ChronicleOptionsConstructorParams = {
24
24
  reactorResultHandler?: ReactorResultHandler;
25
25
  artifactActivator?: ClientArtifactsActivator;
26
26
  readModelNamingPolicy?: ReadModelNamingPolicy;
27
+ maxReceiveMessageSize?: number | null;
28
+ maxSendMessageSize?: number | null;
27
29
  };
28
30
 
29
31
  type ChronicleOptionsFactoryParams = {
@@ -35,6 +37,8 @@ type ChronicleOptionsFactoryParams = {
35
37
  reactorResultHandler?: ReactorResultHandler;
36
38
  artifactActivator?: ClientArtifactsActivator;
37
39
  readModelNamingPolicy?: ReadModelNamingPolicy;
40
+ maxReceiveMessageSize?: number | null;
41
+ maxSendMessageSize?: number | null;
38
42
  };
39
43
 
40
44
  /**
@@ -103,6 +107,18 @@ export class ChronicleOptions {
103
107
  */
104
108
  readonly readModelNamingPolicy?: ReadModelNamingPolicy;
105
109
 
110
+ /**
111
+ * The largest gRPC message, in bytes, the client accepts from the kernel. Defaults to 100 MB, matching the
112
+ * other Chronicle clients. `null` leaves it at gRPC's own default of 4 MB, which is not recommended.
113
+ */
114
+ readonly maxReceiveMessageSize: number | null;
115
+
116
+ /**
117
+ * The largest gRPC message, in bytes, the client sends to the kernel. Defaults to 100 MB, matching the
118
+ * other Chronicle clients. `null` leaves it at gRPC's own default.
119
+ */
120
+ readonly maxSendMessageSize: number | null;
121
+
106
122
  private constructor(options: ChronicleOptionsConstructorParams) {
107
123
  this.connectionString = options.connectionString;
108
124
  this.programIdentifier = options.programIdentifier ?? 'Unknown';
@@ -114,6 +130,8 @@ export class ChronicleOptions {
114
130
  this.reactorResultHandler = options.reactorResultHandler;
115
131
  this.artifactActivator = options.artifactActivator;
116
132
  this.readModelNamingPolicy = options.readModelNamingPolicy;
133
+ this.maxReceiveMessageSize = options.maxReceiveMessageSize === undefined ? ChronicleOptions.defaultMaxMessageSize : options.maxReceiveMessageSize;
134
+ this.maxSendMessageSize = options.maxSendMessageSize === undefined ? ChronicleOptions.defaultMaxMessageSize : options.maxSendMessageSize;
117
135
  const spanNames = options.telemetry?.spanNames;
118
136
  if (spanNames !== undefined && spanNames !== 'legacy' && spanNames !== 'convention') {
119
137
  throw new TypeError('telemetry.spanNames must be legacy or convention.');
@@ -131,6 +149,9 @@ export class ChronicleOptions {
131
149
  this.logger = options.logger;
132
150
  }
133
151
 
152
+ /** The default maximum gRPC message size in bytes: 100 MB. */
153
+ static readonly defaultMaxMessageSize = 100 * 1024 * 1024;
154
+
134
155
  private static defaultDiscoveryPatterns(): string[] {
135
156
  const typescriptEntry = /\.(?:ts|tsx|mts|cts)$/i.test(process.argv[1] ?? '');
136
157
  const typescriptLoader = process.execArgv.some(arg => /(?:tsx|ts-node|--experimental-strip-types|--experimental-transform-types)/i.test(arg));
@@ -173,6 +194,8 @@ export class ChronicleOptions {
173
194
  reactorResultHandler: options?.reactorResultHandler,
174
195
  artifactActivator: options?.artifactActivator,
175
196
  readModelNamingPolicy: options?.readModelNamingPolicy,
197
+ maxReceiveMessageSize: options?.maxReceiveMessageSize,
198
+ maxSendMessageSize: options?.maxSendMessageSize,
176
199
  telemetry: options?.telemetry,
177
200
  logger: options?.logger
178
201
  });
@@ -40,6 +40,8 @@ export declare class ChronicleClient implements IChronicleClient {
40
40
  constructor(options: ChronicleOptions);
41
41
  /** @inheritdoc */
42
42
  getEventStore(name: string | EventStoreName, namespace?: string | EventStoreNamespaceName): Promise<IEventStore>;
43
+ private toNamespaceName;
44
+ private getOrCreateEventStore;
43
45
  /** @inheritdoc */
44
46
  getEventStores(): Promise<EventStoreName[]>;
45
47
  /** @inheritdoc */
@@ -47,6 +49,8 @@ export declare class ChronicleClient implements IChronicleClient {
47
49
  private connectWithRetry;
48
50
  private ensureConnected;
49
51
  private reconnect;
52
+ private recoverConnection;
53
+ private reconnectWithRetry;
50
54
  /**
51
55
  * Waits out the exponential backoff for a failed attempt and reports why.
52
56
  * The delay is jittered so a fleet of clients that lost the same kernel does
@@ -57,8 +61,11 @@ export declare class ChronicleClient implements IChronicleClient {
57
61
  private terminalConnectionError;
58
62
  private hasOAuthCredentialRejection;
59
63
  private shouldReconnect;
64
+ private isRejectedAuthentication;
65
+ private mentionsConnectionProblem;
60
66
  private toErrorMessage;
61
67
  private startConnectionWatchdog;
68
+ private get cannotRunHealthCheck();
62
69
  private runHealthCheck;
63
70
  private startKernelKeepAlive;
64
71
  private failConnection;
@@ -1 +1 @@
1
- {"version":3,"file":"ChronicleClient.d.ts","sourceRoot":"","sources":["../ChronicleClient.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAUzD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAS/C;;;;;;;;;;;;GAYG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IAwBxC,QAAQ,CAAC,OAAO,EAAE,gBAAgB;IAvB9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAQ;IACtD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAU;IAE/C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsB;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsC;IAC9D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IAExD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAE1D,OAAO,CAAC,eAAe,CAAC,CAAiC;IACzD,OAAO,CAAC,iBAAiB,CAAC,CAAgB;IAC1C,OAAO,CAAC,mBAAmB,CAAC,CAAgB;IAC5C,OAAO,CAAC,mBAAmB,CAAC,CAAgB;IAC5C,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,yBAAyB,CAAC,CAAkB;IACpD,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,kBAAkB,CAAC,CAAQ;IACnC,OAAO,CAAC,gCAAgC,CAAK;IAE7C;;;OAGG;IACH,YAAqB,OAAO,EAAE,gBAAgB,EAiD7C;IAED,kBAAkB;IACZ,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,uBAAuB,GAAG,OAAO,CAAC,WAAW,CAAC,CA6DrH;IAED,kBAAkB;IACZ,cAAc,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC,CAuBhD;IAED,kBAAkB;IAClB,OAAO,IAAI,IAAI,CAqBd;YAEa,gBAAgB;YA4ChB,eAAe;YAuBf,SAAS;IAmDvB;;;;OAIG;YACW,OAAO;YAcP,aAAa;IAyB3B,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,eAAe;IA2BvB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,uBAAuB;YAQjB,cAAc;YAkBd,oBAAoB;IAmClC,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,0BAA0B;YAQpB,yBAAyB;CA4B1C"}
1
+ {"version":3,"file":"ChronicleClient.d.ts","sourceRoot":"","sources":["../ChronicleClient.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAUzD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AA4B/C;;;;;;;;;;;;GAYG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IAwBxC,QAAQ,CAAC,OAAO,EAAE,gBAAgB;IAvB9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAQ;IACtD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAU;IAE/C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsB;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsC;IAC9D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IAExD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAE1D,OAAO,CAAC,eAAe,CAAC,CAAiC;IACzD,OAAO,CAAC,iBAAiB,CAAC,CAAgB;IAC1C,OAAO,CAAC,mBAAmB,CAAC,CAAgB;IAC5C,OAAO,CAAC,mBAAmB,CAAC,CAAgB;IAC5C,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,yBAAyB,CAAC,CAAkB;IACpD,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,kBAAkB,CAAC,CAAQ;IACnC,OAAO,CAAC,gCAAgC,CAAK;IAE7C;;;OAGG;IACH,YAAqB,OAAO,EAAE,gBAAgB,EAsD7C;IAED,kBAAkB;IACZ,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,uBAAuB,GAAG,OAAO,CAAC,WAAW,CAAC,CA4BrH;IAED,OAAO,CAAC,eAAe;YAKT,qBAAqB;IA+BnC,kBAAkB;IACZ,cAAc,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC,CAuBhD;IAED,kBAAkB;IAClB,OAAO,IAAI,IAAI,CAqBd;YAEa,gBAAgB;YA4ChB,eAAe;YAuBf,SAAS;YAYT,iBAAiB;YAiBjB,kBAAkB;IA4BhC;;;;OAIG;YACW,OAAO;YAcP,aAAa;IAyB3B,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,wBAAwB;IAKhC,OAAO,CAAC,yBAAyB;IAMjC,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,uBAAuB;IAQ/B,OAAO,KAAK,oBAAoB,GAE/B;YAEa,cAAc;YAkBd,oBAAoB;IAmClC,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,0BAA0B;YAQpB,yBAAyB;CA4B1C"}