@cratis/chronicle 6.48.0 → 6.50.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.
- package/EventStore.ts +8 -2
- package/IEventStore.ts +8 -0
- package/artifacts/DefaultClientArtifactsProvider.ts +5 -0
- package/artifacts/IClientArtifactsProvider.ts +3 -0
- package/connection/ChronicleConnection.ts +6 -0
- package/connection/ChronicleServices.ts +2 -0
- package/dist/EventStore.d.ts +2 -0
- package/dist/EventStore.d.ts.map +1 -1
- package/dist/EventStore.js +7 -2
- package/dist/EventStore.js.map +1 -1
- package/dist/IEventStore.d.ts +7 -0
- package/dist/IEventStore.d.ts.map +1 -1
- package/dist/artifacts/DefaultClientArtifactsProvider.d.ts +2 -0
- package/dist/artifacts/DefaultClientArtifactsProvider.d.ts.map +1 -1
- package/dist/artifacts/DefaultClientArtifactsProvider.js +4 -0
- package/dist/artifacts/DefaultClientArtifactsProvider.js.map +1 -1
- package/dist/artifacts/IClientArtifactsProvider.d.ts +2 -0
- package/dist/artifacts/IClientArtifactsProvider.d.ts.map +1 -1
- package/dist/connection/ChronicleConnection.d.ts +1 -0
- package/dist/connection/ChronicleConnection.d.ts.map +1 -1
- package/dist/connection/ChronicleConnection.js +5 -1
- package/dist/connection/ChronicleConnection.js.map +1 -1
- package/dist/connection/ChronicleServices.d.ts +2 -1
- package/dist/connection/ChronicleServices.d.ts.map +1 -1
- package/dist/eventSequences/AppendOptions.d.ts +22 -0
- package/dist/eventSequences/AppendOptions.d.ts.map +1 -1
- package/dist/eventSequences/EventForEventSourceId.d.ts +17 -0
- package/dist/eventSequences/EventForEventSourceId.d.ts.map +1 -1
- package/dist/eventSequences/EventLog.d.ts +2 -1
- package/dist/eventSequences/EventLog.d.ts.map +1 -1
- package/dist/eventSequences/EventLog.js +2 -2
- package/dist/eventSequences/EventLog.js.map +1 -1
- package/dist/eventSequences/EventSequence.d.ts +4 -1
- package/dist/eventSequences/EventSequence.d.ts.map +1 -1
- package/dist/eventSequences/EventSequence.js +59 -13
- package/dist/eventSequences/EventSequence.js.map +1 -1
- package/dist/eventSequences/ITransactionalEventSequence.d.ts +5 -2
- package/dist/eventSequences/ITransactionalEventSequence.d.ts.map +1 -1
- package/dist/eventSequences/NamedTagsWithRegisteredEventSourceNotSupported.d.ts +5 -0
- package/dist/eventSequences/NamedTagsWithRegisteredEventSourceNotSupported.d.ts.map +1 -0
- package/dist/eventSequences/NamedTagsWithRegisteredEventSourceNotSupported.js +12 -0
- package/dist/eventSequences/NamedTagsWithRegisteredEventSourceNotSupported.js.map +1 -0
- package/dist/eventSequences/TransactionalEventSequence.d.ts +3 -2
- package/dist/eventSequences/TransactionalEventSequence.d.ts.map +1 -1
- package/dist/eventSequences/TransactionalEventSequence.js +7 -4
- package/dist/eventSequences/TransactionalEventSequence.js.map +1 -1
- package/dist/eventSequences/createAppendNotification.d.ts +2 -1
- package/dist/eventSequences/createAppendNotification.d.ts.map +1 -1
- package/dist/eventSequences/createAppendNotification.js +3 -2
- package/dist/eventSequences/createAppendNotification.js.map +1 -1
- package/dist/eventSequences/ensureNamedTagBatchIsSupported.d.ts +3 -0
- package/dist/eventSequences/ensureNamedTagBatchIsSupported.d.ts.map +1 -0
- package/dist/eventSequences/ensureNamedTagBatchIsSupported.js +10 -0
- package/dist/eventSequences/ensureNamedTagBatchIsSupported.js.map +1 -0
- package/dist/eventSequences/index.d.ts +1 -0
- package/dist/eventSequences/index.d.ts.map +1 -1
- package/dist/eventSequences/index.js +1 -0
- package/dist/eventSequences/index.js.map +1 -1
- package/dist/eventSequences/prepareBatchAppend.d.ts +1 -0
- package/dist/eventSequences/prepareBatchAppend.d.ts.map +1 -1
- package/dist/eventSequences/prepareBatchAppend.js +4 -1
- package/dist/eventSequences/prepareBatchAppend.js.map +1 -1
- package/dist/eventSequences/prepareSingleAppend.d.ts +1 -0
- package/dist/eventSequences/prepareSingleAppend.d.ts.map +1 -1
- package/dist/eventSequences/prepareSingleAppend.js +3 -1
- package/dist/eventSequences/prepareSingleAppend.js.map +1 -1
- package/dist/eventSequences/resolveEventSourceRouting.d.ts +39 -0
- package/dist/eventSequences/resolveEventSourceRouting.d.ts.map +1 -0
- package/dist/eventSequences/resolveEventSourceRouting.js +92 -0
- package/dist/eventSequences/resolveEventSourceRouting.js.map +1 -0
- package/dist/eventSources/ConcurrencyDimensions.d.ts +19 -0
- package/dist/eventSources/ConcurrencyDimensions.d.ts.map +1 -0
- package/dist/eventSources/ConcurrencyDimensions.js +19 -0
- package/dist/eventSources/ConcurrencyDimensions.js.map +1 -0
- package/dist/eventSources/DuplicateEventSourceName.d.ts +6 -0
- package/dist/eventSources/DuplicateEventSourceName.d.ts.map +1 -0
- package/dist/eventSources/DuplicateEventSourceName.js +12 -0
- package/dist/eventSources/DuplicateEventSourceName.js.map +1 -0
- package/dist/eventSources/DuplicateEventStreamName.d.ts +7 -0
- package/dist/eventSources/DuplicateEventStreamName.d.ts.map +1 -0
- package/dist/eventSources/DuplicateEventStreamName.js +14 -0
- package/dist/eventSources/DuplicateEventStreamName.js.map +1 -0
- package/dist/eventSources/EventRoutingContradictsEventSource.d.ts +9 -0
- package/dist/eventSources/EventRoutingContradictsEventSource.d.ts.map +1 -0
- package/dist/eventSources/EventRoutingContradictsEventSource.js +18 -0
- package/dist/eventSources/EventRoutingContradictsEventSource.js.map +1 -0
- package/dist/eventSources/EventSourceDefinition.d.ts +20 -0
- package/dist/eventSources/EventSourceDefinition.d.ts.map +1 -0
- package/dist/eventSources/EventSourceDefinition.js +4 -0
- package/dist/eventSources/EventSourceDefinition.js.map +1 -0
- package/dist/eventSources/EventSources.d.ts +31 -0
- package/dist/eventSources/EventSources.d.ts.map +1 -0
- package/dist/eventSources/EventSources.js +90 -0
- package/dist/eventSources/EventSources.js.map +1 -0
- package/dist/eventSources/EventStreamDefinition.d.ts +13 -0
- package/dist/eventSources/EventStreamDefinition.d.ts.map +1 -0
- package/dist/eventSources/EventStreamDefinition.js +4 -0
- package/dist/eventSources/EventStreamDefinition.js.map +1 -0
- package/dist/eventSources/EventStreamDoesNotBelongToEventSource.d.ts +7 -0
- package/dist/eventSources/EventStreamDoesNotBelongToEventSource.d.ts.map +1 -0
- package/dist/eventSources/EventStreamDoesNotBelongToEventSource.js +14 -0
- package/dist/eventSources/EventStreamDoesNotBelongToEventSource.js.map +1 -0
- package/dist/eventSources/EventStreamRequiresEventSource.d.ts +6 -0
- package/dist/eventSources/EventStreamRequiresEventSource.d.ts.map +1 -0
- package/dist/eventSources/EventStreamRequiresEventSource.js +12 -0
- package/dist/eventSources/EventStreamRequiresEventSource.js.map +1 -0
- package/dist/eventSources/IEventSources.d.ts +27 -0
- package/dist/eventSources/IEventSources.d.ts.map +1 -0
- package/dist/eventSources/IEventSources.js +4 -0
- package/dist/eventSources/IEventSources.js.map +1 -0
- package/dist/eventSources/ResolvedEventRouting.d.ts +37 -0
- package/dist/eventSources/ResolvedEventRouting.d.ts.map +1 -0
- package/dist/eventSources/ResolvedEventRouting.js +60 -0
- package/dist/eventSources/ResolvedEventRouting.js.map +1 -0
- package/dist/eventSources/UnknownEventSource.d.ts +6 -0
- package/dist/eventSources/UnknownEventSource.d.ts.map +1 -0
- package/dist/eventSources/UnknownEventSource.js +12 -0
- package/dist/eventSources/UnknownEventSource.js.map +1 -0
- package/dist/eventSources/eventSource.d.ts +60 -0
- package/dist/eventSources/eventSource.d.ts.map +1 -0
- package/dist/eventSources/eventSource.js +72 -0
- package/dist/eventSources/eventSource.js.map +1 -0
- package/dist/eventSources/index.d.ts +17 -0
- package/dist/eventSources/index.d.ts.map +1 -0
- package/dist/eventSources/index.js +13 -0
- package/dist/eventSources/index.js.map +1 -0
- package/dist/events/EventContext.d.ts +12 -0
- package/dist/events/EventContext.d.ts.map +1 -1
- package/dist/events/InvalidNamedTag.d.ts +8 -0
- package/dist/events/InvalidNamedTag.d.ts.map +1 -0
- package/dist/events/InvalidNamedTag.js +13 -0
- package/dist/events/InvalidNamedTag.js.map +1 -0
- package/dist/events/NamedTag.d.ts +25 -0
- package/dist/events/NamedTag.d.ts.map +1 -0
- package/dist/events/NamedTag.js +39 -0
- package/dist/events/NamedTag.js.map +1 -0
- package/dist/events/WireEventContext.d.ts +2 -0
- package/dist/events/WireEventContext.d.ts.map +1 -1
- package/dist/events/index.d.ts +3 -0
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +3 -0
- package/dist/events/index.js.map +1 -1
- package/dist/events/mergeNamedTags.d.ts +13 -0
- package/dist/events/mergeNamedTags.d.ts.map +1 -0
- package/dist/events/mergeNamedTags.js +39 -0
- package/dist/events/mergeNamedTags.js.map +1 -0
- package/dist/events/toClientEventContext.d.ts.map +1 -1
- package/dist/events/toClientEventContext.js +4 -1
- package/dist/events/toClientEventContext.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/projections/eventContextPropertyExpression.d.ts.map +1 -1
- package/dist/projections/eventContextPropertyExpression.js +2 -0
- package/dist/projections/eventContextPropertyExpression.js.map +1 -1
- package/dist/testing/InProcessEventSequence.d.ts.map +1 -1
- package/dist/testing/InProcessEventSequence.js +17 -7
- package/dist/testing/InProcessEventSequence.js.map +1 -1
- package/dist/testing/ReactorScenario.d.ts.map +1 -1
- package/dist/testing/ReactorScenario.js +6 -0
- package/dist/testing/ReactorScenario.js.map +1 -1
- package/dist/testing/projections/ProjectionReadModelProcessor.d.ts.map +1 -1
- package/dist/testing/projections/ProjectionReadModelProcessor.js +2 -1
- package/dist/testing/projections/ProjectionReadModelProcessor.js.map +1 -1
- package/dist/testing/singleAppendRoute.d.ts +5 -0
- package/dist/testing/singleAppendRoute.d.ts.map +1 -1
- package/dist/testing/singleAppendRoute.js +12 -1
- package/dist/testing/singleAppendRoute.js.map +1 -1
- package/dist/transactions/IUnitOfWork.d.ts +4 -1
- package/dist/transactions/IUnitOfWork.d.ts.map +1 -1
- package/dist/transactions/TransactionalAppendOptions.d.ts +14 -0
- package/dist/transactions/TransactionalAppendOptions.d.ts.map +1 -0
- package/dist/transactions/TransactionalAppendOptions.js +4 -0
- package/dist/transactions/TransactionalAppendOptions.js.map +1 -0
- package/dist/transactions/TransactionalEventRouting.d.ts +12 -0
- package/dist/transactions/TransactionalEventRouting.d.ts.map +1 -0
- package/dist/transactions/TransactionalEventRouting.js +4 -0
- package/dist/transactions/TransactionalEventRouting.js.map +1 -0
- package/dist/transactions/UnitOfWork.d.ts +2 -1
- package/dist/transactions/UnitOfWork.d.ts.map +1 -1
- package/dist/transactions/UnitOfWork.js +17 -2
- package/dist/transactions/UnitOfWork.js.map +1 -1
- package/dist/transactions/index.d.ts +2 -0
- package/dist/transactions/index.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/DecoratorType.d.ts +3 -1
- package/dist/types/DecoratorType.d.ts.map +1 -1
- package/dist/types/DecoratorType.js +2 -0
- package/dist/types/DecoratorType.js.map +1 -1
- package/eventSequences/AppendOptions.ts +25 -0
- package/eventSequences/EventForEventSourceId.ts +20 -0
- package/eventSequences/EventLog.ts +4 -2
- package/eventSequences/EventSequence.ts +62 -13
- package/eventSequences/ITransactionalEventSequence.ts +5 -2
- package/eventSequences/NamedTagsWithRegisteredEventSourceNotSupported.ts +12 -0
- package/eventSequences/TransactionalEventSequence.ts +8 -4
- package/eventSequences/createAppendNotification.ts +4 -2
- package/eventSequences/ensureNamedTagBatchIsSupported.ts +11 -0
- package/eventSequences/for_EventSequence/against_a_kernel/when_appending_named_tags.integration.spec.ts +107 -0
- package/eventSequences/for_EventSequence/when_appending_named_tags/given.fixture.ts +38 -0
- package/eventSequences/for_EventSequence/when_appending_named_tags/with_a_batch.spec.ts +49 -0
- package/eventSequences/for_EventSequence/when_appending_named_tags/with_a_single_event.spec.ts +47 -0
- package/eventSequences/for_EventSequence/when_appending_named_tags/with_an_invalid_tag.spec.ts +36 -0
- package/eventSequences/for_EventSequence/when_appending_named_tags/with_registered_sources.spec.ts +74 -0
- package/eventSequences/for_TransactionalEventSequence/when_appending_many_with_named_tags.spec.ts +41 -0
- package/eventSequences/index.ts +1 -0
- package/eventSequences/prepareBatchAppend.ts +4 -1
- package/eventSequences/prepareSingleAppend.ts +3 -1
- package/eventSequences/resolveEventSourceRouting.ts +125 -0
- package/eventSources/ConcurrencyDimensions.ts +26 -0
- package/eventSources/DuplicateEventSourceName.ts +10 -0
- package/eventSources/DuplicateEventStreamName.ts +10 -0
- package/eventSources/EventRoutingContradictsEventSource.ts +10 -0
- package/eventSources/EventSourceDefinition.ts +27 -0
- package/eventSources/EventSources.ts +93 -0
- package/eventSources/EventStreamDefinition.ts +18 -0
- package/eventSources/EventStreamDoesNotBelongToEventSource.ts +10 -0
- package/eventSources/EventStreamRequiresEventSource.ts +10 -0
- package/eventSources/IEventSources.ts +33 -0
- package/eventSources/ResolvedEventRouting.ts +78 -0
- package/eventSources/UnknownEventSource.ts +10 -0
- package/eventSources/eventSource.ts +116 -0
- package/eventSources/for_EventSources/against_a_kernel/when_appending_through_registered_sources.integration.spec.ts +106 -0
- package/eventSources/for_EventSources/when_appending_through_a_definition.spec.ts +190 -0
- package/eventSources/for_EventSources/when_batch_requires_differing_guards.spec.ts +139 -0
- package/eventSources/for_EventSources/when_discovering_and_registering.spec.ts +94 -0
- package/eventSources/index.ts +19 -0
- package/events/EventContext.ts +14 -0
- package/events/InvalidNamedTag.ts +13 -0
- package/events/NamedTag.ts +44 -0
- package/events/WireEventContext.ts +2 -0
- package/events/for_EventContext/with_event_source.spec.ts +22 -0
- package/events/for_NamedTag/when_constructing/with_invalid_values.spec.ts +17 -0
- package/events/for_NamedTag/when_constructing/with_valid_values.spec.ts +19 -0
- package/events/for_mergeNamedTags/when_merging/with_event_and_call_tags.spec.ts +31 -0
- package/events/for_mergeNamedTags/when_merging/with_invalid_entries.spec.ts +17 -0
- package/events/for_toClientEventContext/when_converting_named_tags.spec.ts +31 -0
- package/events/index.ts +3 -0
- package/events/mergeNamedTags.ts +37 -0
- package/events/toClientEventContext.ts +4 -1
- package/for_EventStore/when_implementing_a_legacy_event_store.spec.ts +19 -0
- package/index.ts +3 -0
- package/package.json +6 -6
- package/projections/eventContextPropertyExpression.ts +3 -1
- package/testing/EventScenario.event-sources.spec.ts +34 -0
- package/testing/InProcessEventSequence.ts +17 -7
- package/testing/ReactorScenario.ts +6 -0
- package/testing/for_EventScenario/when_appending_named_tags/with_named_tags.spec.ts +48 -0
- package/testing/for_ReactorScenario/when_recording_side_effects/with_named_tags.spec.ts +119 -0
- package/testing/for_ReadModelScenario/when_seeding_with_options/with_named_tags.spec.ts +35 -0
- package/testing/projections/ProjectionReadModelProcessor.ts +2 -1
- package/testing/singleAppendRoute.ts +13 -1
- package/transactions/IUnitOfWork.ts +4 -1
- package/transactions/TransactionalAppendOptions.ts +17 -0
- package/transactions/TransactionalEventRouting.ts +16 -0
- package/transactions/UnitOfWork.ts +22 -2
- package/transactions/for_UnitOfWork/when_committing_events_with_event_source_routing.spec.ts +41 -0
- package/transactions/for_UnitOfWork/when_committing_events_with_named_tags.spec.ts +112 -0
- package/transactions/index.ts +2 -0
- package/types/DecoratorType.ts +4 -1
|
@@ -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';
|
package/events/EventContext.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { EventType } from './EventType.js';
|
|
5
5
|
import { CausationEntry } from './CausationEntry.js';
|
|
6
6
|
import { Tag } from './Tag.js';
|
|
7
|
+
import type { NamedTag } from './NamedTag.js';
|
|
7
8
|
import type { Identity } from '../identity/Identity.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -31,6 +32,12 @@ export interface EventContext {
|
|
|
31
32
|
/** The stream identifier returned by the kernel. */
|
|
32
33
|
readonly eventStreamId?: string;
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* The registered event source definition the event was appended through.
|
|
37
|
+
* Undefined for events appended without a definition, including events stored before event sources existed.
|
|
38
|
+
*/
|
|
39
|
+
readonly eventSource?: string;
|
|
40
|
+
|
|
34
41
|
/** The compliance subject returned by the kernel. */
|
|
35
42
|
readonly subject?: string;
|
|
36
43
|
|
|
@@ -57,4 +64,11 @@ export interface EventContext {
|
|
|
57
64
|
|
|
58
65
|
/** The tags the event carries. */
|
|
59
66
|
readonly tags: ReadonlyArray<Tag>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The structured named tags the event carries, in the order the kernel stored them.
|
|
70
|
+
* Always present on events read from or delivered by the kernel (empty when the event has none);
|
|
71
|
+
* optional so that contexts constructed by hand remain valid.
|
|
72
|
+
*/
|
|
73
|
+
readonly namedTags?: ReadonlyArray<NamedTag>;
|
|
60
74
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
/**
|
|
5
|
+
* The error that is thrown when a named tag has a blank name or a non-string value, or a named tag
|
|
6
|
+
* collection contains an entry that is not a named tag. Mirrors the .NET client's `InvalidNamedTag`.
|
|
7
|
+
*/
|
|
8
|
+
export class InvalidNamedTag extends Error {
|
|
9
|
+
constructor() {
|
|
10
|
+
super('Named tags require a nonblank name and a non-null value; named tag collections cannot contain null elements.');
|
|
11
|
+
this.name = 'InvalidNamedTag';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
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 { InvalidNamedTag } from './InvalidNamedTag.js';
|
|
5
|
+
|
|
6
|
+
// Matches .NET string.IsNullOrWhiteSpace, which also treats U+0085 and other Unicode white space as blank.
|
|
7
|
+
const blank = /^\p{White_Space}*$/u;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Associates a name with an exact, opaque event tag value. Names and values are compared exactly
|
|
11
|
+
* (ordinal and case-sensitive); one name may carry several values on the same event.
|
|
12
|
+
* Mirrors the .NET client's `NamedTag`.
|
|
13
|
+
*/
|
|
14
|
+
export class NamedTag {
|
|
15
|
+
/** The name of the tag. Never blank. */
|
|
16
|
+
readonly name: string;
|
|
17
|
+
|
|
18
|
+
/** The exact, opaque value of the tag. May be empty. */
|
|
19
|
+
readonly value: string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Initializes a new instance of {@link NamedTag}.
|
|
23
|
+
* @param name - The name. Must be a nonblank string.
|
|
24
|
+
* @param value - The opaque value. Must be a string; the empty string is allowed.
|
|
25
|
+
* @throws {@link InvalidNamedTag} when the name is blank or not a string, or the value is not a string.
|
|
26
|
+
*/
|
|
27
|
+
constructor(name: string, value: string) {
|
|
28
|
+
if (typeof name !== 'string' || blank.test(name) || typeof value !== 'string') {
|
|
29
|
+
throw new InvalidNamedTag();
|
|
30
|
+
}
|
|
31
|
+
this.name = name;
|
|
32
|
+
this.value = value;
|
|
33
|
+
Object.freeze(this);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Determines whether this tag has the same name and value as another, compared exactly.
|
|
38
|
+
* @param other - The tag to compare with.
|
|
39
|
+
* @returns True when both name and value are equal.
|
|
40
|
+
*/
|
|
41
|
+
equals(other: NamedTag): boolean {
|
|
42
|
+
return other instanceof NamedTag && other.name === this.name && other.value === this.value;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -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
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
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 { InvalidNamedTag } from '../../InvalidNamedTag.js';
|
|
6
|
+
import { NamedTag } from '../../NamedTag.js';
|
|
7
|
+
|
|
8
|
+
chai.should();
|
|
9
|
+
|
|
10
|
+
describe('when constructing a named tag with invalid values', () => {
|
|
11
|
+
it('should reject an empty name', () => (() => new NamedTag('', 'value')).should.throw(InvalidNamedTag));
|
|
12
|
+
it('should reject a whitespace name', () => (() => new NamedTag(' \t', 'value')).should.throw(InvalidNamedTag));
|
|
13
|
+
it('should reject a name of only next-line characters like .NET', () => (() => new NamedTag('\u0085', 'value')).should.throw(InvalidNamedTag));
|
|
14
|
+
it('should reject a missing name', () => (() => new NamedTag(undefined as unknown as string, 'value')).should.throw(InvalidNamedTag));
|
|
15
|
+
it('should reject a null value', () => (() => new NamedTag('name', null as unknown as string)).should.throw(InvalidNamedTag));
|
|
16
|
+
it('should reject a non-string value', () => (() => new NamedTag('name', 42 as unknown as string)).should.throw(InvalidNamedTag));
|
|
17
|
+
});
|
|
@@ -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 { NamedTag } from '../../NamedTag.js';
|
|
6
|
+
|
|
7
|
+
chai.should();
|
|
8
|
+
|
|
9
|
+
describe('when constructing a named tag with a name and an empty value', () => {
|
|
10
|
+
const tag = new NamedTag('import-batch', '');
|
|
11
|
+
it('should keep the name exactly', () => tag.name.should.equal('import-batch'));
|
|
12
|
+
it('should keep the empty value', () => tag.value.should.equal(''));
|
|
13
|
+
it('should be immutable', () => Object.isFrozen(tag).should.be.true);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('when comparing named tags', () => {
|
|
17
|
+
it('should treat the same name and value as equal', () => new NamedTag('a', 'B').equals(new NamedTag('a', 'B')).should.be.true);
|
|
18
|
+
it('should compare values case-sensitively', () => new NamedTag('a', 'B').equals(new NamedTag('a', 'b')).should.be.false);
|
|
19
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
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 } from 'vitest';
|
|
5
|
+
import { mergeNamedTags } from '../../mergeNamedTags.js';
|
|
6
|
+
import { NamedTag } from '../../NamedTag.js';
|
|
7
|
+
|
|
8
|
+
chai.should();
|
|
9
|
+
|
|
10
|
+
describe('when merging event and call named tags', () => {
|
|
11
|
+
let result: NamedTag[];
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
result = mergeNamedTags(
|
|
14
|
+
[new NamedTag('side', 'debit'), new NamedTag('transfer', 't-1')],
|
|
15
|
+
[new NamedTag('transfer', 't-1'), new NamedTag('transfer', 'T-1'), new NamedTag('side', 'credit')]);
|
|
16
|
+
});
|
|
17
|
+
it('should keep the first occurrence of each exact pair in order, including several values per name', () =>
|
|
18
|
+
result.map(tag => [tag.name, tag.value]).should.deep.equal([
|
|
19
|
+
['side', 'debit'], ['transfer', 't-1'], ['transfer', 'T-1'], ['side', 'credit']
|
|
20
|
+
]));
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('when merging plain named tag objects', () => {
|
|
24
|
+
let result: NamedTag[];
|
|
25
|
+
beforeEach(() => { result = mergeNamedTags([{ name: 'channel', value: 'web' } as NamedTag]); });
|
|
26
|
+
it('should return validated named tag instances', () => result[0].should.be.instanceOf(NamedTag));
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('when merging no named tags', () => {
|
|
30
|
+
it('should return an empty list', () => mergeNamedTags(undefined, null, []).should.deep.equal([]));
|
|
31
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
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 { InvalidNamedTag } from '../../InvalidNamedTag.js';
|
|
6
|
+
import { mergeNamedTags } from '../../mergeNamedTags.js';
|
|
7
|
+
import type { NamedTag } from '../../NamedTag.js';
|
|
8
|
+
|
|
9
|
+
chai.should();
|
|
10
|
+
|
|
11
|
+
describe('when merging named tags with invalid entries', () => {
|
|
12
|
+
it('should reject a null element', () => (() => mergeNamedTags([null as unknown as NamedTag])).should.throw(InvalidNamedTag));
|
|
13
|
+
it('should reject a plain object with a blank name', () =>
|
|
14
|
+
(() => mergeNamedTags([{ name: ' ', value: 'x' } as NamedTag])).should.throw(InvalidNamedTag));
|
|
15
|
+
it('should reject a string instead of a collection', () =>
|
|
16
|
+
(() => mergeNamedTags('name' as unknown as NamedTag[])).should.throw(InvalidNamedTag));
|
|
17
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
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 } from 'vitest';
|
|
5
|
+
import type { EventContext } from '../EventContext.js';
|
|
6
|
+
import { NamedTag } from '../NamedTag.js';
|
|
7
|
+
import { toClientEventContext } from '../toClientEventContext.js';
|
|
8
|
+
import type { WireEventContext } from '../WireEventContext.js';
|
|
9
|
+
|
|
10
|
+
chai.should();
|
|
11
|
+
|
|
12
|
+
function wireContext(namedTags?: { Name: string; Value: string }[]): WireEventContext {
|
|
13
|
+
return {
|
|
14
|
+
EventSourceId: 'source', SequenceNumber: 1n, EventSourceType: 'Default', EventStreamType: 'All', EventStreamId: 'Default',
|
|
15
|
+
EventType: { Id: 'type', Generation: 1, Tombstone: false }, Occurred: { Value: '2025-01-01T00:00:00.000Z' },
|
|
16
|
+
CorrelationId: undefined, Causation: [], CausedBy: undefined, Tags: [], Hash: '', ObservationState: 1, Subject: 'source',
|
|
17
|
+
NamedTags: namedTags
|
|
18
|
+
} as unknown as WireEventContext;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('when converting a delivered context with named tags', () => {
|
|
22
|
+
let context: EventContext;
|
|
23
|
+
beforeEach(() => { context = toClientEventContext(wireContext([{ Name: 'batch', Value: 'b-1' }, { Name: 'batch', Value: '' }])); });
|
|
24
|
+
it('should expose every named tag in delivered order', () =>
|
|
25
|
+
context.namedTags!.map(tag => [tag.name, tag.value]).should.deep.equal([['batch', 'b-1'], ['batch', '']]));
|
|
26
|
+
it('should expose named tag instances', () => context.namedTags![0].should.be.instanceOf(NamedTag));
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('when converting a delivered context without named tags', () => {
|
|
30
|
+
it('should expose an empty list', () => toClientEventContext(wireContext()).namedTags!.should.deep.equal([]));
|
|
31
|
+
});
|
package/events/index.ts
CHANGED
|
@@ -21,5 +21,8 @@ export { filterEventsByTag, getFilterTagsFor } from './filterEventsByTagDecorato
|
|
|
21
21
|
export { eventSourceType, getEventSourceTypeFor } from './eventSourceTypeDecorator.js';
|
|
22
22
|
export { eventStreamType, getEventStreamTypeFor } from './eventStreamTypeDecorator.js';
|
|
23
23
|
export { mergeTags } from './mergeTags.js';
|
|
24
|
+
export { NamedTag } from './NamedTag.js';
|
|
25
|
+
export { InvalidNamedTag } from './InvalidNamedTag.js';
|
|
26
|
+
export { mergeNamedTags } from './mergeNamedTags.js';
|
|
24
27
|
export * from './constraints/index.js';
|
|
25
28
|
export * from './migrations/index.js';
|
|
@@ -0,0 +1,37 @@
|
|
|
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 { InvalidNamedTag } from './InvalidNamedTag.js';
|
|
5
|
+
import { NamedTag } from './NamedTag.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Validates and merges structured named tags - for example the tags carried by an individual event and
|
|
9
|
+
* the tags supplied for a whole append call - into one distinct list. The first occurrence of each exact
|
|
10
|
+
* (ordinal, case-sensitive) name and value pair is kept, in order. Mirrors the .NET client's
|
|
11
|
+
* `NamedTagConverters.Merge`.
|
|
12
|
+
* @param sources - The named tag sources to merge, in precedence order. An undefined or null source contributes nothing.
|
|
13
|
+
* @returns The distinct, validated named tags.
|
|
14
|
+
* @throws {@link InvalidNamedTag} when a source is not iterable, contains an entry that is not an object,
|
|
15
|
+
* or contains a tag with a blank name or a non-string value.
|
|
16
|
+
*/
|
|
17
|
+
export function mergeNamedTags(...sources: ReadonlyArray<Iterable<NamedTag> | undefined | null>): NamedTag[] {
|
|
18
|
+
const seen = new Set<string>();
|
|
19
|
+
const merged: NamedTag[] = [];
|
|
20
|
+
for (const source of sources) {
|
|
21
|
+
if (source === undefined || source === null) continue;
|
|
22
|
+
if (typeof source !== 'object' || typeof (source as Iterable<NamedTag>)[Symbol.iterator] !== 'function') {
|
|
23
|
+
throw new InvalidNamedTag();
|
|
24
|
+
}
|
|
25
|
+
for (const entry of source as Iterable<unknown>) {
|
|
26
|
+
if (entry === null || typeof entry !== 'object') throw new InvalidNamedTag();
|
|
27
|
+
// Reconstruct so plain { name, value } objects are validated exactly like NamedTag instances.
|
|
28
|
+
const tag = entry instanceof NamedTag ? entry
|
|
29
|
+
: new NamedTag(Reflect.get(entry, 'name') as string, Reflect.get(entry, 'value') as string);
|
|
30
|
+
const key = JSON.stringify([tag.name, tag.value]);
|
|
31
|
+
if (seen.has(key)) continue;
|
|
32
|
+
seen.add(key);
|
|
33
|
+
merged.push(tag);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return merged;
|
|
37
|
+
}
|
|
@@ -8,6 +8,7 @@ import { EventType } from './EventType.js';
|
|
|
8
8
|
import { EventTypeGeneration } from './EventTypeGeneration.js';
|
|
9
9
|
import { EventTypeId } from './EventTypeId.js';
|
|
10
10
|
import { Tag } from './Tag.js';
|
|
11
|
+
import { NamedTag } from './NamedTag.js';
|
|
11
12
|
import type { WireEventContext } from './WireEventContext.js';
|
|
12
13
|
|
|
13
14
|
/** Converts kernel metadata identically for reads, reactors, and reducers. */
|
|
@@ -20,6 +21,7 @@ export function toClientEventContext(context: WireEventContext): EventContext {
|
|
|
20
21
|
eventSourceType: context.EventSourceType,
|
|
21
22
|
eventStreamType: context.EventStreamType,
|
|
22
23
|
eventStreamId: context.EventStreamId,
|
|
24
|
+
eventSource: context.EventSource ? context.EventSource : undefined,
|
|
23
25
|
subject: context.Subject,
|
|
24
26
|
hash: context.Hash,
|
|
25
27
|
observationState: context.ObservationState,
|
|
@@ -36,7 +38,8 @@ export function toClientEventContext(context: WireEventContext): EventContext {
|
|
|
36
38
|
occurred: causation.Occurred ? new Date(causation.Occurred.Value) : undefined,
|
|
37
39
|
properties: { ...causation.Properties }
|
|
38
40
|
})),
|
|
39
|
-
tags: (context.Tags ?? []).map(value => new Tag(value))
|
|
41
|
+
tags: (context.Tags ?? []).map(value => new Tag(value)),
|
|
42
|
+
namedTags: (context.NamedTags ?? []).map(tag => new NamedTag(tag.Name, tag.Value))
|
|
40
43
|
};
|
|
41
44
|
}
|
|
42
45
|
|
|
@@ -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,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cratis/chronicle",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.50.0",
|
|
4
4
|
"description": "Event sourcing for TypeScript and Node.js — the idiomatic client for Cratis Chronicle, the open-source event-sourcing database",
|
|
5
5
|
"author": "Cratis",
|
|
6
6
|
"license": "MIT",
|
|
@@ -173,12 +173,12 @@
|
|
|
173
173
|
"clean": "rm -rf dist",
|
|
174
174
|
"test": "vitest run",
|
|
175
175
|
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
176
|
-
"test:standard-types": "tsc --ignoreConfig --noEmit --target es2022 --module es2022 --moduleResolution bundler --lib es2022,esnext.decorators --types node --skipLibCheck --strict schemas/decorators.standard.spec.ts projections/declarative/addChildCompatibility.standard.spec.ts projections/declarative/for_CompositeKeyBuilder/when_typing_constant_parts.standard.spec.ts projections/modelBound/decorators.standard.spec.ts events/classDecorators.standard.spec.ts reactors/decorators.standard.spec.ts readModels/standardFallbacks.standard.spec.ts readModels/registration.standard.spec.ts for_ChronicleClient/when_registering_artifacts/with_standard_property_mappings.standard.spec.ts artifacts/validateArtifactSchemas.spec.ts testing/for_EventScenario/when_appending_concept_fields/with_standard_decorators.standard.spec.ts testing/for_EventScenario/when_appending_scoped_constraints/with_standard_decorators.standard.spec.ts schemas/for_JsonSchemaGenerator/when_generating_children/with_standard_decorators.standard.spec.ts events/for_handles/when_decorating_a_method.standard.spec.ts testing/for_ReadModelScenario/when_evaluating_arithmetic/with_standard_decorators.standard.spec.ts testing/for_ReadModelScenario/when_evaluating_joins/with_standard_decorators.standard.spec.ts testing/for_EventScenario/when_appending_field_types/with_standard_decorators.standard.spec.ts eventSequences/for_EventSequence/when_appending/with_subject_annotations.standard.spec.ts projections/for_ProjectionDefinitionCompiler/when_compiling_from_all.standard.spec.ts projections/for_ProjectionDefinitionCompiler/when_compiling_tags/with_standard_decorators.standard.spec.ts telemetry/for_ChronicleTelemetry/when_exposing_names/with_convention_names.spec.ts projections/for_ProjectionDefinitionCompiler/when_compiling_from_all_events.standard.spec.ts events/for_observerFilters/when_registering/with_standard_decorators.standard.spec.ts reactors/for_Reactors/against_a_kernel/when_filtering_source_and_stream_types.integration.spec.ts testing/for_observerScenarios/when_selecting_filters/with_source_and_stream_types.spec.ts readModels/for_ReadModelWatcher/watchCompatibility.standard.spec.ts",
|
|
176
|
+
"test:standard-types": "tsc --ignoreConfig --noEmit --target es2022 --module es2022 --moduleResolution bundler --lib es2022,esnext.decorators --types node --skipLibCheck --strict schemas/decorators.standard.spec.ts projections/declarative/addChildCompatibility.standard.spec.ts projections/declarative/for_CompositeKeyBuilder/when_typing_constant_parts.standard.spec.ts projections/modelBound/decorators.standard.spec.ts events/classDecorators.standard.spec.ts reactors/decorators.standard.spec.ts readModels/standardFallbacks.standard.spec.ts readModels/registration.standard.spec.ts for_ChronicleClient/when_registering_artifacts/with_standard_property_mappings.standard.spec.ts artifacts/validateArtifactSchemas.spec.ts testing/for_EventScenario/when_appending_concept_fields/with_standard_decorators.standard.spec.ts testing/for_EventScenario/when_appending_scoped_constraints/with_standard_decorators.standard.spec.ts schemas/for_JsonSchemaGenerator/when_generating_children/with_standard_decorators.standard.spec.ts events/for_handles/when_decorating_a_method.standard.spec.ts testing/for_ReadModelScenario/when_evaluating_arithmetic/with_standard_decorators.standard.spec.ts testing/for_ReadModelScenario/when_evaluating_joins/with_standard_decorators.standard.spec.ts testing/for_EventScenario/when_appending_field_types/with_standard_decorators.standard.spec.ts eventSequences/for_EventSequence/when_appending/with_subject_annotations.standard.spec.ts projections/for_ProjectionDefinitionCompiler/when_compiling_from_all.standard.spec.ts projections/for_ProjectionDefinitionCompiler/when_compiling_tags/with_standard_decorators.standard.spec.ts telemetry/for_ChronicleTelemetry/when_exposing_names/with_convention_names.spec.ts projections/for_ProjectionDefinitionCompiler/when_compiling_from_all_events.standard.spec.ts events/for_observerFilters/when_registering/with_standard_decorators.standard.spec.ts reactors/for_Reactors/against_a_kernel/when_filtering_source_and_stream_types.integration.spec.ts testing/for_observerScenarios/when_selecting_filters/with_source_and_stream_types.spec.ts readModels/for_ReadModelWatcher/watchCompatibility.standard.spec.ts for_EventStore/when_implementing_a_legacy_event_store.spec.ts",
|
|
177
177
|
"test:native": "node ./scripts/check-native-esm.mjs"
|
|
178
178
|
},
|
|
179
179
|
"dependencies": {
|
|
180
180
|
"@bufbuild/protobuf": "^2.16.0",
|
|
181
|
-
"@cratis/chronicle.contracts": "19.
|
|
181
|
+
"@cratis/chronicle.contracts": "19.31.10",
|
|
182
182
|
"@grpc/grpc-js": "^1.14.5",
|
|
183
183
|
"@opentelemetry/api": "^1.9.1",
|
|
184
184
|
"glob": "^13.0.6",
|
|
@@ -188,14 +188,14 @@
|
|
|
188
188
|
"undici": "^8.11.2"
|
|
189
189
|
},
|
|
190
190
|
"devDependencies": {
|
|
191
|
-
"@cratis/fundamentals": "7.
|
|
191
|
+
"@cratis/fundamentals": "7.22.4",
|
|
192
192
|
"@opentelemetry/context-async-hooks": "2.11.0",
|
|
193
193
|
"@opentelemetry/core": "2.11.0",
|
|
194
194
|
"@opentelemetry/instrumentation-grpc": "0.222.0",
|
|
195
195
|
"@opentelemetry/instrumentation-http": "0.222.0",
|
|
196
196
|
"@opentelemetry/sdk-metrics": "2.11.0",
|
|
197
197
|
"@opentelemetry/sdk-trace-base": "2.11.0",
|
|
198
|
-
"@types/node": "^26.6.
|
|
198
|
+
"@types/node": "^26.6.4",
|
|
199
199
|
"@typescript/native": "npm:typescript@^7.0.2",
|
|
200
200
|
"esbuild": "^0.28.2",
|
|
201
201
|
"typescript": "npm:@typescript/typescript6@^6.0.2",
|
|
@@ -203,6 +203,6 @@
|
|
|
203
203
|
"vitest": "^5.0.3"
|
|
204
204
|
},
|
|
205
205
|
"peerDependencies": {
|
|
206
|
-
"@cratis/fundamentals": "^7.
|
|
206
|
+
"@cratis/fundamentals": "^7.22.4"
|
|
207
207
|
}
|
|
208
208
|
}
|
|
@@ -23,7 +23,9 @@ const contextProperties = {
|
|
|
23
23
|
correlationId: true,
|
|
24
24
|
causation: true,
|
|
25
25
|
tags: true
|
|
26
|
-
|
|
26
|
+
// eventSource and namedTags are deliberately not projectable: their kernel resolution is not part of the
|
|
27
|
+
// supported expression set.
|
|
28
|
+
} satisfies Record<Exclude<keyof EventContext, 'eventSource' | 'namedTags'>, true>;
|
|
27
29
|
|
|
28
30
|
// Mirror the kernel's DerivedPropertyFunctions registry: Week() applies to dates only.
|
|
29
31
|
const derivedFunctions: Record<string, string> = { week: 'Week' };
|
|
@@ -0,0 +1,34 @@
|
|
|
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 'reflect-metadata';
|
|
5
|
+
import { chai, describe, it } from 'vitest';
|
|
6
|
+
import { field } from '@cratis/fundamentals';
|
|
7
|
+
import { eventType } from '../events/eventTypeDecorator.js';
|
|
8
|
+
import { EventScenario, UnsupportedEventSequenceOperation } from './index.js';
|
|
9
|
+
|
|
10
|
+
chai.should();
|
|
11
|
+
class ScenarioSourceEvent { @field(String) name: string; constructor(name: string) { this.name = name; } }
|
|
12
|
+
eventType('ScenarioSourceEvent')(ScenarioSourceEvent);
|
|
13
|
+
|
|
14
|
+
const rejects = async (action: () => Promise<unknown>) => {
|
|
15
|
+
let caught: unknown;
|
|
16
|
+
try { await action(); } catch (error) { caught = error; }
|
|
17
|
+
(caught instanceof UnsupportedEventSequenceOperation).should.be.true;
|
|
18
|
+
};
|
|
19
|
+
const create = () => new EventScenario({ artifacts: { eventTypes: [ScenarioSourceEvent] } });
|
|
20
|
+
|
|
21
|
+
describe('when appending with registered event sources in an in-process scenario', () => {
|
|
22
|
+
it('should reject the eventSource option on append instead of dropping it', async () => {
|
|
23
|
+
await rejects(() => create().append('A', new ScenarioSourceEvent('x'), { eventSource: 'Account' }));
|
|
24
|
+
});
|
|
25
|
+
it('should reject the eventSource option on appendMany instead of dropping it', async () => {
|
|
26
|
+
await rejects(() => create().appendMany([{ eventSourceId: 'A', event: new ScenarioSourceEvent('x') }], { eventSource: 'Account' }));
|
|
27
|
+
});
|
|
28
|
+
it('should reject a per-event eventSource on appendMany instead of dropping it', async () => {
|
|
29
|
+
await rejects(() => create().appendMany([{ eventSourceId: 'A', event: new ScenarioSourceEvent('x'), eventSource: 'Account' }]));
|
|
30
|
+
});
|
|
31
|
+
it('should reject a per-event eventStream on appendMany instead of dropping it', async () => {
|
|
32
|
+
await rejects(() => create().appendMany([{ eventSourceId: 'A', event: new ScenarioSourceEvent('x'), eventStream: 'Transactions' }]));
|
|
33
|
+
});
|
|
34
|
+
});
|