@cratis/chronicle 6.9.1 → 6.11.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 +52 -13
- package/ChronicleOptions.spec.ts +54 -0
- package/ChronicleOptions.ts +20 -5
- package/EventStore.ts +7 -1
- package/README.md +2 -3
- package/artifacts/DefaultClientArtifactsProvider.ts +4 -1
- package/connection/ChronicleConnection.auth.spec.ts +270 -0
- package/connection/ChronicleConnection.ts +90 -31
- package/connection/ChronicleServices.ts +1 -1
- package/connection/CompatibilityPreflight.ts +1 -1
- package/connection/RejectedChronicleCredentials.ts +10 -0
- package/connection/TokenProvider.spec.ts +5 -6
- package/connection/TokenProvider.ts +26 -17
- package/connection/clientVersion.spec.ts +27 -0
- package/connection/clientVersion.ts +9 -0
- package/connection/describeRequestError.spec.ts +22 -0
- package/connection/fetchOAuthAccessToken.ts +33 -2
- package/dist/ChronicleClient.d.ts +3 -0
- package/dist/ChronicleClient.d.ts.map +1 -1
- package/dist/ChronicleClient.js +55 -13
- package/dist/ChronicleClient.js.map +1 -1
- package/dist/ChronicleOptions.d.ts +5 -1
- package/dist/ChronicleOptions.d.ts.map +1 -1
- package/dist/ChronicleOptions.js +20 -5
- package/dist/ChronicleOptions.js.map +1 -1
- package/dist/EventStore.d.ts +2 -0
- package/dist/EventStore.d.ts.map +1 -1
- package/dist/EventStore.js +6 -1
- package/dist/EventStore.js.map +1 -1
- package/dist/artifacts/DefaultClientArtifactsProvider.d.ts.map +1 -1
- package/dist/artifacts/DefaultClientArtifactsProvider.js +4 -1
- package/dist/artifacts/DefaultClientArtifactsProvider.js.map +1 -1
- package/dist/connection/ChronicleConnection.d.ts +3 -3
- package/dist/connection/ChronicleConnection.d.ts.map +1 -1
- package/dist/connection/ChronicleConnection.js +98 -26
- package/dist/connection/ChronicleConnection.js.map +1 -1
- package/dist/connection/ChronicleServices.d.ts +1 -2
- package/dist/connection/ChronicleServices.d.ts.map +1 -1
- package/dist/connection/CompatibilityPreflight.js +1 -1
- package/dist/connection/CompatibilityPreflight.js.map +1 -1
- package/dist/connection/RejectedChronicleCredentials.d.ts +5 -0
- package/dist/connection/RejectedChronicleCredentials.d.ts.map +1 -0
- package/dist/connection/RejectedChronicleCredentials.js +10 -0
- package/dist/connection/RejectedChronicleCredentials.js.map +1 -0
- package/dist/connection/TokenProvider.d.ts +8 -6
- package/dist/connection/TokenProvider.d.ts.map +1 -1
- package/dist/connection/TokenProvider.js +25 -11
- package/dist/connection/TokenProvider.js.map +1 -1
- package/dist/connection/clientVersion.d.ts +3 -0
- package/dist/connection/clientVersion.d.ts.map +1 -0
- package/dist/connection/clientVersion.js +7 -0
- package/dist/connection/clientVersion.js.map +1 -0
- package/dist/connection/fetchOAuthAccessToken.d.ts +12 -0
- package/dist/connection/fetchOAuthAccessToken.d.ts.map +1 -1
- package/dist/connection/fetchOAuthAccessToken.js +37 -2
- package/dist/connection/fetchOAuthAccessToken.js.map +1 -1
- package/dist/eventSequences/AppendResult.d.ts +7 -0
- package/dist/eventSequences/AppendResult.d.ts.map +1 -1
- package/dist/eventSequences/EventSequence.d.ts.map +1 -1
- package/dist/eventSequences/EventSequence.js +26 -17
- package/dist/eventSequences/EventSequence.js.map +1 -1
- package/dist/eventSequences/WaitForCompletionOptions.d.ts +8 -0
- package/dist/eventSequences/WaitForCompletionOptions.d.ts.map +1 -0
- package/dist/eventSequences/WaitForCompletionOptions.js +4 -0
- package/dist/eventSequences/WaitForCompletionOptions.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.map +1 -1
- package/dist/externalServices/ExternalServiceBuilder.d.ts +1 -1
- package/dist/externalServices/ExternalServiceBuilder.d.ts.map +1 -1
- package/dist/externalServices/ExternalServiceBuilder.js +1 -1
- package/dist/externalServices/ExternalServiceBuilder.js.map +1 -1
- package/dist/externalServices/ExternalServices.d.ts.map +1 -1
- package/dist/externalServices/ExternalServices.js +3 -1
- package/dist/externalServices/ExternalServices.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/projections/Projections.d.ts.map +1 -1
- package/dist/projections/Projections.js +9 -7
- package/dist/projections/Projections.js.map +1 -1
- package/dist/projections/constantValueExpression.d.ts +3 -0
- package/dist/projections/constantValueExpression.d.ts.map +1 -0
- package/dist/projections/constantValueExpression.js +26 -0
- package/dist/projections/constantValueExpression.js.map +1 -0
- package/dist/projections/declarative/ChildrenBuilder.d.ts +2 -0
- package/dist/projections/declarative/ChildrenBuilder.d.ts.map +1 -1
- package/dist/projections/declarative/ChildrenBuilder.js +5 -0
- package/dist/projections/declarative/ChildrenBuilder.js.map +1 -1
- package/dist/projections/declarative/FromBuilder.d.ts.map +1 -1
- package/dist/projections/declarative/FromBuilder.js +3 -2
- package/dist/projections/declarative/FromBuilder.js.map +1 -1
- package/dist/projections/declarative/JoinBuilder.d.ts.map +1 -1
- package/dist/projections/declarative/JoinBuilder.js +2 -1
- package/dist/projections/declarative/JoinBuilder.js.map +1 -1
- package/dist/projections/declarative/NestedBuilder.d.ts.map +1 -1
- package/dist/projections/declarative/NestedBuilder.js +3 -1
- package/dist/projections/declarative/NestedBuilder.js.map +1 -1
- package/dist/projections/declarative/ProjectionBuilderCore.d.ts +5 -0
- package/dist/projections/declarative/ProjectionBuilderCore.d.ts.map +1 -1
- package/dist/projections/declarative/ProjectionBuilderCore.js +18 -2
- package/dist/projections/declarative/ProjectionBuilderCore.js.map +1 -1
- package/dist/projections/declarative/ProjectionBuilderFor.d.ts +6 -0
- package/dist/projections/declarative/ProjectionBuilderFor.d.ts.map +1 -1
- package/dist/projections/declarative/ProjectionBuilderFor.js +5 -0
- package/dist/projections/declarative/ProjectionBuilderFor.js.map +1 -1
- package/dist/projections/declarative/SetBuilder.d.ts.map +1 -1
- package/dist/projections/declarative/SetBuilder.js +2 -1
- package/dist/projections/declarative/SetBuilder.js.map +1 -1
- package/dist/projections/modelBound/addFrom.d.ts.map +1 -1
- package/dist/projections/modelBound/addFrom.js +3 -2
- package/dist/projections/modelBound/addFrom.js.map +1 -1
- package/dist/projections/modelBound/childrenAndNestedBuilder.d.ts +20 -0
- package/dist/projections/modelBound/childrenAndNestedBuilder.d.ts.map +1 -1
- package/dist/projections/modelBound/childrenAndNestedBuilder.js +14 -11
- package/dist/projections/modelBound/childrenAndNestedBuilder.js.map +1 -1
- package/dist/projections/modelBound/childrenFrom.d.ts +4 -2
- package/dist/projections/modelBound/childrenFrom.d.ts.map +1 -1
- package/dist/projections/modelBound/childrenFrom.js +8 -4
- package/dist/projections/modelBound/childrenFrom.js.map +1 -1
- package/dist/projections/modelBound/clearWith.d.ts.map +1 -1
- package/dist/projections/modelBound/clearWith.js +3 -2
- package/dist/projections/modelBound/clearWith.js.map +1 -1
- package/dist/projections/modelBound/count.d.ts.map +1 -1
- package/dist/projections/modelBound/count.js +3 -2
- package/dist/projections/modelBound/count.js.map +1 -1
- package/dist/projections/modelBound/decrement.d.ts.map +1 -1
- package/dist/projections/modelBound/decrement.js +3 -2
- package/dist/projections/modelBound/decrement.js.map +1 -1
- package/dist/projections/modelBound/fromAll.d.ts.map +1 -1
- package/dist/projections/modelBound/fromAll.js +3 -2
- package/dist/projections/modelBound/fromAll.js.map +1 -1
- package/dist/projections/modelBound/fromEvery.d.ts.map +1 -1
- package/dist/projections/modelBound/fromEvery.js +3 -2
- package/dist/projections/modelBound/fromEvery.js.map +1 -1
- package/dist/projections/modelBound/increment.d.ts.map +1 -1
- package/dist/projections/modelBound/increment.js +3 -2
- package/dist/projections/modelBound/increment.js.map +1 -1
- package/dist/projections/modelBound/join.d.ts.map +1 -1
- package/dist/projections/modelBound/join.js +3 -2
- package/dist/projections/modelBound/join.js.map +1 -1
- package/dist/projections/modelBound/nested.d.ts.map +1 -1
- package/dist/projections/modelBound/nested.js +3 -2
- package/dist/projections/modelBound/nested.js.map +1 -1
- package/dist/projections/modelBound/noAutoMap.d.ts.map +1 -1
- package/dist/projections/modelBound/noAutoMap.js +3 -2
- package/dist/projections/modelBound/noAutoMap.js.map +1 -1
- package/dist/projections/modelBound/removedWith.d.ts.map +1 -1
- package/dist/projections/modelBound/removedWith.js +3 -2
- package/dist/projections/modelBound/removedWith.js.map +1 -1
- package/dist/projections/modelBound/removedWithJoin.d.ts.map +1 -1
- package/dist/projections/modelBound/removedWithJoin.js +3 -2
- package/dist/projections/modelBound/removedWithJoin.js.map +1 -1
- package/dist/projections/modelBound/setFrom.d.ts.map +1 -1
- package/dist/projections/modelBound/setFrom.js +3 -2
- package/dist/projections/modelBound/setFrom.js.map +1 -1
- package/dist/projections/modelBound/setFromContext.d.ts.map +1 -1
- package/dist/projections/modelBound/setFromContext.js +3 -2
- package/dist/projections/modelBound/setFromContext.js.map +1 -1
- package/dist/projections/modelBound/setValue.d.ts.map +1 -1
- package/dist/projections/modelBound/setValue.js +3 -2
- package/dist/projections/modelBound/setValue.js.map +1 -1
- package/dist/projections/modelBound/subtractFrom.d.ts.map +1 -1
- package/dist/projections/modelBound/subtractFrom.js +3 -2
- package/dist/projections/modelBound/subtractFrom.js.map +1 -1
- package/dist/projections/notSetPropertyPath.d.ts +3 -0
- package/dist/projections/notSetPropertyPath.d.ts.map +1 -0
- package/dist/projections/notSetPropertyPath.js +5 -0
- package/dist/projections/notSetPropertyPath.js.map +1 -0
- package/dist/reactors/Reactors.d.ts +4 -0
- package/dist/reactors/Reactors.d.ts.map +1 -1
- package/dist/reactors/Reactors.js +24 -6
- package/dist/reactors/Reactors.js.map +1 -1
- package/dist/reactors/reactor.d.ts +2 -2
- package/dist/reactors/reactor.js +2 -2
- package/dist/readModels/IReadModels.d.ts +3 -2
- package/dist/readModels/IReadModels.d.ts.map +1 -1
- package/dist/readModels/MaterializedReadModels.d.ts.map +1 -1
- package/dist/readModels/MaterializedReadModels.js +2 -4
- package/dist/readModels/MaterializedReadModels.js.map +1 -1
- package/dist/readModels/ReadModels.d.ts +3 -5
- package/dist/readModels/ReadModels.d.ts.map +1 -1
- package/dist/readModels/ReadModels.js +17 -14
- package/dist/readModels/ReadModels.js.map +1 -1
- package/dist/readModels/deserializeReadModel.d.ts +5 -0
- package/dist/readModels/deserializeReadModel.d.ts.map +1 -0
- package/dist/readModels/deserializeReadModel.js +84 -0
- package/dist/readModels/deserializeReadModel.js.map +1 -0
- package/dist/readModels/rootReadModelTypes.d.ts +5 -0
- package/dist/readModels/rootReadModelTypes.d.ts.map +1 -0
- package/dist/readModels/rootReadModelTypes.js +69 -0
- package/dist/readModels/rootReadModelTypes.js.map +1 -0
- package/dist/reducers/Reducers.d.ts +4 -0
- package/dist/reducers/Reducers.d.ts.map +1 -1
- package/dist/reducers/Reducers.js +24 -6
- package/dist/reducers/Reducers.js.map +1 -1
- package/dist/schemas/JsonSchemaGenerator.d.ts.map +1 -1
- package/dist/schemas/JsonSchemaGenerator.js +4 -4
- package/dist/schemas/JsonSchemaGenerator.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/TypeDiscoverer.d.ts +8 -1
- package/dist/types/TypeDiscoverer.d.ts.map +1 -1
- package/dist/types/TypeDiscoverer.js +25 -5
- package/dist/types/TypeDiscoverer.js.map +1 -1
- package/dist/types/TypeIntrospector.d.ts.map +1 -1
- package/dist/types/TypeIntrospector.js +3 -2
- package/dist/types/TypeIntrospector.js.map +1 -1
- package/dist/types/conceptValueType.d.ts +4 -0
- package/dist/types/conceptValueType.d.ts.map +1 -0
- package/dist/types/conceptValueType.js +10 -0
- package/dist/types/conceptValueType.js.map +1 -0
- package/dist/types/modelBoundProperty.d.ts +6 -0
- package/dist/types/modelBoundProperty.d.ts.map +1 -0
- package/dist/types/modelBoundProperty.js +53 -0
- package/dist/types/modelBoundProperty.js.map +1 -0
- package/eventSequences/AppendResult.ts +8 -0
- package/eventSequences/EventSequence.spec.ts +76 -1
- package/eventSequences/EventSequence.ts +28 -20
- package/eventSequences/WaitForCompletionOptions.ts +11 -0
- package/eventSequences/index.ts +1 -0
- package/externalServices/ExternalServiceBuilder.ts +4 -4
- package/externalServices/ExternalServices.spec.ts +87 -0
- package/externalServices/ExternalServices.ts +3 -1
- package/for_ChronicleClient/connectionAuthentication.spec.ts +67 -0
- package/index.ts +1 -0
- package/observation/observationDisposal.spec.ts +74 -0
- package/package.json +2 -1
- package/projections/Projections.childrenAndNested.spec.ts +2 -0
- package/projections/Projections.modelBoundCompleteness.spec.ts +47 -0
- package/projections/Projections.ts +9 -7
- package/projections/constantValueExpression.spec.ts +31 -0
- package/projections/constantValueExpression.ts +28 -0
- package/projections/declarative/ChildrenBuilder.spec.ts +46 -1
- package/projections/declarative/ChildrenBuilder.ts +6 -0
- package/projections/declarative/FromBuilder.ts +3 -2
- package/projections/declarative/JoinBuilder.ts +2 -1
- package/projections/declarative/NestedBuilder.ts +3 -1
- package/projections/declarative/ProjectionBuilderCore.ts +20 -2
- package/projections/declarative/ProjectionBuilderFor.spec.ts +61 -4
- package/projections/declarative/ProjectionBuilderFor.ts +7 -0
- package/projections/declarative/SetBuilder.ts +2 -1
- package/projections/modelBound/addFrom.ts +3 -2
- package/projections/modelBound/childrenAndNestedBuilder.ts +14 -11
- package/projections/modelBound/childrenFrom.ts +11 -5
- package/projections/modelBound/clearWith.ts +3 -2
- package/projections/modelBound/count.ts +3 -2
- package/projections/modelBound/decorators.standard.spec.ts +45 -0
- package/projections/modelBound/decrement.ts +3 -2
- package/projections/modelBound/fromAll.ts +3 -2
- package/projections/modelBound/fromEvery.ts +3 -2
- package/projections/modelBound/increment.ts +3 -2
- package/projections/modelBound/join.ts +3 -2
- package/projections/modelBound/nested.ts +3 -2
- package/projections/modelBound/noAutoMap.ts +3 -2
- package/projections/modelBound/removedWith.ts +3 -2
- package/projections/modelBound/removedWithJoin.ts +3 -2
- package/projections/modelBound/setFrom.ts +3 -2
- package/projections/modelBound/setFromContext.ts +3 -2
- package/projections/modelBound/setValue.ts +3 -2
- package/projections/modelBound/subtractFrom.ts +3 -2
- package/projections/notSetPropertyPath.ts +5 -0
- package/reactors/Reactors.ts +22 -7
- package/reactors/reactor.ts +2 -2
- package/readModels/IReadModels.ts +3 -2
- package/readModels/MaterializedReadModels.spec.ts +22 -0
- package/readModels/MaterializedReadModels.ts +2 -4
- package/readModels/ReadModels.spec.ts +71 -6
- package/readModels/ReadModels.ts +18 -15
- package/readModels/conceptValueType.spec.ts +20 -0
- package/readModels/deserializeReadModel.spec.ts +84 -0
- package/readModels/deserializeReadModel.ts +82 -0
- package/readModels/discovery.spec.ts +27 -0
- package/readModels/rootReadModelTypes.spec.ts +115 -0
- package/readModels/rootReadModelTypes.ts +65 -0
- package/reducers/Reducers.ts +22 -7
- package/schemas/JsonSchemaGenerator.ts +4 -4
- package/schemas/legacyDesignMetadata.spec.ts +24 -2
- package/types/TypeDiscoverer.spec.ts +57 -0
- package/types/TypeDiscoverer.ts +27 -6
- package/types/TypeIntrospector.ts +3 -2
- package/types/conceptValueType.ts +11 -0
- package/types/modelBoundProperty.ts +53 -0
- package/dist/externalServices/ExternalServicesContracts.d.ts +0 -137
- package/dist/externalServices/ExternalServicesContracts.d.ts.map +0 -1
- package/dist/externalServices/ExternalServicesContracts.js +0 -654
- package/dist/externalServices/ExternalServicesContracts.js.map +0 -1
- package/externalServices/ExternalServicesContracts.ts +0 -709
|
@@ -3,12 +3,15 @@
|
|
|
3
3
|
|
|
4
4
|
import 'reflect-metadata';
|
|
5
5
|
import { TypeIntrospector } from '../../types/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { decorateModelBoundProperty } from '../../types/modelBoundProperty.js';
|
|
7
|
+
import { ChroniclePropertyDecorator, getPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
7
8
|
|
|
8
9
|
/** Metadata stored by the childrenFrom property decorator. */
|
|
9
10
|
export interface ChildrenFromMetadata {
|
|
10
11
|
/** The event constructor that adds children to the collection. */
|
|
11
12
|
readonly eventType: Function;
|
|
13
|
+
/** Optional child type for TypeScript runtimes without emitted generic metadata. */
|
|
14
|
+
readonly childType?: Function;
|
|
12
15
|
/** The event property name used as the key for children. Defaults to the event source identifier. */
|
|
13
16
|
readonly key?: string;
|
|
14
17
|
/** The child model property name used to uniquely identify instances in the collection. */
|
|
@@ -22,22 +25,25 @@ const METADATA_KEY = 'chronicle:projection:childrenFrom';
|
|
|
22
25
|
/**
|
|
23
26
|
* Property decorator that configures a children collection sub-projection from an event type.
|
|
24
27
|
* @param eventType - The event constructor.
|
|
25
|
-
* @param key - Optional event property name to use as the key for children.
|
|
28
|
+
* @param key - Optional event property name to use as the key for children, or the child constructor.
|
|
26
29
|
* @param identifiedBy - Optional child model property name used to identify instances.
|
|
27
30
|
* @param parentKey - Optional event property name used as the parent key.
|
|
28
31
|
* @returns A property decorator.
|
|
29
32
|
*/
|
|
30
33
|
export function childrenFrom(
|
|
31
34
|
eventType: Function,
|
|
32
|
-
key?: string,
|
|
35
|
+
key?: string | Function,
|
|
33
36
|
identifiedBy?: string,
|
|
34
37
|
parentKey?: string
|
|
35
38
|
): ChroniclePropertyDecorator {
|
|
36
|
-
return
|
|
39
|
+
return decorateModelBoundProperty((target: object, propertyKey: string | symbol) => {
|
|
37
40
|
const propKey = propertyKey.toString();
|
|
38
41
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, propKey);
|
|
39
42
|
const existing: ChildrenFromMetadata[] = Reflect.getMetadata(METADATA_KEY, target, propKey) ?? [];
|
|
40
|
-
const metadata: ChildrenFromMetadata = {
|
|
43
|
+
const metadata: ChildrenFromMetadata = {
|
|
44
|
+
eventType, key: typeof key === 'string' ? key : undefined,
|
|
45
|
+
childType: typeof key === 'function' ? key : undefined, identifiedBy, parentKey
|
|
46
|
+
};
|
|
41
47
|
Reflect.defineMetadata(METADATA_KEY, [...existing, metadata], target, propKey);
|
|
42
48
|
});
|
|
43
49
|
}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import 'reflect-metadata';
|
|
5
5
|
import { TypeIntrospector } from '../../types/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { decorateModelBoundClassOrProperty } from '../../types/modelBoundProperty.js';
|
|
7
|
+
import { ChronicleClassOrPropertyDecorator, getPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
7
8
|
|
|
8
9
|
/** Metadata stored by the clearWith class or property decorator. */
|
|
9
10
|
export interface ClearWithMetadata {
|
|
@@ -22,7 +23,7 @@ const PROPERTY_METADATA_KEY = 'chronicle:projection:clearWith:property';
|
|
|
22
23
|
* @returns A class and property decorator.
|
|
23
24
|
*/
|
|
24
25
|
export function clearWith(eventType: Function): ChronicleClassOrPropertyDecorator {
|
|
25
|
-
return
|
|
26
|
+
return decorateModelBoundClassOrProperty((target: object, propertyKey?: string | symbol) => {
|
|
26
27
|
if (propertyKey !== undefined) {
|
|
27
28
|
const key = propertyKey.toString();
|
|
28
29
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, key);
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import 'reflect-metadata';
|
|
5
5
|
import { TypeIntrospector } from '../../types/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { decorateModelBoundProperty } from '../../types/modelBoundProperty.js';
|
|
7
|
+
import { ChroniclePropertyDecorator, getPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
7
8
|
|
|
8
9
|
/** Metadata stored by the count property decorator. */
|
|
9
10
|
export interface CountMetadata {
|
|
@@ -22,7 +23,7 @@ const METADATA_KEY = 'chronicle:projection:count';
|
|
|
22
23
|
* @returns A property decorator.
|
|
23
24
|
*/
|
|
24
25
|
export function count(eventType: Function, constantKey?: string): ChroniclePropertyDecorator {
|
|
25
|
-
return
|
|
26
|
+
return decorateModelBoundProperty((target: object, propertyKey: string | symbol) => {
|
|
26
27
|
const key = propertyKey.toString();
|
|
27
28
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, key);
|
|
28
29
|
const existing: CountMetadata[] = Reflect.getMetadata(METADATA_KEY, target, key) ?? [];
|
|
@@ -20,6 +20,12 @@ import { getSetFromMetadata, setFrom } from './setFrom.js';
|
|
|
20
20
|
import { getSetValueMetadata, setValue } from './setValue.js';
|
|
21
21
|
import { getSubtractFromMetadata, subtractFrom } from './subtractFrom.js';
|
|
22
22
|
import { getTrackedJsonSchemaProperties, jsonSchemaProperty } from '../../schemas/jsonSchemaProperty.js';
|
|
23
|
+
import { DecoratorType } from '../../types/DecoratorType.js';
|
|
24
|
+
import { TypeDiscoverer } from '../../types/TypeDiscoverer.js';
|
|
25
|
+
import { DefaultClientArtifactsProvider } from '../../artifacts/DefaultClientArtifactsProvider.js';
|
|
26
|
+
import { ReadModels } from '../../readModels/ReadModels.js';
|
|
27
|
+
import type { ChronicleConnection } from '../../connection/ChronicleConnection.js';
|
|
28
|
+
import { vi } from 'vitest';
|
|
23
29
|
|
|
24
30
|
class Changed {}
|
|
25
31
|
|
|
@@ -45,7 +51,46 @@ class Mappings {
|
|
|
45
51
|
@jsonSchemaProperty() tracked!: string;
|
|
46
52
|
}
|
|
47
53
|
|
|
54
|
+
class StandardMappedOnly {
|
|
55
|
+
@setFrom(Changed) value = '';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class UnconstructedMappedOnly {
|
|
59
|
+
@setFrom(Changed) value = '';
|
|
60
|
+
}
|
|
61
|
+
|
|
48
62
|
describe('standard model-bound decorators', () => {
|
|
63
|
+
it('collects a standard-mapped class for lazy root discovery when constructed without file discovery', () => {
|
|
64
|
+
new StandardMappedOnly();
|
|
65
|
+
new StandardMappedOnly();
|
|
66
|
+
expect(TypeDiscoverer.default.getTypeByDecoratorTypeAndName(DecoratorType.ReadModel, 'StandardMappedOnly'))
|
|
67
|
+
.toBeUndefined();
|
|
68
|
+
expect(DefaultClientArtifactsProvider.default.readModels.filter(type => type === StandardMappedOnly)).toHaveLength(1);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('registers the declaring class once even when subclasses and many instances are constructed', () => {
|
|
72
|
+
class Base {
|
|
73
|
+
@setFrom(Changed) value = '';
|
|
74
|
+
}
|
|
75
|
+
class Derived extends Base {}
|
|
76
|
+
const track = vi.spyOn(TypeDiscoverer.default, 'trackModelBoundProperty');
|
|
77
|
+
try {
|
|
78
|
+
for (let index = 0; index < 50; index++) new Base();
|
|
79
|
+
new Derived();
|
|
80
|
+
expect(track.mock.calls.filter(([type]) => type === Base)).toHaveLength(1);
|
|
81
|
+
expect(track.mock.calls.some(([type]) => type === Derived)).toBe(false);
|
|
82
|
+
} finally {
|
|
83
|
+
track.mockRestore();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should explain that an unconstructed property-only model was never registered', async () => {
|
|
88
|
+
const getInstanceByKey = vi.fn().mockResolvedValue({ ReadModel: '{"value":"stored"}' });
|
|
89
|
+
const connection = { readModels: { getInstanceByKey } } as unknown as ChronicleConnection;
|
|
90
|
+
const readModels = new ReadModels('store', 'Default', connection, DefaultClientArtifactsProvider.default, 'sink');
|
|
91
|
+
await expect(readModels.findInstanceById(UnconstructedMappedOnly, 'id')).rejects.toThrow(/add a class-level @fromEvent/);
|
|
92
|
+
expect(getInstanceByKey).not.toHaveBeenCalled();
|
|
93
|
+
});
|
|
49
94
|
it('stores class and property annotations without constructing an instance', () => {
|
|
50
95
|
const target = Mappings.prototype;
|
|
51
96
|
expect(isNoAutoMap(Mappings)).toBe(true);
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import 'reflect-metadata';
|
|
5
5
|
import { TypeIntrospector } from '../../types/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { decorateModelBoundProperty } from '../../types/modelBoundProperty.js';
|
|
7
|
+
import { ChroniclePropertyDecorator, getPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
7
8
|
|
|
8
9
|
/** Metadata stored by the decrement property decorator. */
|
|
9
10
|
export interface DecrementMetadata {
|
|
@@ -22,7 +23,7 @@ const METADATA_KEY = 'chronicle:projection:decrement';
|
|
|
22
23
|
* @returns A property decorator.
|
|
23
24
|
*/
|
|
24
25
|
export function decrement(eventType: Function, constantKey?: string): ChroniclePropertyDecorator {
|
|
25
|
-
return
|
|
26
|
+
return decorateModelBoundProperty((target: object, propertyKey: string | symbol) => {
|
|
26
27
|
const key = propertyKey.toString();
|
|
27
28
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, key);
|
|
28
29
|
const existing: DecrementMetadata[] = Reflect.getMetadata(METADATA_KEY, target, key) ?? [];
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import 'reflect-metadata';
|
|
5
5
|
import { TypeIntrospector } from '../../types/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { decorateModelBoundProperty } from '../../types/modelBoundProperty.js';
|
|
7
|
+
import { ChroniclePropertyDecorator, getPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
7
8
|
|
|
8
9
|
/** Metadata stored by the fromAll property decorator. */
|
|
9
10
|
export interface FromAllMetadata {
|
|
@@ -25,7 +26,7 @@ const METADATA_KEY = 'chronicle:projection:fromAll';
|
|
|
25
26
|
* @returns A property decorator.
|
|
26
27
|
*/
|
|
27
28
|
export function fromAll(property?: string, contextProperty?: string): ChroniclePropertyDecorator {
|
|
28
|
-
return
|
|
29
|
+
return decorateModelBoundProperty((target: object, propertyKey: string | symbol) => {
|
|
29
30
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, propertyKey.toString());
|
|
30
31
|
const metadata: FromAllMetadata = { property, contextProperty };
|
|
31
32
|
Reflect.defineMetadata(METADATA_KEY, metadata, target, propertyKey.toString());
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import 'reflect-metadata';
|
|
5
5
|
import { TypeIntrospector } from '../../types/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { decorateModelBoundProperty } from '../../types/modelBoundProperty.js';
|
|
7
|
+
import { ChroniclePropertyDecorator, getPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
7
8
|
|
|
8
9
|
/** Metadata stored by the fromEvery property decorator. */
|
|
9
10
|
export interface FromEveryMetadata {
|
|
@@ -22,7 +23,7 @@ const METADATA_KEY = 'chronicle:projection:fromEvery';
|
|
|
22
23
|
* @returns A property decorator.
|
|
23
24
|
*/
|
|
24
25
|
export function fromEvery(property?: string, contextProperty?: string): ChroniclePropertyDecorator {
|
|
25
|
-
return
|
|
26
|
+
return decorateModelBoundProperty((target: object, propertyKey: string | symbol) => {
|
|
26
27
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, propertyKey.toString());
|
|
27
28
|
const metadata: FromEveryMetadata = { property, contextProperty };
|
|
28
29
|
Reflect.defineMetadata(METADATA_KEY, metadata, target, propertyKey.toString());
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import 'reflect-metadata';
|
|
5
5
|
import { TypeIntrospector } from '../../types/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { decorateModelBoundProperty } from '../../types/modelBoundProperty.js';
|
|
7
|
+
import { ChroniclePropertyDecorator, getPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
7
8
|
|
|
8
9
|
/** Metadata stored by the increment property decorator. */
|
|
9
10
|
export interface IncrementMetadata {
|
|
@@ -22,7 +23,7 @@ const METADATA_KEY = 'chronicle:projection:increment';
|
|
|
22
23
|
* @returns A property decorator.
|
|
23
24
|
*/
|
|
24
25
|
export function increment(eventType: Function, constantKey?: string): ChroniclePropertyDecorator {
|
|
25
|
-
return
|
|
26
|
+
return decorateModelBoundProperty((target: object, propertyKey: string | symbol) => {
|
|
26
27
|
const key = propertyKey.toString();
|
|
27
28
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, key);
|
|
28
29
|
const existing: IncrementMetadata[] = Reflect.getMetadata(METADATA_KEY, target, key) ?? [];
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import 'reflect-metadata';
|
|
5
5
|
import { TypeIntrospector } from '../../types/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { decorateModelBoundProperty } from '../../types/modelBoundProperty.js';
|
|
7
|
+
import { ChroniclePropertyDecorator, getPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
7
8
|
|
|
8
9
|
/** Metadata stored by the join property decorator. */
|
|
9
10
|
export interface JoinMetadata {
|
|
@@ -25,7 +26,7 @@ const METADATA_KEY = 'chronicle:projection:join';
|
|
|
25
26
|
* @returns A property decorator.
|
|
26
27
|
*/
|
|
27
28
|
export function join(eventType: Function, on?: string, eventPropertyName?: string): ChroniclePropertyDecorator {
|
|
28
|
-
return
|
|
29
|
+
return decorateModelBoundProperty((target: object, propertyKey: string | symbol) => {
|
|
29
30
|
const key = propertyKey.toString();
|
|
30
31
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, key);
|
|
31
32
|
const existing: JoinMetadata[] = Reflect.getMetadata(METADATA_KEY, target, key) ?? [];
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import 'reflect-metadata';
|
|
5
5
|
import { TypeIntrospector } from '../../types/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { hasPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
7
|
+
import { decorateModelBoundProperty } from '../../types/modelBoundProperty.js';
|
|
7
8
|
|
|
8
9
|
const METADATA_KEY = 'chronicle:projection:nested';
|
|
9
10
|
|
|
@@ -13,7 +14,7 @@ const METADATA_KEY = 'chronicle:projection:nested';
|
|
|
13
14
|
* @param target - The class prototype.
|
|
14
15
|
* @param propertyKey - The property name.
|
|
15
16
|
*/
|
|
16
|
-
const decorateNested =
|
|
17
|
+
const decorateNested = decorateModelBoundProperty((target: object, propertyKey: string | symbol): void => {
|
|
17
18
|
const key = propertyKey.toString();
|
|
18
19
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, key);
|
|
19
20
|
Reflect.defineMetadata(METADATA_KEY, true, target, key);
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import 'reflect-metadata';
|
|
5
5
|
import { TypeIntrospector } from '../../types/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { hasPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
7
|
+
import { decorateModelBoundClassOrProperty } from '../../types/modelBoundProperty.js';
|
|
7
8
|
|
|
8
9
|
const CLASS_METADATA_KEY = 'chronicle:projection:noAutoMap:class';
|
|
9
10
|
const PROPERTY_METADATA_KEY = 'chronicle:projection:noAutoMap:property';
|
|
@@ -18,7 +19,7 @@ const PROPERTY_METADATA_KEY = 'chronicle:projection:noAutoMap:property';
|
|
|
18
19
|
* @param target - The class constructor, or the class prototype when used on a property.
|
|
19
20
|
* @param propertyKey - The property name, when used as a property decorator.
|
|
20
21
|
*/
|
|
21
|
-
const decorateNoAutoMap =
|
|
22
|
+
const decorateNoAutoMap = decorateModelBoundClassOrProperty((target: object, propertyKey?: string | symbol): void => {
|
|
22
23
|
if (propertyKey !== undefined) {
|
|
23
24
|
const key = propertyKey.toString();
|
|
24
25
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, key);
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import 'reflect-metadata';
|
|
5
5
|
import { TypeIntrospector } from '../../types/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { decorateModelBoundClassOrProperty } from '../../types/modelBoundProperty.js';
|
|
7
|
+
import { ChronicleClassOrPropertyDecorator, getPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
7
8
|
|
|
8
9
|
/** Metadata stored by the removedWith class or property decorator. */
|
|
9
10
|
export interface RemovedWithMetadata {
|
|
@@ -28,7 +29,7 @@ const PROPERTY_METADATA_KEY = 'chronicle:projection:removedWith:property';
|
|
|
28
29
|
* @returns A class and property decorator.
|
|
29
30
|
*/
|
|
30
31
|
export function removedWith(eventType: Function, key?: string, parentKey?: string): ChronicleClassOrPropertyDecorator {
|
|
31
|
-
return
|
|
32
|
+
return decorateModelBoundClassOrProperty((target: object, propertyKey?: string | symbol) => {
|
|
32
33
|
if (propertyKey !== undefined) {
|
|
33
34
|
const propKey = propertyKey.toString();
|
|
34
35
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, propKey);
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import 'reflect-metadata';
|
|
5
5
|
import { TypeIntrospector } from '../../types/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { decorateModelBoundClassOrProperty } from '../../types/modelBoundProperty.js';
|
|
7
|
+
import { ChronicleClassOrPropertyDecorator, getPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
7
8
|
|
|
8
9
|
/** Metadata stored by the removedWithJoin class or property decorator. */
|
|
9
10
|
export interface RemovedWithJoinMetadata {
|
|
@@ -23,7 +24,7 @@ const PROPERTY_METADATA_KEY = 'chronicle:projection:removedWithJoin:property';
|
|
|
23
24
|
* @returns A class and property decorator.
|
|
24
25
|
*/
|
|
25
26
|
export function removedWithJoin(eventType: Function, key?: string): ChronicleClassOrPropertyDecorator {
|
|
26
|
-
return
|
|
27
|
+
return decorateModelBoundClassOrProperty((target: object, propertyKey?: string | symbol) => {
|
|
27
28
|
if (propertyKey !== undefined) {
|
|
28
29
|
const propKey = propertyKey.toString();
|
|
29
30
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, propKey);
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import 'reflect-metadata';
|
|
5
5
|
import { TypeIntrospector } from '../../types/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { decorateModelBoundProperty } from '../../types/modelBoundProperty.js';
|
|
7
|
+
import { ChroniclePropertyDecorator, getPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
7
8
|
|
|
8
9
|
/** Metadata stored by the setFrom property decorator. */
|
|
9
10
|
export interface SetFromMetadata {
|
|
@@ -22,7 +23,7 @@ const METADATA_KEY = 'chronicle:projection:setFrom';
|
|
|
22
23
|
* @returns A property decorator.
|
|
23
24
|
*/
|
|
24
25
|
export function setFrom(eventType: Function, eventPropertyName?: string): ChroniclePropertyDecorator {
|
|
25
|
-
return
|
|
26
|
+
return decorateModelBoundProperty((target: object, propertyKey: string | symbol) => {
|
|
26
27
|
const key = propertyKey.toString();
|
|
27
28
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, key);
|
|
28
29
|
const existing: SetFromMetadata[] = Reflect.getMetadata(METADATA_KEY, target, key) ?? [];
|
|
@@ -5,7 +5,8 @@ import 'reflect-metadata';
|
|
|
5
5
|
import { PropertyAccessor, PropertyPathResolverProxyHandler } from '@cratis/fundamentals';
|
|
6
6
|
import type { EventContext } from '../../events/index.js';
|
|
7
7
|
import { TypeIntrospector } from '../../types/index.js';
|
|
8
|
-
import {
|
|
8
|
+
import { decorateModelBoundProperty } from '../../types/modelBoundProperty.js';
|
|
9
|
+
import { ChroniclePropertyDecorator, getPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
9
10
|
|
|
10
11
|
/** Metadata stored by the setFromContext property decorator. */
|
|
11
12
|
export interface SetFromContextMetadata {
|
|
@@ -29,7 +30,7 @@ export function setFromContext(
|
|
|
29
30
|
eventType: Function,
|
|
30
31
|
contextPropertyOrAccessor?: string | PropertyAccessor<EventContext>
|
|
31
32
|
): ChroniclePropertyDecorator {
|
|
32
|
-
return
|
|
33
|
+
return decorateModelBoundProperty((target: object, propertyKey: string | symbol) => {
|
|
33
34
|
const key = propertyKey.toString();
|
|
34
35
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, key);
|
|
35
36
|
const existing: SetFromContextMetadata[] = Reflect.getMetadata(METADATA_KEY, target, key) ?? [];
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import 'reflect-metadata';
|
|
5
5
|
import { TypeIntrospector } from '../../types/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { decorateModelBoundProperty } from '../../types/modelBoundProperty.js';
|
|
7
|
+
import { ChroniclePropertyDecorator, getPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
7
8
|
|
|
8
9
|
/** Metadata stored by the setValue property decorator. */
|
|
9
10
|
export interface SetValueMetadata {
|
|
@@ -22,7 +23,7 @@ const METADATA_KEY = 'chronicle:projection:setValue';
|
|
|
22
23
|
* @returns A property decorator.
|
|
23
24
|
*/
|
|
24
25
|
export function setValue(eventType: Function, value: unknown): ChroniclePropertyDecorator {
|
|
25
|
-
return
|
|
26
|
+
return decorateModelBoundProperty((target: object, propertyKey: string | symbol) => {
|
|
26
27
|
const key = propertyKey.toString();
|
|
27
28
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, key);
|
|
28
29
|
const existing: SetValueMetadata[] = Reflect.getMetadata(METADATA_KEY, target, key) ?? [];
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import 'reflect-metadata';
|
|
5
5
|
import { TypeIntrospector } from '../../types/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { decorateModelBoundProperty } from '../../types/modelBoundProperty.js';
|
|
7
|
+
import { ChroniclePropertyDecorator, getPropertyMetadata } from '../../types/propertyDecoratorMetadata.js';
|
|
7
8
|
|
|
8
9
|
/** Metadata stored by the subtractFrom property decorator. */
|
|
9
10
|
export interface SubtractFromMetadata {
|
|
@@ -22,7 +23,7 @@ const METADATA_KEY = 'chronicle:projection:subtractFrom';
|
|
|
22
23
|
* @returns A property decorator.
|
|
23
24
|
*/
|
|
24
25
|
export function subtractFrom(eventType: Function, eventPropertyName?: string): ChroniclePropertyDecorator {
|
|
25
|
-
return
|
|
26
|
+
return decorateModelBoundProperty((target: object, propertyKey: string | symbol) => {
|
|
26
27
|
const key = propertyKey.toString();
|
|
27
28
|
TypeIntrospector.trackProperty((target as { constructor: Function }).constructor, key);
|
|
28
29
|
const existing: SubtractFromMetadata[] = Reflect.getMetadata(METADATA_KEY, target, key) ?? [];
|
|
@@ -0,0 +1,5 @@
|
|
|
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
|
+
/** The wire representation of PropertyPath.NotSet, used for nested projections. */
|
|
5
|
+
export const notSetPropertyPath = '*NotSet*';
|
package/reactors/Reactors.ts
CHANGED
|
@@ -105,7 +105,9 @@ export class Reactors implements IReactors {
|
|
|
105
105
|
private readonly _lifecycle: ConnectionLifecycle;
|
|
106
106
|
private readonly _reactors = new Map<string, Constructor>();
|
|
107
107
|
private readonly _queues = new Map<string, AsyncQueue<ReactorMessage>>();
|
|
108
|
+
private readonly _observations = new Map<string, AbortController>();
|
|
108
109
|
private _registered = false;
|
|
110
|
+
private _disposed = false;
|
|
109
111
|
|
|
110
112
|
/**
|
|
111
113
|
* Creates a new {@link Reactors} instance.
|
|
@@ -133,6 +135,13 @@ export class Reactors implements IReactors {
|
|
|
133
135
|
});
|
|
134
136
|
}
|
|
135
137
|
|
|
138
|
+
/** Stops observations permanently when the owning client is disposed. */
|
|
139
|
+
dispose(): void {
|
|
140
|
+
this._disposed = true;
|
|
141
|
+
this._registered = false;
|
|
142
|
+
this.disconnectAll();
|
|
143
|
+
}
|
|
144
|
+
|
|
136
145
|
/** @inheritdoc */
|
|
137
146
|
async discover(): Promise<void> {
|
|
138
147
|
this._reactors.clear();
|
|
@@ -147,7 +156,7 @@ export class Reactors implements IReactors {
|
|
|
147
156
|
|
|
148
157
|
/** @inheritdoc */
|
|
149
158
|
async register(): Promise<void> {
|
|
150
|
-
if (this._registered) {
|
|
159
|
+
if (this._registered || this._disposed) {
|
|
151
160
|
return;
|
|
152
161
|
}
|
|
153
162
|
|
|
@@ -155,6 +164,7 @@ export class Reactors implements IReactors {
|
|
|
155
164
|
await this.discover();
|
|
156
165
|
}
|
|
157
166
|
|
|
167
|
+
if (this._disposed) return;
|
|
158
168
|
this._logger.info('Registering reactors', { count: this._reactors.size });
|
|
159
169
|
for (const [id, reactorType] of this._reactors) {
|
|
160
170
|
this.startObservation(id, reactorType);
|
|
@@ -164,6 +174,7 @@ export class Reactors implements IReactors {
|
|
|
164
174
|
}
|
|
165
175
|
|
|
166
176
|
private startObservation(id: string, reactorType: Constructor): void {
|
|
177
|
+
if (this._disposed) return;
|
|
167
178
|
const metadata = getReactorMetadata(reactorType)!;
|
|
168
179
|
const eventSequenceId = metadata.eventSequenceId ?? EventSequenceId.eventLog.value;
|
|
169
180
|
const eventTypes = this.getEventTypesFor(reactorType);
|
|
@@ -205,12 +216,12 @@ export class Reactors implements IReactors {
|
|
|
205
216
|
private scheduleReobserve(id: string, reactorType: Constructor): void {
|
|
206
217
|
// A disconnect clears the registration; the reconnect re-registers every
|
|
207
218
|
// reactor from scratch, so retrying here as well would double up.
|
|
208
|
-
if (!this._registered) {
|
|
219
|
+
if (!this._registered || this._disposed) {
|
|
209
220
|
return;
|
|
210
221
|
}
|
|
211
222
|
|
|
212
223
|
const handle = setTimeout(() => {
|
|
213
|
-
if (!this._registered) {
|
|
224
|
+
if (!this._registered || this._disposed) {
|
|
214
225
|
return;
|
|
215
226
|
}
|
|
216
227
|
|
|
@@ -228,7 +239,9 @@ export class Reactors implements IReactors {
|
|
|
228
239
|
eventTypes: EventTypeEntry[]
|
|
229
240
|
): Promise<void> {
|
|
230
241
|
const queue = new AsyncQueue<ReactorMessage>();
|
|
242
|
+
const controller = new AbortController();
|
|
231
243
|
this._queues.set(id, queue);
|
|
244
|
+
this._observations.set(id, controller);
|
|
232
245
|
|
|
233
246
|
queue.send({
|
|
234
247
|
Content: {
|
|
@@ -259,7 +272,7 @@ export class Reactors implements IReactors {
|
|
|
259
272
|
try {
|
|
260
273
|
const reactorInstance = new (reactorType as new () => Record<string, Function>)();
|
|
261
274
|
|
|
262
|
-
for await (const eventsToObserve of this._connection.reactors.observe(queue)) {
|
|
275
|
+
for await (const eventsToObserve of this._connection.reactors.observe(queue, { signal: controller.signal })) {
|
|
263
276
|
let lastSuccessfullyObservedEvent = SEQUENCE_NUMBER_UNAVAILABLE;
|
|
264
277
|
let state = ObservationState.Success;
|
|
265
278
|
const exceptionMessages: string[] = [];
|
|
@@ -358,7 +371,9 @@ export class Reactors implements IReactors {
|
|
|
358
371
|
// leak a duplicate stream on every reconnect.
|
|
359
372
|
if (this._queues.get(id) === queue) {
|
|
360
373
|
this._queues.delete(id);
|
|
374
|
+
this._observations.delete(id);
|
|
361
375
|
}
|
|
376
|
+
queue.complete();
|
|
362
377
|
}
|
|
363
378
|
}
|
|
364
379
|
|
|
@@ -422,9 +437,9 @@ export class Reactors implements IReactors {
|
|
|
422
437
|
}
|
|
423
438
|
|
|
424
439
|
private disconnectAll(): void {
|
|
425
|
-
for (const
|
|
426
|
-
|
|
427
|
-
|
|
440
|
+
for (const controller of this._observations.values()) controller.abort();
|
|
441
|
+
for (const queue of this._queues.values()) queue.complete();
|
|
442
|
+
this._observations.clear();
|
|
428
443
|
this._queues.clear();
|
|
429
444
|
}
|
|
430
445
|
}
|
package/reactors/reactor.ts
CHANGED
|
@@ -25,8 +25,8 @@ export interface ReactorMetadata {
|
|
|
25
25
|
* identifier. This is the TypeScript equivalent of the C# `[Reactor]` attribute.
|
|
26
26
|
*
|
|
27
27
|
* Reactors observe events from an event sequence and produce side effects.
|
|
28
|
-
* Method dispatch is by convention:
|
|
29
|
-
*
|
|
28
|
+
* Method dispatch is by convention: a handler's name is the event class name with
|
|
29
|
+
* its first letter lowercased (for example, `projectRegistered` handles `ProjectRegistered`).
|
|
30
30
|
*
|
|
31
31
|
* @param id - The unique identifier for the reactor. Defaults to the class name if omitted.
|
|
32
32
|
* @param eventSequenceId - Optional explicit event sequence identifier.
|
|
@@ -33,8 +33,9 @@ export interface IReadModels {
|
|
|
33
33
|
* @param readModelType - The read model type to retrieve.
|
|
34
34
|
* @param key - The read model key.
|
|
35
35
|
* @param sessionId - Optional session identifier.
|
|
36
|
-
* @returns The read model instance. When
|
|
37
|
-
*
|
|
36
|
+
* @returns The read model instance. When the kernel returns an empty document for a missing key,
|
|
37
|
+
* returns a prototype-only instance; a JSON `null` document may construct a model with default fields.
|
|
38
|
+
* Use {@link findInstanceById} when absence must be distinguished from stored data.
|
|
38
39
|
*/
|
|
39
40
|
getInstanceById<TReadModel>(readModelType: Constructor<TReadModel>, key: string, sessionId?: string): Promise<TReadModel>;
|
|
40
41
|
|
|
@@ -25,6 +25,28 @@ function createMaterializedReadModels(instancesJson: string[], releaseResponse:
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
describe('MaterializedReadModels', () => {
|
|
28
|
+
describe('when materialized models have undecorated members', () => {
|
|
29
|
+
class Model { name = ''; count = 0; occurred = new Date(0); }
|
|
30
|
+
const json = JSON.stringify({ name: 'stored', count: 3, occurred: '2024-02-01T00:00:00.000Z' });
|
|
31
|
+
|
|
32
|
+
it('should restore members in paged reads', async () => {
|
|
33
|
+
const { readModels } = createMaterializedReadModels([json]);
|
|
34
|
+
const [instance] = await readModels.getInstances(Model);
|
|
35
|
+
expect(instance).toMatchObject({ name: 'stored', count: 3 });
|
|
36
|
+
expect(instance.occurred).toEqual(new Date('2024-02-01T00:00:00.000Z'));
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should restore members in observed pages', async () => {
|
|
40
|
+
const connection = { materializedReadModels: { observeInstances: async function* () {
|
|
41
|
+
yield { Instances: [json] };
|
|
42
|
+
} } } as unknown as ChronicleConnection;
|
|
43
|
+
const readModels = new MaterializedReadModels('store', 'tenant', connection);
|
|
44
|
+
const response = await readModels.observeInstances(Model)[Symbol.asyncIterator]().next();
|
|
45
|
+
expect(response.value?.[0].name).toBe('stored');
|
|
46
|
+
expect(response.value?.[0].occurred).toBeInstanceOf(Date);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
28
50
|
describe('when a read model has a property decorated with @subject()', () => {
|
|
29
51
|
class Employee {
|
|
30
52
|
id = '';
|
|
@@ -7,6 +7,7 @@ import { ChronicleConnection } from '../connection/index.js';
|
|
|
7
7
|
import { JsonSchemaGenerator } from '../schemas/index.js';
|
|
8
8
|
import { getReadModelMetadata } from './readModel.js';
|
|
9
9
|
import { ReadModelSubjectResolver } from './ReadModelSubjectResolver.js';
|
|
10
|
+
import { deserializeReadModel } from './deserializeReadModel.js';
|
|
10
11
|
import type { IMaterializedReadModels } from './IMaterializedReadModels.js';
|
|
11
12
|
|
|
12
13
|
const defaultTake = 50;
|
|
@@ -82,10 +83,7 @@ export class MaterializedReadModels implements IMaterializedReadModels {
|
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
private deserialize<TReadModel>(readModelType: Constructor<TReadModel>, json: string): TReadModel {
|
|
85
|
-
|
|
86
|
-
return Object.create(readModelType.prototype) as TReadModel;
|
|
87
|
-
}
|
|
88
|
-
return JsonSerializer.deserialize(readModelType as Constructor<object>, json) as TReadModel;
|
|
86
|
+
return deserializeReadModel(readModelType, json);
|
|
89
87
|
}
|
|
90
88
|
|
|
91
89
|
private async releaseInstances<TReadModel>(readModelType: Constructor<TReadModel>, instances: TReadModel[]): Promise<TReadModel[]> {
|