@cratis/chronicle 6.20.0 → 6.22.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 (91) hide show
  1. package/dist/eventSequences/EventSequence.d.ts.map +1 -1
  2. package/dist/eventSequences/EventSequence.js +6 -100
  3. package/dist/eventSequences/EventSequence.js.map +1 -1
  4. package/dist/eventSequences/createAppendNotification.d.ts +15 -0
  5. package/dist/eventSequences/createAppendNotification.d.ts.map +1 -0
  6. package/dist/eventSequences/createAppendNotification.js +25 -0
  7. package/dist/eventSequences/createAppendNotification.js.map +1 -0
  8. package/dist/eventSequences/prepareBatchAppend.d.ts +31 -0
  9. package/dist/eventSequences/prepareBatchAppend.d.ts.map +1 -0
  10. package/dist/eventSequences/prepareBatchAppend.js +54 -0
  11. package/dist/eventSequences/prepareBatchAppend.js.map +1 -0
  12. package/dist/reactors/ReactorDispatcher.d.ts +20 -0
  13. package/dist/reactors/ReactorDispatcher.d.ts.map +1 -0
  14. package/dist/reactors/ReactorDispatcher.js +76 -0
  15. package/dist/reactors/ReactorDispatcher.js.map +1 -0
  16. package/dist/reactors/ReactorSideEffects.d.ts +3 -0
  17. package/dist/reactors/ReactorSideEffects.d.ts.map +1 -1
  18. package/dist/reactors/ReactorSideEffects.js +16 -11
  19. package/dist/reactors/ReactorSideEffects.js.map +1 -1
  20. package/dist/reactors/Reactors.d.ts +0 -1
  21. package/dist/reactors/Reactors.d.ts.map +1 -1
  22. package/dist/reactors/Reactors.js +5 -71
  23. package/dist/reactors/Reactors.js.map +1 -1
  24. package/dist/testing/EventScenario.d.ts +1 -1
  25. package/dist/testing/EventScenario.d.ts.map +1 -1
  26. package/dist/testing/EventScenario.js +7 -4
  27. package/dist/testing/EventScenario.js.map +1 -1
  28. package/dist/testing/EventScenarioGiven.d.ts +1 -1
  29. package/dist/testing/EventScenarioGiven.d.ts.map +1 -1
  30. package/dist/testing/EventScenarioGiven.js +2 -5
  31. package/dist/testing/EventScenarioGiven.js.map +1 -1
  32. package/dist/testing/EventScenarioSeed.d.ts +2 -2
  33. package/dist/testing/EventScenarioSeed.d.ts.map +1 -1
  34. package/dist/testing/EventScenarioSeed.js +2 -2
  35. package/dist/testing/EventScenarioSeed.js.map +1 -1
  36. package/dist/testing/EventScenarioWhen.d.ts +1 -1
  37. package/dist/testing/EventScenarioWhen.d.ts.map +1 -1
  38. package/dist/testing/EventScenarioWhen.js +2 -3
  39. package/dist/testing/EventScenarioWhen.js.map +1 -1
  40. package/dist/testing/InProcessEventSequence.d.ts +26 -7
  41. package/dist/testing/InProcessEventSequence.d.ts.map +1 -1
  42. package/dist/testing/InProcessEventSequence.js +253 -43
  43. package/dist/testing/InProcessEventSequence.js.map +1 -1
  44. package/dist/testing/ReactorDeliveryResult.d.ts +10 -0
  45. package/dist/testing/ReactorDeliveryResult.d.ts.map +1 -0
  46. package/dist/testing/ReactorDeliveryResult.js +4 -0
  47. package/dist/testing/ReactorDeliveryResult.js.map +1 -0
  48. package/dist/testing/ReactorScenario.d.ts +55 -0
  49. package/dist/testing/ReactorScenario.d.ts.map +1 -0
  50. package/dist/testing/ReactorScenario.js +275 -0
  51. package/dist/testing/ReactorScenario.js.map +1 -0
  52. package/dist/testing/ReactorScenarioOptions.d.ts +17 -0
  53. package/dist/testing/ReactorScenarioOptions.d.ts.map +1 -0
  54. package/dist/testing/ReactorScenarioOptions.js +4 -0
  55. package/dist/testing/ReactorScenarioOptions.js.map +1 -0
  56. package/dist/testing/RecordedReactorSideEffect.d.ts +13 -0
  57. package/dist/testing/RecordedReactorSideEffect.d.ts.map +1 -0
  58. package/dist/testing/RecordedReactorSideEffect.js +4 -0
  59. package/dist/testing/RecordedReactorSideEffect.js.map +1 -0
  60. package/dist/testing/UnsupportedReactorOperation.d.ts +7 -0
  61. package/dist/testing/UnsupportedReactorOperation.d.ts.map +1 -0
  62. package/dist/testing/UnsupportedReactorOperation.js +14 -0
  63. package/dist/testing/UnsupportedReactorOperation.js.map +1 -0
  64. package/dist/testing/index.d.ts +5 -0
  65. package/dist/testing/index.d.ts.map +1 -1
  66. package/dist/testing/index.js +2 -0
  67. package/dist/testing/index.js.map +1 -1
  68. package/dist/tsconfig.tsbuildinfo +1 -1
  69. package/eventSequences/EventSequence.ts +10 -107
  70. package/eventSequences/createAppendNotification.ts +35 -0
  71. package/eventSequences/prepareBatchAppend.ts +63 -0
  72. package/package.json +1 -1
  73. package/reactors/ReactorDispatcher.ts +95 -0
  74. package/reactors/ReactorSideEffects.ts +21 -13
  75. package/reactors/Reactors.ts +11 -83
  76. package/testing/EventScenario.batches.spec.ts +537 -0
  77. package/testing/EventScenario.fixtures.spec.ts +2 -13
  78. package/testing/EventScenario.rejections.spec.ts +5 -39
  79. package/testing/EventScenario.ts +7 -5
  80. package/testing/EventScenarioGiven.ts +2 -5
  81. package/testing/EventScenarioSeed.ts +4 -4
  82. package/testing/EventScenarioWhen.ts +2 -4
  83. package/testing/InProcessEventSequence.ts +239 -39
  84. package/testing/ReactorDeliveryResult.ts +13 -0
  85. package/testing/ReactorScenario.rejections.spec.ts +100 -0
  86. package/testing/ReactorScenario.spec.ts +611 -0
  87. package/testing/ReactorScenario.ts +279 -0
  88. package/testing/ReactorScenarioOptions.ts +20 -0
  89. package/testing/RecordedReactorSideEffect.ts +16 -0
  90. package/testing/UnsupportedReactorOperation.ts +10 -0
  91. package/testing/index.ts +5 -0
