@cratis/chronicle 6.48.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 (177) hide show
  1. package/EventStore.ts +8 -2
  2. package/IEventStore.ts +8 -0
  3. package/artifacts/DefaultClientArtifactsProvider.ts +5 -0
  4. package/artifacts/IClientArtifactsProvider.ts +3 -0
  5. package/connection/ChronicleConnection.ts +6 -0
  6. package/connection/ChronicleServices.ts +2 -0
  7. package/dist/EventStore.d.ts +2 -0
  8. package/dist/EventStore.d.ts.map +1 -1
  9. package/dist/EventStore.js +7 -2
  10. package/dist/EventStore.js.map +1 -1
  11. package/dist/IEventStore.d.ts +7 -0
  12. package/dist/IEventStore.d.ts.map +1 -1
  13. package/dist/artifacts/DefaultClientArtifactsProvider.d.ts +2 -0
  14. package/dist/artifacts/DefaultClientArtifactsProvider.d.ts.map +1 -1
  15. package/dist/artifacts/DefaultClientArtifactsProvider.js +4 -0
  16. package/dist/artifacts/DefaultClientArtifactsProvider.js.map +1 -1
  17. package/dist/artifacts/IClientArtifactsProvider.d.ts +2 -0
  18. package/dist/artifacts/IClientArtifactsProvider.d.ts.map +1 -1
  19. package/dist/connection/ChronicleConnection.d.ts +1 -0
  20. package/dist/connection/ChronicleConnection.d.ts.map +1 -1
  21. package/dist/connection/ChronicleConnection.js +5 -1
  22. package/dist/connection/ChronicleConnection.js.map +1 -1
  23. package/dist/connection/ChronicleServices.d.ts +2 -1
  24. package/dist/connection/ChronicleServices.d.ts.map +1 -1
  25. package/dist/eventSequences/AppendOptions.d.ts +12 -0
  26. package/dist/eventSequences/AppendOptions.d.ts.map +1 -1
  27. package/dist/eventSequences/EventForEventSourceId.d.ts +11 -0
  28. package/dist/eventSequences/EventForEventSourceId.d.ts.map +1 -1
  29. package/dist/eventSequences/EventLog.d.ts +2 -1
  30. package/dist/eventSequences/EventLog.d.ts.map +1 -1
  31. package/dist/eventSequences/EventLog.js +2 -2
  32. package/dist/eventSequences/EventLog.js.map +1 -1
  33. package/dist/eventSequences/EventSequence.d.ts +4 -1
  34. package/dist/eventSequences/EventSequence.d.ts.map +1 -1
  35. package/dist/eventSequences/EventSequence.js +32 -6
  36. package/dist/eventSequences/EventSequence.js.map +1 -1
  37. package/dist/eventSequences/ITransactionalEventSequence.d.ts +5 -2
  38. package/dist/eventSequences/ITransactionalEventSequence.d.ts.map +1 -1
  39. package/dist/eventSequences/TransactionalEventSequence.d.ts +3 -2
  40. package/dist/eventSequences/TransactionalEventSequence.d.ts.map +1 -1
  41. package/dist/eventSequences/TransactionalEventSequence.js +4 -4
  42. package/dist/eventSequences/TransactionalEventSequence.js.map +1 -1
  43. package/dist/eventSequences/resolveEventSourceRouting.d.ts +39 -0
  44. package/dist/eventSequences/resolveEventSourceRouting.d.ts.map +1 -0
  45. package/dist/eventSequences/resolveEventSourceRouting.js +92 -0
  46. package/dist/eventSequences/resolveEventSourceRouting.js.map +1 -0
  47. package/dist/eventSources/ConcurrencyDimensions.d.ts +19 -0
  48. package/dist/eventSources/ConcurrencyDimensions.d.ts.map +1 -0
  49. package/dist/eventSources/ConcurrencyDimensions.js +19 -0
  50. package/dist/eventSources/ConcurrencyDimensions.js.map +1 -0
  51. package/dist/eventSources/DuplicateEventSourceName.d.ts +6 -0
  52. package/dist/eventSources/DuplicateEventSourceName.d.ts.map +1 -0
  53. package/dist/eventSources/DuplicateEventSourceName.js +12 -0
  54. package/dist/eventSources/DuplicateEventSourceName.js.map +1 -0
  55. package/dist/eventSources/DuplicateEventStreamName.d.ts +7 -0
  56. package/dist/eventSources/DuplicateEventStreamName.d.ts.map +1 -0
  57. package/dist/eventSources/DuplicateEventStreamName.js +14 -0
  58. package/dist/eventSources/DuplicateEventStreamName.js.map +1 -0
  59. package/dist/eventSources/EventRoutingContradictsEventSource.d.ts +9 -0
  60. package/dist/eventSources/EventRoutingContradictsEventSource.d.ts.map +1 -0
  61. package/dist/eventSources/EventRoutingContradictsEventSource.js +18 -0
  62. package/dist/eventSources/EventRoutingContradictsEventSource.js.map +1 -0
  63. package/dist/eventSources/EventSourceDefinition.d.ts +20 -0
  64. package/dist/eventSources/EventSourceDefinition.d.ts.map +1 -0
  65. package/dist/eventSources/EventSourceDefinition.js +4 -0
  66. package/dist/eventSources/EventSourceDefinition.js.map +1 -0
  67. package/dist/eventSources/EventSources.d.ts +31 -0
  68. package/dist/eventSources/EventSources.d.ts.map +1 -0
  69. package/dist/eventSources/EventSources.js +90 -0
  70. package/dist/eventSources/EventSources.js.map +1 -0
  71. package/dist/eventSources/EventStreamDefinition.d.ts +13 -0
  72. package/dist/eventSources/EventStreamDefinition.d.ts.map +1 -0
  73. package/dist/eventSources/EventStreamDefinition.js +4 -0
  74. package/dist/eventSources/EventStreamDefinition.js.map +1 -0
  75. package/dist/eventSources/EventStreamDoesNotBelongToEventSource.d.ts +7 -0
  76. package/dist/eventSources/EventStreamDoesNotBelongToEventSource.d.ts.map +1 -0
  77. package/dist/eventSources/EventStreamDoesNotBelongToEventSource.js +14 -0
  78. package/dist/eventSources/EventStreamDoesNotBelongToEventSource.js.map +1 -0
  79. package/dist/eventSources/EventStreamRequiresEventSource.d.ts +6 -0
  80. package/dist/eventSources/EventStreamRequiresEventSource.d.ts.map +1 -0
  81. package/dist/eventSources/EventStreamRequiresEventSource.js +12 -0
  82. package/dist/eventSources/EventStreamRequiresEventSource.js.map +1 -0
  83. package/dist/eventSources/IEventSources.d.ts +27 -0
  84. package/dist/eventSources/IEventSources.d.ts.map +1 -0
  85. package/dist/eventSources/IEventSources.js +4 -0
  86. package/dist/eventSources/IEventSources.js.map +1 -0
  87. package/dist/eventSources/ResolvedEventRouting.d.ts +37 -0
  88. package/dist/eventSources/ResolvedEventRouting.d.ts.map +1 -0
  89. package/dist/eventSources/ResolvedEventRouting.js +60 -0
  90. package/dist/eventSources/ResolvedEventRouting.js.map +1 -0
  91. package/dist/eventSources/UnknownEventSource.d.ts +6 -0
  92. package/dist/eventSources/UnknownEventSource.d.ts.map +1 -0
  93. package/dist/eventSources/UnknownEventSource.js +12 -0
  94. package/dist/eventSources/UnknownEventSource.js.map +1 -0
  95. package/dist/eventSources/eventSource.d.ts +60 -0
  96. package/dist/eventSources/eventSource.d.ts.map +1 -0
  97. package/dist/eventSources/eventSource.js +72 -0
  98. package/dist/eventSources/eventSource.js.map +1 -0
  99. package/dist/eventSources/index.d.ts +17 -0
  100. package/dist/eventSources/index.d.ts.map +1 -0
  101. package/dist/eventSources/index.js +13 -0
  102. package/dist/eventSources/index.js.map +1 -0
  103. package/dist/events/EventContext.d.ts +5 -0
  104. package/dist/events/EventContext.d.ts.map +1 -1
  105. package/dist/events/WireEventContext.d.ts +2 -0
  106. package/dist/events/WireEventContext.d.ts.map +1 -1
  107. package/dist/events/toClientEventContext.d.ts.map +1 -1
  108. package/dist/events/toClientEventContext.js +1 -0
  109. package/dist/events/toClientEventContext.js.map +1 -1
  110. package/dist/index.d.ts +3 -1
  111. package/dist/index.d.ts.map +1 -1
  112. package/dist/index.js +3 -1
  113. package/dist/index.js.map +1 -1
  114. package/dist/projections/eventContextPropertyExpression.d.ts.map +1 -1
  115. package/dist/projections/eventContextPropertyExpression.js +1 -0
  116. package/dist/projections/eventContextPropertyExpression.js.map +1 -1
  117. package/dist/testing/projections/ProjectionReadModelProcessor.d.ts.map +1 -1
  118. package/dist/testing/projections/ProjectionReadModelProcessor.js +2 -1
  119. package/dist/testing/projections/ProjectionReadModelProcessor.js.map +1 -1
  120. package/dist/transactions/IUnitOfWork.d.ts +3 -1
  121. package/dist/transactions/IUnitOfWork.d.ts.map +1 -1
  122. package/dist/transactions/TransactionalEventRouting.d.ts +12 -0
  123. package/dist/transactions/TransactionalEventRouting.d.ts.map +1 -0
  124. package/dist/transactions/TransactionalEventRouting.js +4 -0
  125. package/dist/transactions/TransactionalEventRouting.js.map +1 -0
  126. package/dist/transactions/UnitOfWork.d.ts +2 -1
  127. package/dist/transactions/UnitOfWork.d.ts.map +1 -1
  128. package/dist/transactions/UnitOfWork.js +11 -2
  129. package/dist/transactions/UnitOfWork.js.map +1 -1
  130. package/dist/transactions/index.d.ts +1 -0
  131. package/dist/transactions/index.d.ts.map +1 -1
  132. package/dist/tsconfig.tsbuildinfo +1 -1
  133. package/dist/types/DecoratorType.d.ts +3 -1
  134. package/dist/types/DecoratorType.d.ts.map +1 -1
  135. package/dist/types/DecoratorType.js +2 -0
  136. package/dist/types/DecoratorType.js.map +1 -1
  137. package/eventSequences/AppendOptions.ts +14 -0
  138. package/eventSequences/EventForEventSourceId.ts +13 -0
  139. package/eventSequences/EventLog.ts +4 -2
  140. package/eventSequences/EventSequence.ts +33 -6
  141. package/eventSequences/ITransactionalEventSequence.ts +5 -2
  142. package/eventSequences/TransactionalEventSequence.ts +5 -4
  143. package/eventSequences/resolveEventSourceRouting.ts +125 -0
  144. package/eventSources/ConcurrencyDimensions.ts +26 -0
  145. package/eventSources/DuplicateEventSourceName.ts +10 -0
  146. package/eventSources/DuplicateEventStreamName.ts +10 -0
  147. package/eventSources/EventRoutingContradictsEventSource.ts +10 -0
  148. package/eventSources/EventSourceDefinition.ts +27 -0
  149. package/eventSources/EventSources.ts +93 -0
  150. package/eventSources/EventStreamDefinition.ts +18 -0
  151. package/eventSources/EventStreamDoesNotBelongToEventSource.ts +10 -0
  152. package/eventSources/EventStreamRequiresEventSource.ts +10 -0
  153. package/eventSources/IEventSources.ts +33 -0
  154. package/eventSources/ResolvedEventRouting.ts +78 -0
  155. package/eventSources/UnknownEventSource.ts +10 -0
  156. package/eventSources/eventSource.ts +116 -0
  157. package/eventSources/for_EventSources/against_a_kernel/when_appending_through_registered_sources.integration.spec.ts +106 -0
  158. package/eventSources/for_EventSources/when_appending_through_a_definition.spec.ts +190 -0
  159. package/eventSources/for_EventSources/when_batch_requires_differing_guards.spec.ts +139 -0
  160. package/eventSources/for_EventSources/when_discovering_and_registering.spec.ts +94 -0
  161. package/eventSources/index.ts +19 -0
  162. package/events/EventContext.ts +6 -0
  163. package/events/WireEventContext.ts +2 -0
  164. package/events/for_EventContext/with_event_source.spec.ts +22 -0
  165. package/events/toClientEventContext.ts +1 -0
  166. package/for_EventStore/when_implementing_a_legacy_event_store.spec.ts +19 -0
  167. package/index.ts +3 -0
  168. package/package.json +3 -3
  169. package/projections/eventContextPropertyExpression.ts +2 -1
  170. package/testing/EventScenario.event-sources.spec.ts +34 -0
  171. package/testing/projections/ProjectionReadModelProcessor.ts +2 -1
  172. package/transactions/IUnitOfWork.ts +3 -1
  173. package/transactions/TransactionalEventRouting.ts +16 -0
  174. package/transactions/UnitOfWork.ts +13 -2
  175. package/transactions/for_UnitOfWork/when_committing_events_with_event_source_routing.spec.ts +41 -0
  176. package/transactions/index.ts +1 -0
  177. package/types/DecoratorType.ts +4 -1
