@cratis/chronicle 6.3.2 → 6.5.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/ChronicleClient.ts +1 -1
- package/EventStore.ts +7 -2
- package/README.md +13 -2
- package/artifacts/validateArtifactSchemas.spec.ts +75 -0
- package/artifacts/validateArtifactSchemas.ts +39 -0
- package/auditing/AmbientContext.spec.ts +57 -0
- package/auditing/CausationManager.ts +13 -15
- package/compliance/pii.ts +7 -6
- package/compliance/subject.ts +6 -5
- package/confidentiality/encrypted.ts +7 -6
- package/correlation/CorrelationIdManager.ts +5 -0
- package/dist/ChronicleClient.js +1 -1
- package/dist/ChronicleClient.js.map +1 -1
- package/dist/EventStore.d.ts +3 -1
- package/dist/EventStore.d.ts.map +1 -1
- package/dist/EventStore.js +6 -2
- package/dist/EventStore.js.map +1 -1
- package/dist/artifacts/validateArtifactSchemas.d.ts +4 -0
- package/dist/artifacts/validateArtifactSchemas.d.ts.map +1 -0
- package/dist/artifacts/validateArtifactSchemas.js +38 -0
- package/dist/artifacts/validateArtifactSchemas.js.map +1 -0
- package/dist/auditing/CausationManager.d.ts +3 -1
- package/dist/auditing/CausationManager.d.ts.map +1 -1
- package/dist/auditing/CausationManager.js +9 -15
- package/dist/auditing/CausationManager.js.map +1 -1
- package/dist/compliance/pii.d.ts +2 -1
- package/dist/compliance/pii.d.ts.map +1 -1
- package/dist/compliance/pii.js +6 -5
- package/dist/compliance/pii.js.map +1 -1
- package/dist/compliance/subject.d.ts +2 -1
- package/dist/compliance/subject.d.ts.map +1 -1
- package/dist/compliance/subject.js +5 -4
- package/dist/compliance/subject.js.map +1 -1
- package/dist/confidentiality/encrypted.d.ts +2 -1
- package/dist/confidentiality/encrypted.d.ts.map +1 -1
- package/dist/confidentiality/encrypted.js +6 -5
- package/dist/confidentiality/encrypted.js.map +1 -1
- package/dist/correlation/CorrelationIdManager.d.ts +2 -0
- package/dist/correlation/CorrelationIdManager.d.ts.map +1 -1
- package/dist/correlation/CorrelationIdManager.js +4 -0
- package/dist/correlation/CorrelationIdManager.js.map +1 -1
- package/dist/eventSequences/AppendOptions.d.ts +3 -4
- package/dist/eventSequences/AppendOptions.d.ts.map +1 -1
- package/dist/eventSequences/EventSequence.d.ts.map +1 -1
- package/dist/eventSequences/EventSequence.js +19 -14
- package/dist/eventSequences/EventSequence.js.map +1 -1
- package/dist/eventSequences/EventSequenceNumber.d.ts +3 -1
- package/dist/eventSequences/EventSequenceNumber.d.ts.map +1 -1
- package/dist/eventSequences/EventSequenceNumber.js +3 -1
- package/dist/eventSequences/EventSequenceNumber.js.map +1 -1
- package/dist/events/eventTypeDecorator.d.ts +8 -7
- package/dist/events/eventTypeDecorator.d.ts.map +1 -1
- package/dist/events/eventTypeDecorator.js +22 -5
- package/dist/events/eventTypeDecorator.js.map +1 -1
- package/dist/events/index.d.ts +2 -0
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +2 -0
- package/dist/events/index.js.map +1 -1
- package/dist/events/isRegisteredEvent.d.ts +4 -0
- package/dist/events/isRegisteredEvent.d.ts.map +1 -0
- package/dist/events/isRegisteredEvent.js +9 -0
- package/dist/events/isRegisteredEvent.js.map +1 -0
- package/dist/identity/IdentityProvider.d.ts +2 -0
- package/dist/identity/IdentityProvider.d.ts.map +1 -1
- package/dist/identity/IdentityProvider.js +4 -0
- package/dist/identity/IdentityProvider.js.map +1 -1
- package/dist/projections/modelBound/addFrom.d.ts +2 -1
- package/dist/projections/modelBound/addFrom.d.ts.map +1 -1
- package/dist/projections/modelBound/addFrom.js +4 -3
- package/dist/projections/modelBound/addFrom.js.map +1 -1
- package/dist/projections/modelBound/childrenFrom.d.ts +2 -1
- package/dist/projections/modelBound/childrenFrom.d.ts.map +1 -1
- package/dist/projections/modelBound/childrenFrom.js +4 -3
- package/dist/projections/modelBound/childrenFrom.js.map +1 -1
- package/dist/projections/modelBound/clearWith.d.ts +2 -1
- package/dist/projections/modelBound/clearWith.d.ts.map +1 -1
- package/dist/projections/modelBound/clearWith.js +4 -3
- package/dist/projections/modelBound/clearWith.js.map +1 -1
- package/dist/projections/modelBound/count.d.ts +2 -1
- package/dist/projections/modelBound/count.d.ts.map +1 -1
- package/dist/projections/modelBound/count.js +4 -3
- package/dist/projections/modelBound/count.js.map +1 -1
- package/dist/projections/modelBound/decrement.d.ts +2 -1
- package/dist/projections/modelBound/decrement.d.ts.map +1 -1
- package/dist/projections/modelBound/decrement.js +4 -3
- package/dist/projections/modelBound/decrement.js.map +1 -1
- package/dist/projections/modelBound/fromAll.d.ts +2 -1
- package/dist/projections/modelBound/fromAll.d.ts.map +1 -1
- package/dist/projections/modelBound/fromAll.js +4 -3
- package/dist/projections/modelBound/fromAll.js.map +1 -1
- package/dist/projections/modelBound/fromEvery.d.ts +2 -1
- package/dist/projections/modelBound/fromEvery.d.ts.map +1 -1
- package/dist/projections/modelBound/fromEvery.js +4 -3
- package/dist/projections/modelBound/fromEvery.js.map +1 -1
- package/dist/projections/modelBound/increment.d.ts +2 -1
- package/dist/projections/modelBound/increment.d.ts.map +1 -1
- package/dist/projections/modelBound/increment.js +4 -3
- package/dist/projections/modelBound/increment.js.map +1 -1
- package/dist/projections/modelBound/join.d.ts +2 -1
- package/dist/projections/modelBound/join.d.ts.map +1 -1
- package/dist/projections/modelBound/join.js +4 -3
- package/dist/projections/modelBound/join.js.map +1 -1
- package/dist/projections/modelBound/nested.d.ts +1 -6
- package/dist/projections/modelBound/nested.d.ts.map +1 -1
- package/dist/projections/modelBound/nested.js +6 -2
- package/dist/projections/modelBound/nested.js.map +1 -1
- package/dist/projections/modelBound/noAutoMap.d.ts +4 -11
- package/dist/projections/modelBound/noAutoMap.d.ts.map +1 -1
- package/dist/projections/modelBound/noAutoMap.js +6 -2
- package/dist/projections/modelBound/noAutoMap.js.map +1 -1
- package/dist/projections/modelBound/removedWith.d.ts +2 -1
- package/dist/projections/modelBound/removedWith.d.ts.map +1 -1
- package/dist/projections/modelBound/removedWith.js +4 -3
- package/dist/projections/modelBound/removedWith.js.map +1 -1
- package/dist/projections/modelBound/removedWithJoin.d.ts +2 -1
- package/dist/projections/modelBound/removedWithJoin.d.ts.map +1 -1
- package/dist/projections/modelBound/removedWithJoin.js +4 -3
- package/dist/projections/modelBound/removedWithJoin.js.map +1 -1
- package/dist/projections/modelBound/setFrom.d.ts +2 -1
- package/dist/projections/modelBound/setFrom.d.ts.map +1 -1
- package/dist/projections/modelBound/setFrom.js +4 -3
- package/dist/projections/modelBound/setFrom.js.map +1 -1
- package/dist/projections/modelBound/setFromContext.d.ts +3 -2
- package/dist/projections/modelBound/setFromContext.d.ts.map +1 -1
- package/dist/projections/modelBound/setFromContext.js +4 -3
- package/dist/projections/modelBound/setFromContext.js.map +1 -1
- package/dist/projections/modelBound/setValue.d.ts +2 -1
- package/dist/projections/modelBound/setValue.d.ts.map +1 -1
- package/dist/projections/modelBound/setValue.js +4 -3
- package/dist/projections/modelBound/setValue.js.map +1 -1
- package/dist/projections/modelBound/subtractFrom.d.ts +2 -1
- package/dist/projections/modelBound/subtractFrom.d.ts.map +1 -1
- package/dist/projections/modelBound/subtractFrom.js +4 -3
- package/dist/projections/modelBound/subtractFrom.js.map +1 -1
- package/dist/readModels/IReadModels.d.ts +10 -1
- package/dist/readModels/IReadModels.d.ts.map +1 -1
- package/dist/readModels/MaterializedReadModels.js +2 -2
- package/dist/readModels/MaterializedReadModels.js.map +1 -1
- package/dist/readModels/ReadModels.d.ts +6 -1
- package/dist/readModels/ReadModels.d.ts.map +1 -1
- package/dist/readModels/ReadModels.js +28 -2
- package/dist/readModels/ReadModels.js.map +1 -1
- package/dist/readModels/readModel.d.ts +2 -1
- package/dist/readModels/readModel.d.ts.map +1 -1
- package/dist/readModels/readModel.js +22 -5
- package/dist/readModels/readModel.js.map +1 -1
- package/dist/schemas/JsonSchemaGenerator.d.ts +6 -1
- package/dist/schemas/JsonSchemaGenerator.d.ts.map +1 -1
- package/dist/schemas/JsonSchemaGenerator.js +41 -31
- package/dist/schemas/JsonSchemaGenerator.js.map +1 -1
- package/dist/schemas/createDeferredSchema.d.ts +7 -0
- package/dist/schemas/createDeferredSchema.d.ts.map +1 -0
- package/dist/schemas/createDeferredSchema.js +19 -0
- package/dist/schemas/createDeferredSchema.js.map +1 -0
- package/dist/schemas/index.d.ts +1 -0
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js.map +1 -1
- package/dist/schemas/jsonSchemaProperty.d.ts +2 -1
- package/dist/schemas/jsonSchemaProperty.d.ts.map +1 -1
- package/dist/schemas/jsonSchemaProperty.js +3 -2
- package/dist/schemas/jsonSchemaProperty.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/TypeIntrospector.d.ts.map +1 -1
- package/dist/types/TypeIntrospector.js +9 -3
- package/dist/types/TypeIntrospector.js.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/propertyDecoratorMetadata.d.ts +17 -0
- package/dist/types/propertyDecoratorMetadata.d.ts.map +1 -0
- package/dist/types/propertyDecoratorMetadata.js +76 -0
- package/dist/types/propertyDecoratorMetadata.js.map +1 -0
- package/dist/types/standardDecoratorMetadata.d.ts +9 -0
- package/dist/types/standardDecoratorMetadata.d.ts.map +1 -0
- package/dist/types/standardDecoratorMetadata.js +20 -0
- package/dist/types/standardDecoratorMetadata.js.map +1 -0
- package/eventSequences/AppendOptions.ts +3 -4
- package/eventSequences/EventSequence.spec.ts +59 -0
- package/eventSequences/EventSequence.ts +21 -15
- package/eventSequences/EventSequenceNumber.ts +4 -1
- package/eventSequences/for_EventSequence/when_appending/with_metadata.spec.ts +2 -1
- package/events/classDecorators.standard.spec.ts +77 -0
- package/events/eventTypeDecorator.ts +31 -13
- package/events/index.ts +3 -0
- package/events/isRegisteredEvent.spec.ts +26 -0
- package/events/isRegisteredEvent.ts +12 -0
- package/identity/IdentityProvider.ts +5 -0
- package/package.json +3 -1
- package/projections/modelBound/addFrom.ts +5 -4
- package/projections/modelBound/childrenFrom.ts +5 -4
- package/projections/modelBound/clearWith.ts +5 -4
- package/projections/modelBound/count.ts +5 -4
- package/projections/modelBound/decorators.standard.spec.ts +71 -0
- package/projections/modelBound/decrement.ts +5 -4
- package/projections/modelBound/fromAll.ts +5 -4
- package/projections/modelBound/fromEvery.ts +5 -4
- package/projections/modelBound/increment.ts +5 -4
- package/projections/modelBound/join.ts +5 -4
- package/projections/modelBound/nested.ts +9 -2
- package/projections/modelBound/noAutoMap.ts +11 -2
- package/projections/modelBound/removedWith.ts +5 -4
- package/projections/modelBound/removedWithJoin.ts +5 -4
- package/projections/modelBound/setFrom.ts +5 -4
- package/projections/modelBound/setFromContext.ts +7 -6
- package/projections/modelBound/setValue.ts +5 -4
- package/projections/modelBound/subtractFrom.ts +5 -4
- package/readModels/IReadModels.ts +11 -1
- package/readModels/MaterializedReadModels.spec.ts +17 -6
- package/readModels/MaterializedReadModels.ts +2 -2
- package/readModels/ReadModels.spec.ts +96 -3
- package/readModels/ReadModels.ts +32 -2
- package/readModels/readModel.ts +24 -6
- package/readModels/standardFallbacks.standard.spec.ts +39 -0
- package/schemas/JsonSchemaGenerator.confidentiality.spec.ts +2 -0
- package/schemas/JsonSchemaGenerator.spec.ts +28 -1
- package/schemas/JsonSchemaGenerator.ts +45 -30
- package/schemas/createDeferredSchema.ts +21 -0
- package/schemas/decorators.expected.fixture.ts +24 -0
- package/schemas/decorators.legacy.spec.ts +62 -0
- package/schemas/decorators.standard.spec.ts +235 -0
- package/schemas/index.ts +1 -0
- package/schemas/jsonSchemaProperty.ts +4 -3
- package/schemas/legacyDesignMetadata.spec.ts +39 -0
- package/types/TypeIntrospector.ts +8 -3
- package/types/index.ts +2 -0
- package/types/propertyDecoratorMetadata.ts +80 -0
- package/types/standardDecoratorMetadata.spec.ts +31 -0
- package/types/standardDecoratorMetadata.ts +26 -0
- package/vitest.config.ts +17 -0
|
@@ -0,0 +1,76 @@
|
|
|
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
|
+
import 'reflect-metadata';
|
|
4
|
+
import { getStandardMetadata } from './standardDecoratorMetadata.js';
|
|
5
|
+
/** Dispatches class and field decorations while retaining the legacy invocation shape. */
|
|
6
|
+
export function decorateClassOrProperty(legacy) {
|
|
7
|
+
const propertyDecorator = decorateProperty(legacy);
|
|
8
|
+
return (target, propertyOrContext) => {
|
|
9
|
+
if (typeof propertyOrContext === 'object' && propertyOrContext !== null) {
|
|
10
|
+
if (propertyOrContext.kind === 'class') {
|
|
11
|
+
if (typeof target !== 'function')
|
|
12
|
+
throw new TypeError('Chronicle class decorators require a class constructor.');
|
|
13
|
+
legacy(target);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
propertyDecorator(target, propertyOrContext);
|
|
17
|
+
}
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (!target)
|
|
21
|
+
throw new TypeError('Invalid legacy decorator invocation.');
|
|
22
|
+
legacy(target, propertyOrContext);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** Stores standard property annotations on the class metadata object, without constructing an instance. */
|
|
26
|
+
export function decorateProperty(legacy) {
|
|
27
|
+
return (target, keyOrContext) => {
|
|
28
|
+
if (typeof keyOrContext === 'object' && keyOrContext !== null) {
|
|
29
|
+
const context = keyOrContext;
|
|
30
|
+
if (target !== undefined || context.kind !== 'field' || context.static || context.private || typeof context.name !== 'string' || !context.metadata) {
|
|
31
|
+
throw new TypeError('Chronicle property decorators require a public instance field with standard decorator metadata.');
|
|
32
|
+
}
|
|
33
|
+
// Legacy decorators use target.constructor to track fields and store type-level annotations.
|
|
34
|
+
// The context.metadata object is owned by this class; never mutate its parent's metadata.
|
|
35
|
+
Object.defineProperty(context.metadata, 'constructor', { value: context.metadata, configurable: true });
|
|
36
|
+
legacy(context.metadata, context.name);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!target || (typeof keyOrContext !== 'string' && typeof keyOrContext !== 'symbol')) {
|
|
40
|
+
throw new TypeError('Invalid legacy property decorator invocation.');
|
|
41
|
+
}
|
|
42
|
+
legacy(target, keyOrContext);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** Reads an annotation declared by either decorator transform on a class field. */
|
|
46
|
+
export function getPropertyMetadata(key, target, property) {
|
|
47
|
+
const ownLegacy = Reflect.getOwnMetadata(key, target, property);
|
|
48
|
+
if (ownLegacy !== undefined)
|
|
49
|
+
return ownLegacy;
|
|
50
|
+
const type = target.constructor;
|
|
51
|
+
const metadata = type && getStandardMetadata(type);
|
|
52
|
+
const ownStandard = metadata && Reflect.getOwnMetadata(key, metadata, property);
|
|
53
|
+
if (ownStandard !== undefined)
|
|
54
|
+
return ownStandard;
|
|
55
|
+
return Reflect.getMetadata(key, target, property)
|
|
56
|
+
?? (metadata ? Reflect.getMetadata(key, metadata, property) : undefined);
|
|
57
|
+
}
|
|
58
|
+
/** Checks whether either decorator transform declared an annotation on a class field. */
|
|
59
|
+
export function hasPropertyMetadata(key, target, property) {
|
|
60
|
+
const type = target.constructor;
|
|
61
|
+
const metadata = type && getStandardMetadata(type);
|
|
62
|
+
return Reflect.hasMetadata(key, target, property) || !!metadata && Reflect.hasMetadata(key, metadata, property);
|
|
63
|
+
}
|
|
64
|
+
/** Reads a type-level annotation emitted by a standard field decorator (or its legacy equivalent). */
|
|
65
|
+
export function getTypeOrFieldMetadata(key, type) {
|
|
66
|
+
const ownLegacy = Reflect.getOwnMetadata(key, type);
|
|
67
|
+
if (ownLegacy !== undefined)
|
|
68
|
+
return ownLegacy;
|
|
69
|
+
const metadata = getStandardMetadata(type);
|
|
70
|
+
const ownStandard = metadata && Reflect.getOwnMetadata(key, metadata);
|
|
71
|
+
if (ownStandard !== undefined)
|
|
72
|
+
return ownStandard;
|
|
73
|
+
return Reflect.getMetadata(key, type)
|
|
74
|
+
?? (metadata ? Reflect.getMetadata(key, metadata) : undefined);
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=propertyDecoratorMetadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"propertyDecoratorMetadata.js","sourceRoot":"","sources":["../../types/propertyDecoratorMetadata.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** A class decorator accepted by both the legacy and standard TypeScript transforms. */
|
|
2
|
+
export type ChronicleClassDecorator = ClassDecorator & ((value: Function, context: ClassDecoratorContext) => void);
|
|
3
|
+
/** Gets the metadata object attached when a standard-decorated class finishes evaluation. */
|
|
4
|
+
export declare function getStandardMetadata(type: Function): object | undefined;
|
|
5
|
+
/** Whether a class completed its own standard decorator evaluation. */
|
|
6
|
+
export declare function hasOwnStandardMetadata(type: Function): boolean;
|
|
7
|
+
/** Prevents a class decorator from inspecting fields before their metadata is attached. */
|
|
8
|
+
export declare function requireCompletedStandardMetadata(type: Function): void;
|
|
9
|
+
//# sourceMappingURL=standardDecoratorMetadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standardDecoratorMetadata.d.ts","sourceRoot":"","sources":["../../types/standardDecoratorMetadata.ts"],"names":[],"mappings":"AAOA,wFAAwF;AACxF,MAAM,MAAM,uBAAuB,GAAG,cAAc,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC,CAAC;AAEnH,6FAA6F;AAC7F,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAEtE;AAED,uEAAuE;AACvE,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAE9D;AAED,2FAA2F;AAC3F,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAIrE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
// Node versions without native decorator metadata need this before evaluating a decorated class.
|
|
4
|
+
const symbolWithMetadata = Symbol;
|
|
5
|
+
symbolWithMetadata.metadata ??= Symbol.for('Symbol.metadata');
|
|
6
|
+
/** Gets the metadata object attached when a standard-decorated class finishes evaluation. */
|
|
7
|
+
export function getStandardMetadata(type) {
|
|
8
|
+
return Reflect.get(type, symbolWithMetadata.metadata);
|
|
9
|
+
}
|
|
10
|
+
/** Whether a class completed its own standard decorator evaluation. */
|
|
11
|
+
export function hasOwnStandardMetadata(type) {
|
|
12
|
+
return Object.prototype.hasOwnProperty.call(type, symbolWithMetadata.metadata);
|
|
13
|
+
}
|
|
14
|
+
/** Prevents a class decorator from inspecting fields before their metadata is attached. */
|
|
15
|
+
export function requireCompletedStandardMetadata(type) {
|
|
16
|
+
if (!hasOwnStandardMetadata(type)) {
|
|
17
|
+
throw new TypeError(`Standard decorator metadata for ${type.name} is not complete; inspect its schema after class evaluation.`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=standardDecoratorMetadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standardDecoratorMetadata.js","sourceRoot":"","sources":["../../types/standardDecoratorMetadata.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,qGAAqG;AAErG,iGAAiG;AACjG,MAAM,kBAAkB,GAAG,MAAyC,CAAC;AACrE,kBAAkB,CAAC,QAAQ,KAAK,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;AAK9D,6FAA6F;AAC7F,MAAM,UAAU,mBAAmB,CAAC,IAAc;IAC9C,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,kBAAkB,CAAC,QAAQ,CAAuB,CAAC;AAChF,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,sBAAsB,CAAC,IAAc;IACjD,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC;AACnF,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,gCAAgC,CAAC,IAAc;IAC3D,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CAAC,mCAAmC,IAAI,CAAC,IAAI,8DAA8D,CAAC,CAAC;IACpI,CAAC;AACL,CAAC"}
|
|
@@ -42,10 +42,9 @@ export interface AppendOptions {
|
|
|
42
42
|
tags?: ReadonlyArray<string | Tag>;
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
-
* Optional
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* entry here, it falls back to {@link concurrencyScope}.
|
|
45
|
+
* Optional labeled concurrency scopes, keyed by event source id. Labels need not be append targets.
|
|
46
|
+
* Any target without an explicit scope falls back to {@link concurrencyScope}.
|
|
47
|
+
* The kernel requires at least one event in a batch, so scope-only batches are not supported.
|
|
49
48
|
*/
|
|
50
49
|
concurrencyScopes?: Record<string, ConcurrencyScope>;
|
|
51
50
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import { describe, expect, it, vi } from 'vitest';
|
|
5
5
|
import type { ChronicleConnection } from '../connection/index.js';
|
|
6
|
+
import { causationManager, CausationType } from '../auditing/index.js';
|
|
6
7
|
import type { IUnitOfWorkManager } from '../transactions/IUnitOfWorkManager.js';
|
|
7
8
|
import { eventType } from '../events/eventTypeDecorator.js';
|
|
8
9
|
import { CompleteStreamError } from './CompleteStreamError.js';
|
|
@@ -354,6 +355,64 @@ describe('EventSequence', () => {
|
|
|
354
355
|
});
|
|
355
356
|
});
|
|
356
357
|
|
|
358
|
+
describe('when appending several batches in one command scope', () => {
|
|
359
|
+
it('should not carry the previous append link into the next append', async () => {
|
|
360
|
+
const { eventSequence, appendManyForEventSources } = createEventSequence();
|
|
361
|
+
await causationManager.run(new CausationType('Command'), {}, async () => {
|
|
362
|
+
const commandChain = causationManager.getCurrentChain().map(c => ({
|
|
363
|
+
Occurred: { Value: c.occurred.toISOString() }, Type: c.type.name, Properties: { ...c.properties }
|
|
364
|
+
}));
|
|
365
|
+
await eventSequence.appendMany([{ eventSourceId: 'target', event: new SomethingHappened() }]);
|
|
366
|
+
await eventSequence.appendMany([{ eventSourceId: 'target', event: new SomethingHappened() }]);
|
|
367
|
+
|
|
368
|
+
// The test runner may have an ambient causation link; neither batch may add one to the next.
|
|
369
|
+
const [first, second] = appendManyForEventSources.mock.calls;
|
|
370
|
+
const expectedChain = [...commandChain, expect.objectContaining({
|
|
371
|
+
Type: CausationType.appendManyEvents.name, Properties: { count: '1' }
|
|
372
|
+
})];
|
|
373
|
+
expect(first[0].Causation).toEqual(expectedChain);
|
|
374
|
+
expect(second[0].Causation).toEqual(expectedChain);
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
describe('when a batch has an independent concurrency label', () => {
|
|
380
|
+
it('should send both the event target and the independent scope', async () => {
|
|
381
|
+
const { eventSequence, appendManyForEventSources } = createEventSequence();
|
|
382
|
+
await eventSequence.appendMany([{ eventSourceId: 'target', event: new SomethingHappened() }], {
|
|
383
|
+
concurrencyScope: { sequenceNumber: 7n },
|
|
384
|
+
concurrencyScopes: { independent: { sequenceNumber: 3n, eventSourceId: true } }
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
const scopes = appendManyForEventSources.mock.calls[0][0].ConcurrencyScopes;
|
|
388
|
+
expect(scopes).toEqual(expect.arrayContaining([
|
|
389
|
+
expect.objectContaining({ EventSourceId: 'independent', Scope: expect.objectContaining({ SequenceNumber: 3n, EventSourceId: true }) }),
|
|
390
|
+
expect.objectContaining({ EventSourceId: 'target', Scope: expect.objectContaining({ SequenceNumber: 7n }) })
|
|
391
|
+
]));
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
describe('when a batch expects no matching event', () => {
|
|
396
|
+
it('should send the dedicated flag and unavailable sequence number', async () => {
|
|
397
|
+
const { eventSequence, appendManyForEventSources } = createEventSequence();
|
|
398
|
+
await eventSequence.appendMany([{ eventSourceId: 'target', event: new SomethingHappened() }], {
|
|
399
|
+
concurrencyScopes: { independent: { sequenceNumber: EventSequenceNumber.beforeFirst.value, eventSourceId: true } }
|
|
400
|
+
});
|
|
401
|
+
const scope = appendManyForEventSources.mock.calls[0][0].ConcurrencyScopes[0].Scope;
|
|
402
|
+
expect(scope.ExpectsNoMatchingEvent).toBe(true);
|
|
403
|
+
expect(scope.SequenceNumber).toBe(EventSequenceNumber.unset.value);
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
describe('when a batch contains only concurrency scopes', () => {
|
|
408
|
+
it('should reject locally because the kernel requires an event', async () => {
|
|
409
|
+
const { eventSequence, appendManyForEventSources } = createEventSequence();
|
|
410
|
+
await expect(eventSequence.appendMany([], { concurrencyScopes: { independent: { sequenceNumber: 1n } } }))
|
|
411
|
+
.rejects.toThrow('Chronicle requires at least one event');
|
|
412
|
+
expect(appendManyForEventSources).not.toHaveBeenCalled();
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
|
|
357
416
|
describe('when an EventForEventSourceId specifies its own stream targeting', () => {
|
|
358
417
|
const { eventSequence, appendManyForEventSources } = createEventSequence();
|
|
359
418
|
|
|
@@ -71,8 +71,8 @@ export class EventSequence implements IEventSequence {
|
|
|
71
71
|
// Merge static tags declared on the event type with tags supplied at append time.
|
|
72
72
|
const tags = mergeTags(getTagsFor(event.constructor as Function), options?.tags);
|
|
73
73
|
|
|
74
|
-
causationManager.
|
|
75
|
-
|
|
74
|
+
const causationChain = causationManager.run(CausationType.appendEvent, { eventType: eventType.id.value },
|
|
75
|
+
() => causationManager.getCurrentChain());
|
|
76
76
|
const identity = identityProvider.getCurrent();
|
|
77
77
|
|
|
78
78
|
const metricAttributes = {
|
|
@@ -212,23 +212,26 @@ export class EventSequence implements IEventSequence {
|
|
|
212
212
|
const appendOptions = typeof eventSourceIdOrEvents === 'string'
|
|
213
213
|
? options
|
|
214
214
|
: eventsOrOptions as AppendOptions | undefined;
|
|
215
|
+
if (eventsForEventSourceIds.length === 0 && Object.keys(appendOptions?.concurrencyScopes ?? {}).length > 0) {
|
|
216
|
+
throw new Error('Chronicle requires at least one event to validate concurrency scopes.');
|
|
217
|
+
}
|
|
215
218
|
|
|
216
219
|
const correlationId = appendOptions?.correlationId === undefined
|
|
217
220
|
? Guid.as(correlationIdManager.current.value)
|
|
218
221
|
: Guid.as(appendOptions.correlationId);
|
|
219
222
|
|
|
220
|
-
causationManager.
|
|
221
|
-
|
|
223
|
+
const batchCausationChain = causationManager.run(CausationType.appendManyEvents, { count: String(eventsForEventSourceIds.length) },
|
|
224
|
+
() => causationManager.getCurrentChain());
|
|
222
225
|
const identity = identityProvider.getCurrent();
|
|
223
226
|
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
227
|
+
// Explicit labels may narrow a different event source than any target in this batch.
|
|
228
|
+
// Preserve them and supply the shared fallback only for targets without an explicit scope.
|
|
229
|
+
const concurrencyScopes = new Map<string, ConcurrencyScope | undefined>(Object.entries(appendOptions?.concurrencyScopes ?? {}));
|
|
230
|
+
for (const { eventSourceId } of eventsForEventSourceIds) {
|
|
231
|
+
if (!concurrencyScopes.has(eventSourceId)) {
|
|
232
|
+
concurrencyScopes.set(eventSourceId, appendOptions?.concurrencyScope);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
232
235
|
|
|
233
236
|
const eventsToAppend = eventsForEventSourceIds.map(({ eventSourceId, event, eventStreamType, eventStreamId, eventSourceType, subject, occurred, tags: instanceTags }) => {
|
|
234
237
|
const eventType = getEventTypeFor(event.constructor as Function);
|
|
@@ -286,9 +289,9 @@ export class EventSequence implements IEventSequence {
|
|
|
286
289
|
Properties: { ...c.properties }
|
|
287
290
|
})),
|
|
288
291
|
CausedBy: toContractsCausedBy(identity),
|
|
289
|
-
ConcurrencyScopes:
|
|
292
|
+
ConcurrencyScopes: [...concurrencyScopes].map(([eventSourceId, scope]) => ({
|
|
290
293
|
EventSourceId: eventSourceId,
|
|
291
|
-
Scope:
|
|
294
|
+
Scope: this.toContractConcurrencyScope(scope)
|
|
292
295
|
}))
|
|
293
296
|
});
|
|
294
297
|
|
|
@@ -752,7 +755,10 @@ export class EventSequence implements IEventSequence {
|
|
|
752
755
|
|
|
753
756
|
private toContractConcurrencyScope(scope?: ConcurrencyScope) {
|
|
754
757
|
return {
|
|
755
|
-
SequenceNumber: scope?.sequenceNumber
|
|
758
|
+
SequenceNumber: scope?.sequenceNumber === EventSequenceNumber.beforeFirst.value
|
|
759
|
+
? EventSequenceNumber.unset.value
|
|
760
|
+
: scope?.sequenceNumber ?? EventSequenceNumber.unset.value,
|
|
761
|
+
ExpectsNoMatchingEvent: scope?.sequenceNumber === EventSequenceNumber.beforeFirst.value,
|
|
756
762
|
EventSourceId: scope?.eventSourceId ?? false,
|
|
757
763
|
EventStreamType: scope?.eventStreamType ?? '',
|
|
758
764
|
EventStreamId: scope?.eventStreamId ?? '',
|
|
@@ -8,9 +8,12 @@ export class EventSequenceNumber {
|
|
|
8
8
|
/** Represents the first possible sequence number. */
|
|
9
9
|
static readonly first = new EventSequenceNumber(0n);
|
|
10
10
|
|
|
11
|
-
/** Represents an unset sequence number. */
|
|
11
|
+
/** Represents an unset/unavailable sequence number; no expected revision was supplied. */
|
|
12
12
|
static readonly unset = new EventSequenceNumber(18446744073709551615n);
|
|
13
13
|
|
|
14
|
+
/** Expects no event matching the scope. Encoded as ExpectsNoMatchingEvent, not as a wire sequence number. */
|
|
15
|
+
static readonly beforeFirst = new EventSequenceNumber(18446744073709551613n);
|
|
16
|
+
|
|
14
17
|
constructor(readonly value: bigint) {}
|
|
15
18
|
|
|
16
19
|
/**
|
|
@@ -67,8 +67,9 @@ describe('when appending with explicit legacy routing and metadata', () => {
|
|
|
67
67
|
request.Occurred!.Value.should.equal(occurred.toISOString());
|
|
68
68
|
});
|
|
69
69
|
it('should not derive concurrency scope from the route', () => {
|
|
70
|
+
// The .NET converter sends the contract's explicit false when no before-first check was requested.
|
|
70
71
|
request.ConcurrencyScope!.should.deep.equal({
|
|
71
|
-
SequenceNumber: EventSequenceNumber.unset.value, EventSourceId: false,
|
|
72
|
+
SequenceNumber: EventSequenceNumber.unset.value, ExpectsNoMatchingEvent: false, EventSourceId: false,
|
|
72
73
|
EventStreamType: '', EventStreamId: '', EventSourceType: '', EventTypes: []
|
|
73
74
|
});
|
|
74
75
|
});
|
|
@@ -0,0 +1,77 @@
|
|
|
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, expect, it } from 'vitest';
|
|
5
|
+
import { constraint, getConstraintMetadata } from './constraints/constraint.js';
|
|
6
|
+
import { eventType, getEventTypeMetadata } from './eventTypeDecorator.js';
|
|
7
|
+
import { filterEventsByTag, getFilterTagsFor } from './filterEventsByTagDecorator.js';
|
|
8
|
+
import { eventTypeMigration, getEventTypeMigrationMetadata } from './migrations/eventTypeMigration.js';
|
|
9
|
+
import { tag, tags, getTagsFor } from './tagDecorator.js';
|
|
10
|
+
import { entersOn, getEntersOnMetadata } from '../projections/modelBound/entersOn.js';
|
|
11
|
+
import { eventLog, eventSequence, getEventSequenceMetadata } from '../projections/modelBound/eventSequence.js';
|
|
12
|
+
import { fromEvent, getFromEventMetadata } from '../projections/modelBound/fromEvent.js';
|
|
13
|
+
import { getGlobalForMetadata, globalFor } from '../projections/modelBound/globalFor.js';
|
|
14
|
+
import { isNotRewindable, notRewindable } from '../projections/modelBound/notRewindable.js';
|
|
15
|
+
import { isPassive, passive } from '../projections/modelBound/passive.js';
|
|
16
|
+
import { getVariantOfMetadata, variantOf } from '../projections/modelBound/variantOf.js';
|
|
17
|
+
import { getSeederMetadata, seeder } from '../seeding/seeder.js';
|
|
18
|
+
import { getWebhookMetadata, webhook } from '../webhooks/webhook.js';
|
|
19
|
+
|
|
20
|
+
@eventType('migration-fixture', 1)
|
|
21
|
+
class Previous {}
|
|
22
|
+
|
|
23
|
+
@eventType('migration-fixture', 2)
|
|
24
|
+
class Upgraded {}
|
|
25
|
+
|
|
26
|
+
@eventTypeMigration(Upgraded, Previous)
|
|
27
|
+
class Migration {}
|
|
28
|
+
|
|
29
|
+
@constraint('standard-constraint')
|
|
30
|
+
class Constraint {}
|
|
31
|
+
|
|
32
|
+
@tag('first')
|
|
33
|
+
@tags('second')
|
|
34
|
+
@filterEventsByTag('required')
|
|
35
|
+
class Tagged {}
|
|
36
|
+
|
|
37
|
+
@fromEvent(Upgraded)
|
|
38
|
+
@entersOn(Upgraded)
|
|
39
|
+
@eventSequence('sequence')
|
|
40
|
+
@passive
|
|
41
|
+
@notRewindable
|
|
42
|
+
class ModelBound {}
|
|
43
|
+
|
|
44
|
+
@eventLog
|
|
45
|
+
class EventLogModel {}
|
|
46
|
+
|
|
47
|
+
@globalFor(Upgraded)
|
|
48
|
+
class GlobalHandler {}
|
|
49
|
+
|
|
50
|
+
@variantOf(Upgraded, 'kind')
|
|
51
|
+
class Variant {}
|
|
52
|
+
|
|
53
|
+
@seeder()
|
|
54
|
+
class Seeder {}
|
|
55
|
+
|
|
56
|
+
@webhook('standard-webhook', 'https://example.com/hooks')
|
|
57
|
+
class Webhook {}
|
|
58
|
+
|
|
59
|
+
describe('standard class decorators', () => {
|
|
60
|
+
it('retains class registrations and metadata without legacy decorator syntax', () => {
|
|
61
|
+
expect(getEventTypeMetadata(Previous)?.schema.properties).toEqual({});
|
|
62
|
+
expect(getEventTypeMigrationMetadata(Migration)?.eventType.generation.value).toBe(2);
|
|
63
|
+
expect(getConstraintMetadata(Constraint)?.id.value).toBe('standard-constraint');
|
|
64
|
+
expect(getTagsFor(Tagged).map(item => item.value)).toEqual(['second', 'first']);
|
|
65
|
+
expect(getFilterTagsFor(Tagged).map(item => item.value)).toEqual(['required']);
|
|
66
|
+
expect(getFromEventMetadata(ModelBound)).toHaveLength(1);
|
|
67
|
+
expect(getEntersOnMetadata(ModelBound)).toHaveLength(1);
|
|
68
|
+
expect(getEventSequenceMetadata(ModelBound)).toBe('sequence');
|
|
69
|
+
expect(isPassive(ModelBound)).toBe(true);
|
|
70
|
+
expect(isNotRewindable(ModelBound)).toBe(true);
|
|
71
|
+
expect(getEventSequenceMetadata(EventLogModel)).toBeDefined();
|
|
72
|
+
expect(getGlobalForMetadata(GlobalHandler)?.identity).toBe(Upgraded);
|
|
73
|
+
expect(getVariantOfMetadata(Variant)?.identity).toBe(Upgraded);
|
|
74
|
+
expect(getSeederMetadata(Seeder)?.isSeeder).toBe(true);
|
|
75
|
+
expect(getWebhookMetadata(Webhook)?.targetUrl.value).toBe('https://example.com/hooks');
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -8,6 +8,8 @@ import { EventTypeId } from './EventTypeId.js';
|
|
|
8
8
|
import { EventTypeGeneration } from './EventTypeGeneration.js';
|
|
9
9
|
import { DecoratorType, TypeDiscoverer, TypeIntrospector } from '../types/index.js';
|
|
10
10
|
import { JsonSchema, JsonSchemaGenerator } from '../schemas/index.js';
|
|
11
|
+
import { ChronicleClassDecorator } from '../types/standardDecoratorMetadata.js';
|
|
12
|
+
import { createDeferredSchema } from '../schemas/createDeferredSchema.js';
|
|
11
13
|
|
|
12
14
|
/** Metadata key used to store event type information on a class. */
|
|
13
15
|
const EVENT_TYPE_METADATA_KEY = 'chronicle:eventType';
|
|
@@ -51,18 +53,18 @@ export interface EventTypeMetadata {
|
|
|
51
53
|
* }
|
|
52
54
|
* ```
|
|
53
55
|
*/
|
|
54
|
-
export function eventType():
|
|
55
|
-
export function eventType(id: string):
|
|
56
|
-
export function eventType(id: string, generation: number):
|
|
57
|
-
export function eventType(id: string, generation: number, tombstone: boolean):
|
|
58
|
-
export function eventType(generation: number):
|
|
59
|
-
export function eventType(generation: number, tombstone: boolean):
|
|
60
|
-
export function eventType(tombstone: boolean):
|
|
56
|
+
export function eventType(): ChronicleClassDecorator;
|
|
57
|
+
export function eventType(id: string): ChronicleClassDecorator;
|
|
58
|
+
export function eventType(id: string, generation: number): ChronicleClassDecorator;
|
|
59
|
+
export function eventType(id: string, generation: number, tombstone: boolean): ChronicleClassDecorator;
|
|
60
|
+
export function eventType(generation: number): ChronicleClassDecorator;
|
|
61
|
+
export function eventType(generation: number, tombstone: boolean): ChronicleClassDecorator;
|
|
62
|
+
export function eventType(tombstone: boolean): ChronicleClassDecorator;
|
|
61
63
|
export function eventType(
|
|
62
64
|
idOrGenerationOrTombstone?: string | number | boolean,
|
|
63
65
|
generationOrTombstone?: number | boolean,
|
|
64
66
|
tombstone: boolean = false
|
|
65
|
-
):
|
|
67
|
+
): ChronicleClassDecorator {
|
|
66
68
|
let id = '';
|
|
67
69
|
let generation = EventTypeGeneration.firstValue;
|
|
68
70
|
let isTombstone = false;
|
|
@@ -86,14 +88,23 @@ export function eventType(
|
|
|
86
88
|
isTombstone = idOrGenerationOrTombstone;
|
|
87
89
|
}
|
|
88
90
|
|
|
89
|
-
return (target: object) => {
|
|
90
|
-
|
|
91
|
+
return (target: object, context?: ClassDecoratorContext) => {
|
|
92
|
+
let constructor = target as Function;
|
|
91
93
|
const eventTypeId = new EventTypeId(id || constructor.name);
|
|
92
94
|
const eventTypeInstance = new EventType(eventTypeId, new EventTypeGeneration(generation), isTombstone);
|
|
93
|
-
const members = TypeIntrospector.getMembers(constructor);
|
|
94
|
-
const
|
|
95
|
+
const members = context?.kind === 'class' ? undefined : TypeIntrospector.getMembers(constructor);
|
|
96
|
+
const resolve = createDeferredSchema(() => constructor);
|
|
97
|
+
const metadata: EventTypeMetadata = context?.kind === 'class' ? {
|
|
95
98
|
eventType: eventTypeInstance,
|
|
96
|
-
members
|
|
99
|
+
get members() {
|
|
100
|
+
return resolve().members;
|
|
101
|
+
},
|
|
102
|
+
get schema() {
|
|
103
|
+
return resolve().schema;
|
|
104
|
+
}
|
|
105
|
+
} : {
|
|
106
|
+
eventType: eventTypeInstance,
|
|
107
|
+
members: members!,
|
|
97
108
|
schema: JsonSchemaGenerator.generate(constructor, members)
|
|
98
109
|
};
|
|
99
110
|
Reflect.defineMetadata(EVENT_TYPE_METADATA_KEY, metadata, target);
|
|
@@ -102,6 +113,13 @@ export function eventType(
|
|
|
102
113
|
constructor as Constructor,
|
|
103
114
|
eventTypeId.value
|
|
104
115
|
);
|
|
116
|
+
context?.addInitializer(function () {
|
|
117
|
+
if (this !== constructor) {
|
|
118
|
+
constructor = this;
|
|
119
|
+
Reflect.defineMetadata(EVENT_TYPE_METADATA_KEY, metadata, constructor);
|
|
120
|
+
TypeDiscoverer.default.register(DecoratorType.EventType, constructor as Constructor, eventTypeId.value);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
105
123
|
};
|
|
106
124
|
}
|
|
107
125
|
|
package/events/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Copyright (c) Cratis. All rights reserved.
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
|
|
4
|
+
import '../types/standardDecoratorMetadata.js';
|
|
5
|
+
|
|
4
6
|
export { EventType } from './EventType.js';
|
|
5
7
|
export { EventTypeId } from './EventTypeId.js';
|
|
6
8
|
export { EventTypeGeneration } from './EventTypeGeneration.js';
|
|
@@ -11,6 +13,7 @@ export type { CausationEntry } from './CausationEntry.js';
|
|
|
11
13
|
export type { AppendedEvent } from './AppendedEvent.js';
|
|
12
14
|
export type { IEventTypes } from './IEventTypes.js';
|
|
13
15
|
export { EventTypes } from './EventTypes.js';
|
|
16
|
+
export { isRegisteredEvent } from './isRegisteredEvent.js';
|
|
14
17
|
export { Tag } from './Tag.js';
|
|
15
18
|
export { tag, tags, getTagsFor } from './tagDecorator.js';
|
|
16
19
|
export { filterEventsByTag, getFilterTagsFor } from './filterEventsByTagDecorator.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
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, expect, it } from 'vitest';
|
|
5
|
+
import type { IEventStore } from '../IEventStore.js';
|
|
6
|
+
import { eventType } from './eventTypeDecorator.js';
|
|
7
|
+
import { isRegisteredEvent } from './isRegisteredEvent.js';
|
|
8
|
+
|
|
9
|
+
class CurrentEvent {}
|
|
10
|
+
class UnregisteredEvent {}
|
|
11
|
+
eventType('c00f9f33-0ccb-4319-9def-b19ef26f0351')(CurrentEvent);
|
|
12
|
+
eventType('c00f9f33-0ccb-4319-9def-b19ef26f0352')(UnregisteredEvent);
|
|
13
|
+
|
|
14
|
+
const store = { eventTypes: { all: [CurrentEvent] } } as IEventStore;
|
|
15
|
+
|
|
16
|
+
describe('when classifying a command return value against a store', () => {
|
|
17
|
+
it('should accept an instance of the registered event', () => {
|
|
18
|
+
expect(isRegisteredEvent(store, new CurrentEvent())).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should reject decorated but unregistered types and ordinary payloads', () => {
|
|
22
|
+
expect(isRegisteredEvent(store, new UnregisteredEvent())).toBe(false);
|
|
23
|
+
expect(isRegisteredEvent(store, { event: new CurrentEvent() })).toBe(false);
|
|
24
|
+
expect(isRegisteredEvent(store, null)).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import type { Constructor } from '@cratis/fundamentals';
|
|
5
|
+
import type { IEventStore } from '../IEventStore.js';
|
|
6
|
+
import { hasEventType } from './eventTypeDecorator.js';
|
|
7
|
+
|
|
8
|
+
/** Checks whether a value is an instance of a current event type registered in this store. */
|
|
9
|
+
export function isRegisteredEvent(store: Pick<IEventStore, 'eventTypes'>, value: unknown): value is object {
|
|
10
|
+
return typeof value === 'object' && value !== null &&
|
|
11
|
+
hasEventType(value.constructor) && store.eventTypes.all.includes(value.constructor as Constructor);
|
|
12
|
+
}
|
|
@@ -16,6 +16,11 @@ export class IdentityProvider implements IIdentityProvider {
|
|
|
16
16
|
return this._storage.getStore() ?? Identity.system;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/** Runs an async or synchronous operation as the given identity, restoring the caller's identity afterward. */
|
|
20
|
+
run<T>(identity: Identity, callback: () => T): T {
|
|
21
|
+
return this._storage.run(identity, callback);
|
|
22
|
+
}
|
|
23
|
+
|
|
19
24
|
/** @inheritdoc */
|
|
20
25
|
setCurrentIdentity(identity: Identity): void {
|
|
21
26
|
this._storage.enterWith(identity);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cratis/chronicle",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.5.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",
|
|
@@ -162,6 +162,7 @@
|
|
|
162
162
|
"build": "tsc -b",
|
|
163
163
|
"clean": "rm -rf dist",
|
|
164
164
|
"test": "vitest run",
|
|
165
|
+
"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/modelBound/decorators.standard.spec.ts events/classDecorators.standard.spec.ts readModels/standardFallbacks.standard.spec.ts artifacts/validateArtifactSchemas.spec.ts",
|
|
165
166
|
"test:native": "node ./scripts/check-native-esm.mjs"
|
|
166
167
|
},
|
|
167
168
|
"dependencies": {
|
|
@@ -178,6 +179,7 @@
|
|
|
178
179
|
"@cratis/fundamentals": "7.19.2",
|
|
179
180
|
"@types/node": "^26.5.1",
|
|
180
181
|
"@typescript/native": "npm:typescript@^7.0.2",
|
|
182
|
+
"esbuild": "^0.28.2",
|
|
181
183
|
"typescript": "npm:@typescript/typescript6@^6.0.2",
|
|
182
184
|
"vite": "^8.3.0",
|
|
183
185
|
"vitest": "^5.0.1"
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import 'reflect-metadata';
|
|
5
5
|
import { TypeIntrospector } from '../../types/index.js';
|
|
6
|
+
import { ChroniclePropertyDecorator, decorateProperty, getPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
6
7
|
|
|
7
8
|
/** Metadata stored by the addFrom property decorator. */
|
|
8
9
|
export interface AddFromMetadata {
|
|
@@ -20,14 +21,14 @@ const METADATA_KEY = 'chronicle:projection:addFrom';
|
|
|
20
21
|
* @param eventPropertyName - Optional event property name. Defaults to the property name.
|
|
21
22
|
* @returns A property decorator.
|
|
22
23
|
*/
|
|
23
|
-
export function addFrom(eventType: Function, eventPropertyName?: string):
|
|
24
|
-
return (target: object, propertyKey: string | symbol) => {
|
|
24
|
+
export function addFrom(eventType: Function, eventPropertyName?: string): ChroniclePropertyDecorator {
|
|
25
|
+
return decorateProperty((target: object, propertyKey: string | symbol) => {
|
|
25
26
|
const key = propertyKey.toString();
|
|
26
27
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, key);
|
|
27
28
|
const existing: AddFromMetadata[] = Reflect.getMetadata(METADATA_KEY, target, key) ?? [];
|
|
28
29
|
const metadata: AddFromMetadata = { eventType, eventPropertyName };
|
|
29
30
|
Reflect.defineMetadata(METADATA_KEY, [...existing, metadata], target, key);
|
|
30
|
-
};
|
|
31
|
+
});
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
/**
|
|
@@ -37,5 +38,5 @@ export function addFrom(eventType: Function, eventPropertyName?: string): Proper
|
|
|
37
38
|
* @returns An array of addFrom metadata entries.
|
|
38
39
|
*/
|
|
39
40
|
export function getAddFromMetadata(target: object, propertyKey: string): AddFromMetadata[] {
|
|
40
|
-
return
|
|
41
|
+
return getPropertyMetadata<AddFromMetadata[]>(METADATA_KEY, target, propertyKey) ?? [];
|
|
41
42
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import 'reflect-metadata';
|
|
5
5
|
import { TypeIntrospector } from '../../types/index.js';
|
|
6
|
+
import { ChroniclePropertyDecorator, decorateProperty, getPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
6
7
|
|
|
7
8
|
/** Metadata stored by the childrenFrom property decorator. */
|
|
8
9
|
export interface ChildrenFromMetadata {
|
|
@@ -31,14 +32,14 @@ export function childrenFrom(
|
|
|
31
32
|
key?: string,
|
|
32
33
|
identifiedBy?: string,
|
|
33
34
|
parentKey?: string
|
|
34
|
-
):
|
|
35
|
-
return (target: object, propertyKey: string | symbol) => {
|
|
35
|
+
): ChroniclePropertyDecorator {
|
|
36
|
+
return decorateProperty((target: object, propertyKey: string | symbol) => {
|
|
36
37
|
const propKey = propertyKey.toString();
|
|
37
38
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, propKey);
|
|
38
39
|
const existing: ChildrenFromMetadata[] = Reflect.getMetadata(METADATA_KEY, target, propKey) ?? [];
|
|
39
40
|
const metadata: ChildrenFromMetadata = { eventType, key, identifiedBy, parentKey };
|
|
40
41
|
Reflect.defineMetadata(METADATA_KEY, [...existing, metadata], target, propKey);
|
|
41
|
-
};
|
|
42
|
+
});
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
/**
|
|
@@ -48,5 +49,5 @@ export function childrenFrom(
|
|
|
48
49
|
* @returns An array of childrenFrom metadata entries.
|
|
49
50
|
*/
|
|
50
51
|
export function getChildrenFromMetadata(target: object, propertyKey: string): ChildrenFromMetadata[] {
|
|
51
|
-
return
|
|
52
|
+
return getPropertyMetadata<ChildrenFromMetadata[]>(METADATA_KEY, target, propertyKey) ?? [];
|
|
52
53
|
}
|