@@ -4,11 +4,10 @@
4
4
  import 'reflect-metadata';
5
5
  import { diag } from '@opentelemetry/api';
6
6
  import { Constructor } from '@cratis/fundamentals';
7
- import { EventObservationState, ObservationState, ReactorMessage, ReplayState } from '@cratis/chronicle.contracts';
7
+ import { ObservationState, ReactorMessage, ReplayState } from '@cratis/chronicle.contracts';
8
8
  import { IClientArtifactsProvider } from '../artifacts/index.js';
9
9
  import { ChronicleConnection } from '../connection/index.js';
10
10
  import { ConnectionLifecycle } from '../connection/ConnectionLifecycle.js';
11
- import { getEventTypeMetadata } from '../events/eventTypeDecorator.js';
12
11
  import { toClientEventContext } from '../events/toClientEventContext.js';
13
12
  import { getTagsFor } from '../events/tagDecorator.js';
14
13
  import { getFilterTagsFor } from '../events/filterEventsByTagDecorator.js';
@@ -19,7 +18,8 @@ import { IReactors } from './IReactors.js';
19
18
  import { dispatchReactorSideEffects } from './ReactorSideEffects.js';
20
19
  import { getReactorMetadata } from './reactor.js';
21
20
  import { isOnceOnly } from './onceOnly.js';