@@ -0,0 +1,106 @@
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 { randomUUID } from 'node:crypto';
5
+ import { afterAll, beforeAll, chai, describe, it } from 'vitest';
6
+ import { field } from '@cratis/fundamentals';
7
+ import { ChronicleClient, ChronicleOptions, EventSequenceNumber, ConcurrencyDimensions, eventSource, eventStream, eventType,
8
+ type IClientArtifactsProvider, type IEventStore } from '../../../index.js';
9
+
10
+ const should = chai.should();
11
+ const connectionString = process.env.CHRONICLE_INTEGRATION_CONNECTION_STRING;
12
+
13
+ @eventType()
14
+ class FundsDeposited { @field(Number) amount = 0; }
15
+
16
+ @eventSource({ name: 'Account', description: 'A bank account', concurrency: ConcurrencyDimensions.eventSourceId })
17
+ @eventStream('Transactions', { description: 'Money movements' })
18
+ class AccountEventSource {}
19
+
20
+ @eventSource({ name: 'Customer' })
21
+ class CustomerEventSource {}
22
+
23
+ @eventSource({ name: 'Ledger' })
24
+ @eventStream('Monthly', { concurrency: ConcurrencyDimensions.eventSourceId | ConcurrencyDimensions.eventStreamId })
25
+ class LedgerEventSource {}
26
+
27
+ const artifacts: IClientArtifactsProvider = {
28
+ eventTypes: [FundsDeposited], eventSources: [AccountEventSource, CustomerEventSource, LedgerEventSource], readModels: [], reactors: [], reducers: [],
29
+ seeders: [], constraints: [], projections: [], webhooks: [], eventTypeMigrations: [], globalForHandlers: []
30
+ };
31
+
32
+ const sequenceStart = EventSequenceNumber.first;
33
+ const deposit = (amount: number) => Object.assign(new FundsDeposited(), { amount });
34
+
35
+ describe.skipIf(!connectionString && !process.env.CI)('when appending through registered event sources against a kernel', () => {
36
+ const storeName = `Sources${randomUUID().replaceAll('-', '').slice(0, 12)}`;
37
+ let client: ChronicleClient;
38
+ let store: IEventStore;
39
+
40
+ beforeAll(async () => {
41
+ client = new ChronicleClient(ChronicleOptions.fromConnectionString(connectionString!, { discoveryPatterns: [], clientArtifactsProvider: artifacts }));
42
+ store = await client.getEventStore(storeName);
43
+ });
44
+ afterAll(() => client?.dispose());
45
+
46
+ // The sequence-number read does not narrow by route, so it also returns events routed through a definition.
47
+ const eventsFor = (id: string) => store.eventLog.getFromSequenceNumber(sequenceStart, id, [FundsDeposited]);
48
+
49
+ it('should register the definitions with the kernel', async () => {
50
+ const connection = (store as unknown as { _connection: { eventSources: { allEventSources(request: unknown): Promise<{ Data: Array<{ Name: string; Streams: Array<{ Name: string }> }> }> } } })._connection;
51
+ const response = await connection.eventSources.allEventSources({ EventStore: storeName });
52
+ const account = response.Data.find(_ => _.Name === 'Account');
53
+ should.exist(account);
54
+ account!.Streams.map(_ => _.Name).should.deep.equal(['Transactions']);
55
+ response.Data.some(_ => _.Name === 'Customer').should.equal(true);
56
+ });
57
+
58
+ it('should round-trip the event source and stream on appended events', async () => {
59
+ const id = randomUUID();
60
+ const result = await store.eventLog.append(id, deposit(10), { eventSource: AccountEventSource, eventStream: 'Transactions', streamId: 'tx-1' });
61
+ result.isSuccess.should.equal(true);
62
+ const [event] = await eventsFor(id);
63
+ event.context.eventSource!.should.equal('Account');
64
+ event.context.eventSourceType!.should.equal('Account');
65
+ event.context.eventStreamType!.should.equal('Transactions');
66
+ });
67
+
68
+ it('should leave the source unset for legacy appends', async () => {
69
+ const id = randomUUID();
70
+ await store.eventLog.append(id, deposit(1));
71
+ const [event] = await eventsFor(id);
72
+ should.equal(event.context.eventSource, undefined);
73
+ });
74
+
75
+ it('should reject a stale explicit scope on append-many and keep the batch atomic', async () => {
76
+ const id = randomUUID();
77
+ await store.eventLog.appendMany(id, [deposit(1), deposit(2)], { eventSource: 'Account' });
78
+ const stale = { sequenceNumber: 0n, eventSourceId: true };
79
+ const result = await store.eventLog.appendMany(id, [deposit(3)], { eventSource: 'Account', concurrencyScope: stale }).catch(error => error);
80
+ const events = await eventsFor(id);
81
+ events.should.have.length(2);
82
+ (result instanceof Error || result.some((appended: { isSuccess: boolean }) => !appended.isSuccess)).should.equal(true);
83
+ });
84
+
85
+ it('should route a mixed-source batch per event', async () => {
86
+ const account = randomUUID(); const customer = randomUUID();
87
+ await store.eventLog.appendMany([
88
+ { eventSourceId: account, event: deposit(1) },
89
+ { eventSourceId: customer, event: deposit(2), eventSource: CustomerEventSource }
90
+ ], { eventSource: AccountEventSource });
91
+ const [accountEvent] = await eventsFor(account);
92
+ const [customerEvent] = await eventsFor(customer);
93
+ accountEvent.context.eventSource!.should.equal('Account');
94
+ customerEvent.context.eventSource!.should.equal('Customer');
95
+ });
96
+
97
+ it('should guard a shared event source id once and reject differing guards without writing', async () => {
98
+ const id = randomUUID();
99
+ const ledger = (streamId: string) => ({ eventSourceId: id, event: deposit(1), eventSource: LedgerEventSource, eventStream: 'Monthly', eventStreamId: streamId });
100
+ const shared = await store.eventLog.appendMany([ledger('2026-01'), ledger('2026-01')]);
101
+ shared.every(_ => _.isSuccess).should.equal(true);
102
+ const error = await store.eventLog.appendMany([ledger('2026-01'), ledger('2026-02')]).catch(e => e as Error);
103
+ (error instanceof Error).should.equal(true);
104
+ (await eventsFor(id)).should.have.length(2);
105
+ });
106
+ });
@@ -0,0 +1,190 @@
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 { beforeEach, chai, describe, it, vi } from 'vitest';
5
+ import type { ChronicleConnection } from '../../connection/index.js';
6
+ import { EventSequence, EventSequenceId } from '../../eventSequences/index.js';
7
+ import { eventType } from '../../events/index.js';
8
+ import type { IUnitOfWorkManager } from '../../transactions/index.js';
9
+ import {
10
+ ConcurrencyDimensions, eventSource, eventStream, EventSources,
11
+ EventRoutingContradictsEventSource, EventStreamDoesNotBelongToEventSource, EventStreamRequiresEventSource, UnknownEventSource
12
+ } from '../index.js';
13
+ import type { IClientArtifactsProvider } from '../../artifacts/index.js';
14
+
15
+ const should = chai.should();
16
+ class Deposited { constructor(readonly amount = 1) {} }
17
+ eventType('DepositedToAccount')(Deposited);
18
+
19
+ @eventSource({ concurrency: ConcurrencyDimensions.eventSourceId })
20
+ @eventStream('Transactions', { concurrency: ConcurrencyDimensions.eventSourceId | ConcurrencyDimensions.eventStreamType })
21
+ class AccountEventSource {}
22
+
23
+ @eventSource({ name: 'Customer' })
24
+ @eventStream('Profile')
25
+ class CustomerEventSource {}
26
+
27
+ @eventSource({ name: 'Plain' })
28
+ class PlainEventSource {}
29
+
30
+ async function create(tail = 7n) {
31
+ const append = vi.fn().mockResolvedValue({ Response: { SequenceNumber: 0n } });
32
+ const appendManyForEventSources = vi.fn().mockResolvedValue({ Response: { SequenceNumbers: [0n, 1n] } });
33
+ const tailSequenceNumber = vi.fn().mockResolvedValue({ Data: { SequenceNumber: tail } });
34
+ const connection = { eventSequences: { append, appendManyForEventSources, tailSequenceNumber } } as unknown as ChronicleConnection;
35
+ const sources = new EventSources('store', connection, { eventSources: [AccountEventSource, CustomerEventSource, PlainEventSource] } as unknown as IClientArtifactsProvider);
36
+ await sources.discover();
37
+ return { sequence: new EventSequence(EventSequenceId.eventLog, 'store', 'ns', connection, {} as IUnitOfWorkManager, undefined, undefined, sources), append, appendManyForEventSources, tailSequenceNumber };
38
+ }
39
+
40
+ describe('when appending through a definition and stream', () => {
41
+ let request: any; let tailRequest: any;
42
+ beforeEach(async () => {
43
+ const c = await create();
44
+ await c.sequence.append('acc-1', new Deposited(), { eventSource: AccountEventSource, eventStream: 'Transactions', streamId: 'tx-1' });
45
+ request = c.append.mock.calls[0][0];
46
+ tailRequest = c.tailSequenceNumber.mock.calls[0][0];
47
+ });
48
+ it('should set the wire event source key and routing', () => {
49
+ request.EventSource.should.equal('Account');
50
+ request.EventSourceType.should.equal('Account');
51
+ request.EventStreamType.should.equal('Transactions');
52
+ request.EventStreamId.should.equal('tx-1');
53
+ });
54
+ it('should derive the scope from the stream dimensions', () => {
55
+ request.ConcurrencyScope.SequenceNumber.should.equal(7n);
56
+ request.ConcurrencyScope.EventSourceId.should.equal(true);
57
+ request.ConcurrencyScope.EventStreamType.should.equal('Transactions');
58
+ request.ConcurrencyScope.EventSourceType.should.equal('');
59
+ });
60
+ it('should read the tail with exactly the scoped dimensions', () => {
61
+ tailRequest.EventSourceId.should.equal('acc-1');
62
+ tailRequest.EventStreamType.should.equal('Transactions');
63
+ tailRequest.EventSourceType.should.equal('');
64
+ });
65
+ });
66
+
67
+ describe('when appending through a definition by name without a stream', () => {
68
+ it('should use the source dimensions and leave the stream to the kernel', async () => {
69
+ const c = await create();
70
+ await c.sequence.append('acc-1', new Deposited(), { eventSource: 'Account' });
71
+ const request = c.append.mock.calls[0][0];
72
+ request.EventSource.should.equal('Account');
73
+ should.equal(request.EventStreamType, undefined);
74
+ request.ConcurrencyScope.EventSourceId.should.equal(true);
75
+ request.ConcurrencyScope.EventStreamType.should.equal('');
76
+ });
77
+ it('should expect no matching event when the scope is empty', async () => {
78
+ const c = await create(18446744073709551615n);
79
+ await c.sequence.append('acc-1', new Deposited(), { eventSource: 'Account' });
80
+ c.append.mock.calls[0][0].ConcurrencyScope.ExpectsNoMatchingEvent.should.equal(true);
81
+ });
82
+ });
83
+
84
+ describe('when an explicit concurrency scope is given', () => {
85
+ it('should win over the definition and not read the tail', async () => {
86
+ const c = await create();
87
+ await c.sequence.append('acc-1', new Deposited(), { eventSource: 'Account', concurrencyScope: { sequenceNumber: 3n } });
88
+ c.append.mock.calls[0][0].ConcurrencyScope.SequenceNumber.should.equal(3n);
89
+ c.tailSequenceNumber.mock.calls.length.should.equal(0);
90
+ });
91
+ });
92
+
93
+ describe('when the definition declares no concurrency', () => {
94
+ it('should not add a scope', async () => {
95
+ const c = await create();
96
+ await c.sequence.append('c-1', new Deposited(), { eventSource: 'Plain' });
97
+ const request = c.append.mock.calls[0][0];
98
+ request.EventSource.should.equal('Plain');
99
+ request.ConcurrencyScope.SequenceNumber.should.equal(18446744073709551615n);
100
+ c.tailSequenceNumber.mock.calls.length.should.equal(0);
101
+ });
102
+ });
103
+
104
+ describe('when appending without a definition', () => {
105
+ it('should keep legacy behavior and send no event source key', async () => {
106
+ const c = await create();
107
+ await c.sequence.append('acc-1', new Deposited(), { sourceType: 'Legacy' });
108
+ const request = c.append.mock.calls[0][0];
109
+ should.equal(request.EventSource, undefined);
110
+ request.EventSourceType.should.equal('Legacy');
111
+ });
112
+ });
113
+
114
+ describe('when routing is invalid', () => {
115
+ it('should reject an unknown source', async () => {
116
+ const c = await create();
117
+ let error: unknown; await c.sequence.append('a', new Deposited(), { eventSource: 'Ghost' }).catch(e => error = e);
118
+ (error as Error).should.be.instanceOf(UnknownEventSource);
119
+ c.append.mock.calls.length.should.equal(0);
120
+ });
121
+ it('should reject an undeclared stream', async () => {
122
+ const c = await create();
123
+ let error: unknown; await c.sequence.append('a', new Deposited(), { eventSource: 'Account', eventStream: 'Profile' }).catch(e => error = e);
124
+ (error as Error).should.be.instanceOf(EventStreamDoesNotBelongToEventSource);
125
+ });
126
+ it('should reject a stream without a source', async () => {
127
+ const c = await create();
128
+ let error: unknown; await c.sequence.append('a', new Deposited(), { eventStream: 'Transactions' }).catch(e => error = e);
129
+ (error as Error).should.be.instanceOf(EventStreamRequiresEventSource);
130
+ });
131
+ it('should reject a contradicting explicit source type or stream type', async () => {
132
+ const c = await create();
133
+ const errors: unknown[] = [];
134
+ await c.sequence.append('a', new Deposited(), { eventSource: 'Account', sourceType: 'Other' }).catch(e => errors.push(e));
135
+ await c.sequence.append('a', new Deposited(), { eventSource: 'Account', eventStream: 'Transactions', streamType: 'Other' }).catch(e => errors.push(e));
136
+ errors.every(e => e instanceof EventRoutingContradictsEventSource).should.equal(true);
137
+ errors.should.have.length(2);
138
+ });
139
+ it('should accept explicit values that agree', async () => {
140
+ const c = await create();
141
+ await c.sequence.append('a', new Deposited(), { eventSource: 'Account', eventStream: 'Transactions', sourceType: 'Account', streamType: 'Transactions' });
142
+ c.append.mock.calls.length.should.equal(1);
143
+ });
144
+ });
145
+
146
+ describe('when appending many through a definition', () => {
147
+ it('should route every event and derive one scope per event source id', async () => {
148
+ const c = await create();
149
+ await c.sequence.appendMany('acc-1', [new Deposited(), new Deposited()], { eventSource: 'Account', eventStream: 'Transactions' });
150
+ const request = c.appendManyForEventSources.mock.calls[0][0];
151
+ request.Events.map((e: any) => e.EventSource).should.deep.equal(['Account', 'Account']);
152
+ request.Events.map((e: any) => e.EventStreamType).should.deep.equal(['Transactions', 'Transactions']);
153
+ request.ConcurrencyScopes.should.have.length(1);
154
+ request.ConcurrencyScopes[0].EventSourceId.should.equal('acc-1');
155
+ request.ConcurrencyScopes[0].Scope.SequenceNumber.should.equal(7n);
156
+ });
157
+ it('should let per-event routing override shared options in a mixed batch without inheriting the stream', async () => {
158
+ const c = await create();
159
+ await c.sequence.appendMany([
160
+ { eventSourceId: 'acc-1', event: new Deposited() },
161
+ { eventSourceId: 'cust-1', event: new Deposited(), eventSource: CustomerEventSource },
162
+ { eventSourceId: 'cust-2', event: new Deposited(), eventSource: 'Customer', eventStream: 'Profile' },
163
+ { eventSourceId: 'old-1', event: new Deposited(), eventSource: 'Plain' }
164
+ ], { eventSource: 'Account', eventStream: 'Transactions' });
165
+ const events = c.appendManyForEventSources.mock.calls[0][0].Events;
166
+ events.map((e: any) => e.EventSource).should.deep.equal(['Account', 'Customer', 'Customer', 'Plain']);
167
+ events.map((e: any) => e.EventStreamType).should.deep.equal(['Transactions', undefined, 'Profile', undefined]);
168
+ events.map((e: any) => e.EventSourceType).should.deep.equal(['Account', 'Customer', 'Customer', 'Plain']);
169
+ });
170
+ it('should let an explicit per-id scope win and a shared scope win over derivation', async () => {
171
+ const c = await create();
172
+ await c.sequence.appendMany([
173
+ { eventSourceId: 'acc-1', event: new Deposited() },
174
+ { eventSourceId: 'acc-2', event: new Deposited() }
175
+ ], { eventSource: 'Account', concurrencyScopes: { 'acc-1': { sequenceNumber: 1n } } });
176
+ const scopes = c.appendManyForEventSources.mock.calls[0][0].ConcurrencyScopes;
177
+ scopes.find((s: any) => s.EventSourceId === 'acc-1').Scope.SequenceNumber.should.equal(1n);
178
+ scopes.find((s: any) => s.EventSourceId === 'acc-2').Scope.SequenceNumber.should.equal(7n);
179
+ });
180
+ it('should send nothing when any event routing is invalid (atomic)', async () => {
181
+ const c = await create();
182
+ let error: unknown;
183
+ await c.sequence.appendMany([
184
+ { eventSourceId: 'a', event: new Deposited() },
185
+ { eventSourceId: 'b', event: new Deposited(), eventSource: 'Ghost' }
186
+ ], { eventSource: 'Account' }).catch(e => error = e);
187
+ (error as Error).should.be.instanceOf(UnknownEventSource);
188
+ c.appendManyForEventSources.mock.calls.length.should.equal(0);
189
+ });
190
+ });
@@ -0,0 +1,139 @@
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 { Guid } from '@cratis/fundamentals';
5
+ import { chai, describe, it, vi } from 'vitest';
6
+ import type { ChronicleConnection } from '../../connection/index.js';
7
+ import { EventSequence, EventSequenceId } from '../../eventSequences/index.js';
8
+ import { eventType } from '../../events/index.js';
9
+ import { UnitOfWork } from '../../transactions/UnitOfWork.js';
10
+ import type { IUnitOfWorkManager } from '../../transactions/index.js';
11
+ import { ConcurrencyDimensions, eventSource, eventStream, EventSources } from '../index.js';
12
+ import type { IClientArtifactsProvider } from '../../artifacts/index.js';
13
+ import type { IEventStore } from '../../IEventStore.js';
14
+
15
+ chai.should();
16
+ class Booked { constructor(readonly amount = 1) {} }
17
+ eventType('BookedToLedger')(Booked);
18
+
19
+ const sourceAndStreamId = ConcurrencyDimensions.eventSourceId | ConcurrencyDimensions.eventStreamType | ConcurrencyDimensions.eventStreamId;
20
+
21
+ @eventSource({ name: 'Ledger' })
22
+ @eventStream('Monthly', { concurrency: sourceAndStreamId })
23
+ class LedgerEventSource {}
24
+
25
+ @eventSource({ name: 'Archive', concurrency: ConcurrencyDimensions.eventSourceId })
26
+ class ArchiveEventSource {}
27
+
28
+ @eventSource({ name: 'Mirror' })
29
+ @eventStream('Monthly', { concurrency: sourceAndStreamId })
30
+ class MirrorEventSource {}
31
+
32
+ @eventSource({ name: 'Open' })
33
+ class OpenEventSource {}
34
+
35
+ @eventSource({ name: 'Routed', concurrency: ConcurrencyDimensions.eventSourceId })
36
+ class RoutedEventSource {}
37
+
38
+ async function create() {
39
+ const appendManyForEventSources = vi.fn().mockResolvedValue({ Response: { SequenceNumbers: [0n, 1n] } });
40
+ const tailSequenceNumber = vi.fn().mockResolvedValue({ Data: { SequenceNumber: 5n } });
41
+ const connection = { eventSequences: { appendManyForEventSources, tailSequenceNumber } } as unknown as ChronicleConnection;
42
+ const sources = new EventSources('store', connection, {
43
+ eventSources: [LedgerEventSource, ArchiveEventSource, MirrorEventSource, OpenEventSource, RoutedEventSource]
44
+ } as unknown as IClientArtifactsProvider);
45
+ await sources.discover();
46
+ const sequence = new EventSequence(EventSequenceId.eventLog, 'store', 'ns', connection, {} as IUnitOfWorkManager, undefined, undefined, sources);
47
+ return { sequence, appendManyForEventSources, tailSequenceNumber, sources };
48
+ }
49
+
50
+ const ledger = (streamId: string, eventSourceId = 'a-1') =>
51
+ ({ eventSourceId, event: new Booked(), eventSource: LedgerEventSource, eventStream: 'Monthly', eventStreamId: streamId });
52
+
53
+ describe('when a batch needs differing stream id guards for one event source id', () => {
54
+ it('should throw before any wire write', async () => {
55
+ const c = await create();
56
+ let error: Error | undefined;
57
+ try { await c.sequence.appendMany([ledger('2026-01'), ledger('2026-02')]); } catch (e) { error = e as Error; }
58
+ (error?.message ?? '').should.contain("'a-1'").and.contain('separate batches');
59
+ c.appendManyForEventSources.mock.calls.length.should.equal(0);
60
+ c.tailSequenceNumber.mock.calls.length.should.equal(0);
61
+ });
62
+ });
63
+
64
+ describe('when a batch has the same stream id guard repeated', () => {
65
+ it('should send one shared scope for the id', async () => {
66
+ const c = await create();
67
+ await c.sequence.appendMany([ledger('2026-01'), ledger('2026-01')]);
68
+ const scopes = c.appendManyForEventSources.mock.calls[0][0].ConcurrencyScopes;
69
+ scopes.length.should.equal(1);
70
+ scopes[0].Scope.EventStreamId.should.equal('2026-01');
71
+ });
72
+ });
73
+
74
+ describe('when a batch needs differing guards from different definitions for one id', () => {
75
+ it('should throw before any wire write', async () => {
76
+ const c = await create();
77
+ let error: Error | undefined;
78
+ try {
79
+ await c.sequence.appendMany([ledger('2026-01'), { eventSourceId: 'a-1', event: new Booked(), eventSource: ArchiveEventSource }]);
80
+ } catch (e) { error = e as Error; }
81
+ (error?.message ?? '').should.contain('differing concurrency guards');
82
+ c.appendManyForEventSources.mock.calls.length.should.equal(0);
83
+ });
84
+ });
85
+
86
+ describe('when equivalent guards differ only in values the dimensions do not select', () => {
87
+ it('should share one conservative scope', async () => {
88
+ const c = await create();
89
+ await c.sequence.appendMany([
90
+ { eventSourceId: 'a-1', event: new Booked(), eventSource: ArchiveEventSource, eventStreamId: 'x' },
91
+ { eventSourceId: 'a-1', event: new Booked(), eventSource: ArchiveEventSource, eventStreamId: 'y' }
92
+ ]);
93
+ const scopes = c.appendManyForEventSources.mock.calls[0][0].ConcurrencyScopes;
94
+ scopes.length.should.equal(1);
95
+ scopes[0].Scope.EventSourceId.should.equal(true);
96
+ });
97
+ });
98
+
99
+ describe('when an unguarded definition event precedes a guarded one for the same id', () => {
100
+ it('should still guard the id', async () => {
101
+ const c = await create();
102
+ await c.sequence.appendMany([
103
+ { eventSourceId: 'a-1', event: new Booked(), eventSource: OpenEventSource },
104
+ { eventSourceId: 'a-1', event: new Booked(), eventSource: ArchiveEventSource }
105
+ ]);
106
+ const scopes = c.appendManyForEventSources.mock.calls[0][0].ConcurrencyScopes;
107
+ scopes[0].Scope.EventSourceId.should.equal(true);
108
+ scopes[0].Scope.SequenceNumber.should.equal(5n);
109
+ });
110
+ });
111
+
112
+ describe('when an explicit broader scope exists for the id', () => {
113
+ it('should send it and not require agreement between the derived guards', async () => {
114
+ const c = await create();
115
+ await c.sequence.appendMany([ledger('2026-01'), ledger('2026-02')],
116
+ { concurrencyScopes: { 'a-1': { sequenceNumber: 3n, eventSourceId: true } } });
117
+ const scopes = c.appendManyForEventSources.mock.calls[0][0].ConcurrencyScopes;
118
+ scopes.length.should.equal(1);
119
+ scopes[0].Scope.SequenceNumber.should.equal(3n);
120
+ c.tailSequenceNumber.mock.calls.length.should.equal(0);
121
+ });
122
+ });
123
+
124
+ describe('when a unit of work holds a conflicting group after a valid group', () => {
125
+ it('should reject on commit before any group is written', async () => {
126
+ const c = await create();
127
+ const appendMany = vi.fn().mockResolvedValue([]);
128
+ const store = { eventSources: c.sources, getEventSequence: () => ({ appendMany }) } as unknown as IEventStore;
129
+ const unitOfWork = new UnitOfWork(Guid.create(), () => {}, store);
130
+ unitOfWork.addEvent(EventSequenceId.eventLog, 'ok-1', new Booked(), { eventSource: ArchiveEventSource });
131
+ const other = new EventSequenceId('other');
132
+ unitOfWork.addEvent(other, 'a-1', new Booked(), { eventSource: LedgerEventSource, eventStream: 'Monthly' });
133
+ unitOfWork.addEvent(other, 'a-1', new Booked(), { eventSource: ArchiveEventSource });
134
+ let error: Error | undefined;
135
+ try { await unitOfWork.commit(); } catch (e) { error = e as Error; }
136
+ (error?.message ?? '').should.contain('differing concurrency guards');
137
+ appendMany.mock.calls.length.should.equal(0);
138
+ });
139
+ });
@@ -0,0 +1,94 @@
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 { beforeEach, chai, describe, it, vi } from 'vitest';
5
+ import type { Constructor } from '@cratis/fundamentals';
6
+ import type { ChronicleConnection } from '../../connection/index.js';
7
+ import { ConcurrencyDimensions, eventSource, eventStream, EventSources } from '../index.js';
8
+ import { DuplicateEventSourceName } from '../DuplicateEventSourceName.js';
9
+ import { DuplicateEventStreamName } from '../DuplicateEventStreamName.js';
10
+ import { UnknownEventSource } from '../UnknownEventSource.js';
11
+ import type { IClientArtifactsProvider } from '../../artifacts/index.js';
12
+
13
+ const should = chai.should();
14
+
15
+ @eventSource({ description: 'An account', concurrency: ConcurrencyDimensions.eventSourceId })
16
+ @eventStream('Transactions', { description: 'Money moves', concurrency: ConcurrencyDimensions.eventSourceId | ConcurrencyDimensions.eventStreamType })
17
+ @eventStream('Statements')
18
+ class AccountEventSource {}
19
+
20
+ @eventSource({ name: 'Same' }) class First {}
21
+ @eventSource({ name: 'Same' }) class Second {}
22
+ @eventSource({ name: 'Dup' })
23
+ @eventStream('S') @eventStream('S')
24
+ class DuplicateStreams {}
25
+
26
+ function artifacts(...types: Constructor[]): IClientArtifactsProvider {
27
+ return { eventSources: types } as unknown as IClientArtifactsProvider;
28
+ }
29
+
30
+ function create(...types: Constructor[]) {
31
+ const registerEventSources = vi.fn().mockResolvedValue({ IsSuccess: true, ValidationResults: [], ExceptionMessages: [] });
32
+ const connection = { eventSources: { registerEventSources } } as unknown as ChronicleConnection;
33
+ return { sources: new EventSources('store', connection, artifacts(...types)), registerEventSources };
34
+ }
35
+
36
+ describe('when discovering a decorated event source', () => {
37
+ let sources: EventSources;
38
+ beforeEach(async () => {
39
+ ({ sources } = create(AccountEventSource));
40
+ await sources.discover();
41
+ });
42
+ it('should default the name by trimming the EventSource suffix', () => sources.all[0].name.should.equal('Account'));
43
+ it('should keep description and concurrency', () => {
44
+ sources.all[0].description.should.equal('An account');
45
+ sources.all[0].concurrency.should.equal(ConcurrencyDimensions.eventSourceId);
46
+ });
47
+ it('should keep streams in declaration order with their own dimensions', () => {
48
+ sources.all[0].streams.map(_ => _.name).should.deep.equal(['Transactions', 'Statements']);
49
+ sources.all[0].streams[0].concurrency.should.equal(5);
50
+ sources.all[0].streams[1].concurrency.should.equal(0);
51
+ });
52
+ it('should resolve by class and by name', () => {
53
+ sources.getFor(AccountEventSource).should.equal(sources.getFor('Account'));
54
+ });
55
+ it('should reject an unknown source', () => {
56
+ (() => sources.getFor('Nope')).should.throw(UnknownEventSource);
57
+ });
58
+ });
59
+
60
+ describe('when discovering duplicate names', () => {
61
+ it('should reject duplicate source names', async () => {
62
+ const { sources } = create(First, Second);
63
+ let error: unknown;
64
+ await sources.discover().catch(e => error = e);
65
+ (error as Error).should.be.instanceOf(DuplicateEventSourceName);
66
+ });
67
+ it('should reject duplicate stream names', async () => {
68
+ const { sources } = create(DuplicateStreams);
69
+ let error: unknown;
70
+ await sources.discover().catch(e => error = e);
71
+ (error as Error).should.be.instanceOf(DuplicateEventStreamName);
72
+ });
73
+ });
74
+
75
+ describe('when registering', () => {
76
+ it('should send the definitions with client ownership and wire dimension flags', async () => {
77
+ const { sources, registerEventSources } = create(AccountEventSource);
78
+ await sources.discover();
79
+ await sources.register();
80
+ const request = registerEventSources.mock.calls[0][0];
81
+ request.EventStore.should.equal('store');
82
+ request.Sources.should.have.length(1);
83
+ request.Sources[0].Name.should.equal('Account');
84
+ request.Sources[0].Owner.should.equal(1);
85
+ request.Sources[0].Concurrency.should.equal(1);
86
+ request.Sources[0].Streams[0].should.deep.equal({ Name: 'Transactions', Description: 'Money moves', Concurrency: 5 });
87
+ });
88
+ it('should not call the kernel when there are no definitions', async () => {
89
+ const { sources, registerEventSources } = create();
90
+ await sources.discover();
91
+ await sources.register();
92
+ should.equal(registerEventSources.mock.calls.length, 0);
93
+ });
94
+ });
@@ -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
+ export { ConcurrencyDimensions } from './ConcurrencyDimensions.js';
5
+ export type { ConcurrencyDimensionFlags } from './ConcurrencyDimensions.js';
6
+ export type { EventSourceDefinition } from './EventSourceDefinition.js';
7
+ export type { EventStreamDefinition } from './EventStreamDefinition.js';
8
+ export { eventSource, eventStream, getEventSourceMetadata, getEventStreamsFor } from './eventSource.js';
9
+ export type { EventSourceOptions, EventStreamOptions, EventSourceMetadata, EventStreamMetadata } from './eventSource.js';
10
+ export type { IEventSources } from './IEventSources.js';
11
+ export { EventSources } from './EventSources.js';
12
+ export { ResolvedEventRouting } from './ResolvedEventRouting.js';
13
+ export type { ExplicitRouting } from './ResolvedEventRouting.js';
14
+ export { UnknownEventSource } from './UnknownEventSource.js';
15
+ export { DuplicateEventSourceName } from './DuplicateEventSourceName.js';
16
+ export { DuplicateEventStreamName } from './DuplicateEventStreamName.js';
17
+ export { EventStreamDoesNotBelongToEventSource } from './EventStreamDoesNotBelongToEventSource.js';
18
+ export { EventRoutingContradictsEventSource } from './EventRoutingContradictsEventSource.js';
19
+ export { EventStreamRequiresEventSource } from './EventStreamRequiresEventSource.js';
@@ -31,6 +31,12 @@ export interface EventContext {
31
31
  /** The stream identifier returned by the kernel. */
32
32
  readonly eventStreamId?: string;
33
33
 
34
+ /**
35
+ * The registered event source definition the event was appended through.
36
+ * Undefined for events appended without a definition, including events stored before event sources existed.
37
+ */
38
+ readonly eventSource?: string;
39
+
34
40
  /** The compliance subject returned by the kernel. */
35
41
  readonly subject?: string;
36
42
 
@@ -7,6 +7,8 @@ import type { AppendedEventResponse } from '@cratis/chronicle.contracts';
7
7
  export type WireEventContext = Omit<NonNullable<AppendedEventResponse['Context']>, 'ObservationState'> & {
8
8
  /** Numeric wire value shared by the generated observation-state enums. */
9
9
  ObservationState: number;
10
+ /** The registered event source name; empty or absent when the event was not appended through a definition. */
11
+ EventSource?: string;
10
12
  /** Store metadata is carried by observer deliveries, but not every read response. */
11
13
  EventStore?: string;
12
14
  /** Namespace metadata is carried by observer deliveries, but not every read response. */
@@ -0,0 +1,22 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { describe, it, chai } from 'vitest';
5
+ import { toClientEventContext } from '../toClientEventContext.js';
6
+ import type { WireEventContext } from '../WireEventContext.js';
7
+
8
+ const should = chai.should();
9
+ const wire = (extra: Partial<WireEventContext>): WireEventContext => ({
10
+ EventSourceId: 's', SequenceNumber: 1n, ObservationState: 0, Tags: [], Causation: [],
11
+ EventType: { Id: 't', Generation: 1, Tombstone: false }, Occurred: { Value: '2025-01-01T00:00:00.000Z' }, ...extra
12
+ } as unknown as WireEventContext);
13
+
14
+ describe('when converting a kernel event context', () => {
15
+ it('should carry the registered event source', () => {
16
+ toClientEventContext(wire({ EventSource: 'Account' })).eventSource!.should.equal('Account');
17
+ });
18
+ it('should leave the source unset when the event was not appended through a definition', () => {
19
+ should.equal(toClientEventContext(wire({ EventSource: '' })).eventSource, undefined);
20
+ should.equal(toClientEventContext(wire({})).eventSource, undefined);
21
+ });
22
+ });
@@ -20,6 +20,7 @@ export function toClientEventContext(context: WireEventContext): EventContext {
20
20
  eventSourceType: context.EventSourceType,
21
21
  eventStreamType: context.EventStreamType,
22
22
  eventStreamId: context.EventStreamId,
23
+ eventSource: context.EventSource ? context.EventSource : undefined,
23
24
  subject: context.Subject,
24
25
  hash: context.Hash,
25
26
  observationState: context.ObservationState,
@@ -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,