22
- import { getReplayEventType } from './replay.js';
21
+ import { getReactorEventTypes, selectReactorHandler, invokeReactorHandler } from './ReactorDispatcher.js';
22
+ import type { ReactorEventTypeEntry } from './ReactorDispatcher.js';
23
23
  import type { ReactorResultHandler } from './ReactorResultHandler.js';
24
24
  import type { ReactorServices } from './ReactorServices.js';
25
25
  import type { IEventStore } from '../IEventStore.js';
@@ -36,13 +36,6 @@ const EVENT_SOURCE_ID_KEY = '$eventSourceId';
36
36
  /** Sentinel sequence number sent back when no event was successfully processed. */
37
37
  const SEQUENCE_NUMBER_UNAVAILABLE = 4294967295n;
38
38
 
39
- interface EventTypeEntry {
40
- readonly id: string;
41
- readonly generation: number;
42
- readonly methodName?: string;
43
- readonly replayMethodName?: string;
44
- }
45
-
46
39
  /**
47
40
  * A push-based async queue that implements {@link AsyncIterable} for use with nice-grpc
48
41
  * bidirectional streaming. Values pushed via {@link send} are yielded in order to
@@ -187,7 +180,7 @@ export class Reactors implements IReactors {
187
180
  if (this._disposed) return;
188
181
  const metadata = getReactorMetadata(reactorType)!;
189
182
  const eventSequenceId = metadata.eventSequenceId ?? EventSequenceId.eventLog.value;
190
- const eventTypes = this.getEventTypesFor(reactorType);
183
+ const eventTypes = getReactorEventTypes(reactorType, this._clientArtifacts.eventTypes);
191
184
 
192
185
  this._logger.info('Starting reactor observation', {
193
186
  reactorId: id,
@@ -203,7 +196,7 @@ export class Reactors implements IReactors {
203
196
  id: string,
204
197
  reactorType: Constructor,
205
198
  eventSequenceId: string,
206
- eventTypes: EventTypeEntry[]
199
+ eventTypes: ReactorEventTypeEntry[]
207
200
  ): Promise<void> {
208
201
  try {
209
202
  await this.observeReactor(id, reactorType, eventSequenceId, eventTypes);
@@ -246,7 +239,7 @@ export class Reactors implements IReactors {
246
239
  id: string,
247
240
  reactorType: Constructor,
248
241
  eventSequenceId: string,
249
- eventTypes: EventTypeEntry[]
242
+ eventTypes: ReactorEventTypeEntry[]
250
243
  ): Promise<void> {
251
244
  const queue = new AsyncQueue<ReactorMessage>();
252
245
  const controller = new AbortController();
@@ -323,12 +316,8 @@ export class Reactors implements IReactors {
323
316
  }
324
317
 
325
318
  const selectHandler = (event: typeof eventsToObserve.Events[number]) => {
326
- const entry = eventTypes.find(candidate => candidate.id === event.Context?.EventType?.Id);
327
- if (!entry) return undefined;
328
- const isReplay = (event.Context!.ObservationState & EventObservationState.Replay) !== 0;
329
- const methodName = isReplay ? (entry.replayMethodName ?? entry.methodName) : entry.methodName;
330
- const method = methodName ? (reactorInstance?.[methodName] ?? (reactorType.prototype as Record<string, Function>)[methodName]) : undefined;
331
- return { methodName, isReplay, skipReplay: isReplay && method !== undefined && isOnceOnly(method) };
319
+ return selectReactorHandler(eventTypes, reactorType, reactorInstance,
320
+ event.Context?.EventType?.Id, event.Context?.ObservationState ?? 0);
332
321
  };
333
322
 
334
323
  const processEvents = async (artifact: ActivatedArtifact<Record<string, Function>>) => {
@@ -369,11 +358,9 @@ export class Reactors implements IReactors {
369
358
  eventTypeId
370
359
  });
371
360
 
372
- await runActivated(artifact, async () => {
373
- const handlerResult = await artifact.instance[methodName](content, context, services);
374
- await dispatchReactorSideEffects(this._eventLog, handlerResult, context, reactorType as Function,
375
- this._eventStoreName, this._namespace, this._resultHandler);
376
- }, { delivery: ArtifactDelivery.Events, eventContext: context, methodName });
361
+ await invokeReactorHandler(artifact, methodName, content, context, services, result =>
362
+ dispatchReactorSideEffects(this._eventLog, result, context, reactorType as Function,
363
+ this._eventStoreName, this._namespace, this._resultHandler));
377
364
 
378
365
  lastSuccessfullyObservedEvent = event.Context!.SequenceNumber;
379
366
  } catch (err) {
@@ -446,65 +433,6 @@ export class Reactors implements IReactors {
446
433
  }
447
434
  }
448
435
 
449
- private getEventTypesFor(reactorType: Constructor): EventTypeEntry[] {
450
- const proto = reactorType.prototype as Record<string, unknown>;
451
- const entries: EventTypeEntry[] = [];
452
- const replayHandlers = new Map<string, string>();
453
- const eventTypes = new Map<string, { eventTypeClass: Function; id: string; generation: number }>();
454
-
455
- for (const eventTypeClass of this._clientArtifacts.eventTypes) {
456
- const metadata = getEventTypeMetadata(eventTypeClass);
457
- if (metadata) {
458
- eventTypes.set((eventTypeClass as Function).name, {
459
- eventTypeClass: eventTypeClass as Function,
460
- id: metadata.eventType.id.value,
461
- generation: metadata.eventType.generation.value
462
- });
463
- }
464
- }
465
-
466
- // A derived method shadows a base method of the same name, even if it is not marked for replay.
467
- const seenMethods = new Set<string>();
468
- for (let current = proto; current && current !== Object.prototype; current = Object.getPrototypeOf(current) as Record<string, unknown>) {
469
- for (const name of Object.getOwnPropertyNames(current)) {
470
- if (seenMethods.has(name)) continue;
471
- seenMethods.add(name);
472
- const method = current[name];
473
- if (typeof method !== 'function') continue;
474
- const replayEventType = getReplayEventType(method);
475
- if (replayEventType === undefined) continue;
476
-
477
- const eventType = replayEventType === true
478
- ? eventTypes.get(name.startsWith('replay') ? name.slice('replay'.length) : '')
479
- : [...eventTypes.values()].find(candidate => candidate.eventTypeClass === replayEventType);
480
- if (!eventType) {
481
- throw new Error(`Replay handler '${name}' on reactor '${(reactorType as Function).name}' has no registered event type.`);
482
- }
483
- if (replayHandlers.has(eventType.id)) {
484
- throw new Error(`Reactor '${(reactorType as Function).name}' has multiple replay handlers for event type '${eventType.id}': '${replayHandlers.get(eventType.id)}' and '${name}'.`);
485
- }
486
- replayHandlers.set(eventType.id, name);
487
- }
488
- }
489
-
490
- for (const [className, eventType] of eventTypes) {
491
- const methodName = className.charAt(0).toLowerCase() + className.slice(1);
492
- const liveMethod = proto[methodName];
493
- const liveMethodName = typeof liveMethod === 'function' && getReplayEventType(liveMethod) === undefined ? methodName : undefined;
494
- const replayMethodName = replayHandlers.get(eventType.id);
495
- if (liveMethodName || replayMethodName) {
496
- entries.push({
497
- id: eventType.id,
498
- generation: eventType.generation,
499
- methodName: liveMethodName,
500
- replayMethodName
501
- });
502
- }
503
- }
504
-
505
- return entries;
506
- }
507
-
508
436
  private disconnectAll(): void {
509
437
  for (const controller of this._observations.values()) controller.abort();
510
438
  for (const queue of this._queues.values()) queue.complete();