@cratis/chronicle 6.17.0 → 6.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/projections/declarative/AddChildBuilder.d.ts +13 -2
  2. package/dist/projections/declarative/AddChildBuilder.d.ts.map +1 -1
  3. package/dist/projections/declarative/AddChildBuilder.js +27 -1
  4. package/dist/projections/declarative/AddChildBuilder.js.map +1 -1
  5. package/dist/projections/declarative/FromBuilder.d.ts +4 -0
  6. package/dist/projections/declarative/FromBuilder.d.ts.map +1 -1
  7. package/dist/projections/declarative/FromBuilder.js +1 -2
  8. package/dist/projections/declarative/FromBuilder.js.map +1 -1
  9. package/dist/projections/declarative/IAddChildBuilder.d.ts +12 -0
  10. package/dist/projections/declarative/IAddChildBuilder.d.ts.map +1 -1
  11. package/dist/projections/declarative/IReadModelPropertiesBuilder.d.ts +16 -1
  12. package/dist/projections/declarative/IReadModelPropertiesBuilder.d.ts.map +1 -1
  13. package/dist/projections/declarative/JoinBuilder.d.ts +4 -0
  14. package/dist/projections/declarative/JoinBuilder.d.ts.map +1 -1
  15. package/dist/projections/declarative/JoinBuilder.js +1 -2
  16. package/dist/projections/declarative/JoinBuilder.js.map +1 -1
  17. package/dist/projections/declarative/ProjectionBuilderCore.js +1 -1
  18. package/dist/projections/declarative/ProjectionBuilderCore.js.map +1 -1
  19. package/dist/projections/modelBound/childrenAndNestedBuilder.d.ts.map +1 -1
  20. package/dist/projections/modelBound/childrenAndNestedBuilder.js +41 -10
  21. package/dist/projections/modelBound/childrenAndNestedBuilder.js.map +1 -1
  22. package/dist/testing/IReadModelProcessor.d.ts +7 -0
  23. package/dist/testing/IReadModelProcessor.d.ts.map +1 -0
  24. package/dist/testing/IReadModelProcessor.js +4 -0
  25. package/dist/testing/IReadModelProcessor.js.map +1 -0
  26. package/dist/testing/ReadModelScenario.d.ts +6 -8
  27. package/dist/testing/ReadModelScenario.d.ts.map +1 -1
  28. package/dist/testing/ReadModelScenario.js +50 -50
  29. package/dist/testing/ReadModelScenario.js.map +1 -1
  30. package/dist/testing/ReadModelState.d.ts +6 -0
  31. package/dist/testing/ReadModelState.d.ts.map +1 -0
  32. package/dist/testing/ReadModelState.js +4 -0
  33. package/dist/testing/ReadModelState.js.map +1 -0
  34. package/dist/testing/ReducerReadModelProcessor.d.ts +13 -0
  35. package/dist/testing/ReducerReadModelProcessor.d.ts.map +1 -0
  36. package/dist/testing/ReducerReadModelProcessor.js +38 -0
  37. package/dist/testing/ReducerReadModelProcessor.js.map +1 -0
  38. package/dist/testing/ScenarioEvent.d.ts +8 -0
  39. package/dist/testing/ScenarioEvent.d.ts.map +1 -0
  40. package/dist/testing/ScenarioEvent.js +4 -0
  41. package/dist/testing/ScenarioEvent.js.map +1 -0
  42. package/dist/testing/index.d.ts +1 -0
  43. package/dist/testing/index.d.ts.map +1 -1
  44. package/dist/testing/index.js +1 -0
  45. package/dist/testing/index.js.map +1 -1
  46. package/dist/testing/projections/ProjectionCapabilities.d.ts +3 -0
  47. package/dist/testing/projections/ProjectionCapabilities.d.ts.map +1 -1
  48. package/dist/testing/projections/ProjectionCapabilities.js +117 -43
  49. package/dist/testing/projections/ProjectionCapabilities.js.map +1 -1
  50. package/dist/testing/projections/ProjectionExpressionEvaluator.d.ts +9 -0
  51. package/dist/testing/projections/ProjectionExpressionEvaluator.d.ts.map +1 -0
  52. package/dist/testing/projections/ProjectionExpressionEvaluator.js +68 -0
  53. package/dist/testing/projections/ProjectionExpressionEvaluator.js.map +1 -0
  54. package/dist/testing/projections/ProjectionReadModelProcessor.d.ts +30 -0
  55. package/dist/testing/projections/ProjectionReadModelProcessor.d.ts.map +1 -0
  56. package/dist/testing/projections/ProjectionReadModelProcessor.js +142 -0
  57. package/dist/testing/projections/ProjectionReadModelProcessor.js.map +1 -0
  58. package/dist/testing/projections/ProjectionValueConverter.d.ts +13 -0
  59. package/dist/testing/projections/ProjectionValueConverter.d.ts.map +1 -0
  60. package/dist/testing/projections/ProjectionValueConverter.js +122 -0
  61. package/dist/testing/projections/ProjectionValueConverter.js.map +1 -0
  62. package/dist/testing/projections/for_ProjectionCapabilities/given/Changed.d.ts +0 -2
  63. package/dist/testing/projections/for_ProjectionCapabilities/given/Changed.d.ts.map +1 -1
  64. package/dist/testing/projections/for_ProjectionCapabilities/given/Changed.js +0 -4
  65. package/dist/testing/projections/for_ProjectionCapabilities/given/Changed.js.map +1 -1
  66. package/dist/tsconfig.tsbuildinfo +1 -1
  67. package/package.json +2 -2
  68. package/projections/ProjectionDefinitionCompiler.registration.spec.ts +4 -2
  69. package/projections/Projections.childrenAndNested.spec.ts +250 -1
  70. package/projections/declarative/AddChildBuilder.ts +33 -2
  71. package/projections/declarative/FromBuilder.ts +9 -1
  72. package/projections/declarative/IAddChildBuilder.ts +14 -0
  73. package/projections/declarative/IReadModelPropertiesBuilder.ts +18 -1
  74. package/projections/declarative/JoinBuilder.ts +9 -1
  75. package/projections/declarative/ProjectionBuilderCore.ts +1 -1
  76. package/projections/declarative/addChildCompatibility.standard.spec.ts +80 -0
  77. package/projections/eventContextExpressions.spec.ts +20 -0
  78. package/projections/modelBound/childrenAndNestedBuilder.ts +45 -11
  79. package/testing/IReadModelProcessor.ts +10 -0
  80. package/testing/ReadModelScenario.spec.ts +156 -10
  81. package/testing/ReadModelScenario.ts +57 -57
  82. package/testing/ReadModelState.ts +5 -0
  83. package/testing/ReducerReadModelProcessor.ts +39 -0
  84. package/testing/ScenarioEvent.ts +7 -0
  85. package/testing/index.ts +1 -0
  86. package/testing/projections/ProjectionCapabilities.ts +100 -39
  87. package/testing/projections/ProjectionExpressionEvaluator.ts +57 -0
  88. package/testing/projections/ProjectionReadModelProcessor.fixtures.spec.ts +135 -0
  89. package/testing/projections/ProjectionReadModelProcessor.ts +147 -0
  90. package/testing/projections/ProjectionValueConverter.spec.ts +81 -0
  91. package/testing/projections/ProjectionValueConverter.ts +113 -0
  92. package/testing/projections/for_ProjectionCapabilities/given/Changed.ts +1 -3
  93. package/testing/projections/for_ProjectionCapabilities/when_rejecting_unsupported_operations.spec.ts +81 -1
  94. package/testing/projections/for_ProjectionCapabilities/when_validating_numeric_schemas.spec.ts +40 -12
  95. package/testing/projections/for_ProjectionCapabilities/when_validating_schema_boundaries.spec.ts +76 -29
  96. package/testing/projections/for_ProjectionCapabilities/when_validating_supported_operations.spec.ts +8 -35
@@ -1 +1 @@
1
- {"version":3,"file":"ReadModelScenario.d.ts","sourceRoot":"","sources":["../../testing/ReadModelScenario.ts"],"names":[],"mappings":"AAGA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAczF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC;AAEnF,KAAK,iBAAiB,GAAG,IAAI,CAAC,wBAAwB,EAAE,UAAU,GAAG,YAAY,GAAG,aAAa,CAAC,CAAC;AAKnG;;;;GAIG;AACH,qBAAa,iBAAiB,CAAC,UAAU,SAAS,MAAM;IACpD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA0B;IACzD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAc;IAC3C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyB;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAA6D;IAE7E,+DAA+D;IAC/D,YAAY,aAAa,EAAE,WAAW,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC,EAAE,iBAAiB,EAwBhF;IAED,4CAA4C;IAC5C,IAAI,KAAK,IAAI,6BAA6B,CAAC,UAAU,CAAC,CAErD;IAED,yEAAyE;IACzE,IAAI,QAAQ,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAQzC;IAED,4FAA4F;IACtF,wBAAwB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAErE;IAED,yEAAyE;IACnE,0BAA0B,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE7D;IAED,yFAAyF;IACzF,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CA0B5D;IAED,OAAO,CAAC,OAAO;YAQD,MAAM;CAqBvB"}
1
+ {"version":3,"file":"ReadModelScenario.d.ts","sourceRoot":"","sources":["../../testing/ReadModelScenario.ts"],"names":[],"mappings":"AAGA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAazF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC;AAQnF,KAAK,iBAAiB,GAAG,IAAI,CAAC,wBAAwB,EAAE,UAAU,GAAG,YAAY,GAAG,aAAa,CAAC,GAC9F,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAE,YAAY,GAAG,mBAAmB,CAAC,CAAC,CAAC;AAEhF;;;;GAIG;AACH,qBAAa,iBAAiB,CAAC,UAAU,SAAS,MAAM;IACpD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;IAC7D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAA+D;IAE/E,qFAAqF;IACrF,YAAY,aAAa,EAAE,WAAW,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC,EAAE,iBAAiB,EAwChF;IAED,4CAA4C;IAC5C,IAAI,KAAK,IAAI,6BAA6B,CAAC,UAAU,CAAC,CAErD;IAED,yEAAyE;IACzE,IAAI,QAAQ,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAQzC;IAED,4FAA4F;IACtF,wBAAwB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAErE;IAED,yEAAyE;IACnE,0BAA0B,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE7D;IAED,yFAAyF;IACzF,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CA0B5D;IAED,OAAO,CAAC,OAAO;CAUlB"}
@@ -2,6 +2,7 @@
2
2
  // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
3
  import 'reflect-metadata';
4
4
  import { JsonSerializer } from '@cratis/fundamentals';
5
+ import { DefaultClientArtifactsProvider } from '../artifacts/DefaultClientArtifactsProvider.js';
5
6
  import { getEventTypeMetadata } from '../events/eventTypeDecorator.js';
6
7
  import { getFromEventMetadata } from '../projections/modelBound/fromEvent.js';
7
8
  import { getRemovedWithClassMetadata } from '../projections/modelBound/removedWith.js';
@@ -10,47 +11,65 @@ import { getClearWithClassMetadata } from '../projections/modelBound/clearWith.j
10
11
  import { getVariantOfMetadata } from '../projections/modelBound/variantOf.js';
11
12
  import { getEntersOnMetadata } from '../projections/modelBound/entersOn.js';
12
13
  import { getProjectionMetadata } from '../projections/declarative/projection.js';
14
+ import { ProjectionDefinitionCompiler } from '../projections/ProjectionDefinitionCompiler.js';
15
+ import { getReadModelId } from '../readModels/readModel.js';
13
16
  import { getReducerMetadata } from '../reducers/reducer.js';
14
- import { ReducerEventDispatcher } from '../reducers/ReducerEventDispatcher.js';
15
- import { DecoratorType } from '../types/DecoratorType.js';
16
17
  import { hasModelBoundProperties, TypeDiscoverer } from '../types/TypeDiscoverer.js';
17
18
  import { ReadModelScenarioGivenBuilder } from './ReadModelScenarioGivenBuilder.js';
19
+ import { ReducerReadModelProcessor } from './ReducerReadModelProcessor.js';
20
+ import { ProjectionReadModelProcessor } from './projections/ProjectionReadModelProcessor.js';
21
+ import { UnsupportedProjectionOperation } from './projections/UnsupportedProjectionOperation.js';
18
22
  /**
19
- * Folds seeded events through a reducer in-process, without a Chronicle kernel.
20
- * Projections, observer scheduling, storage, migrations, and compliance are not simulated.
23
+ * Folds seeded events through a reducer or a validated flat projection, without a Chronicle kernel.
24
+ * Observer scheduling, storage, migrations, and compliance are not simulated.
21
25
  * Supply an artifact catalog to isolate a scenario from process-wide decorator discovery.
22
26
  */
23
27
  export class ReadModelScenario {
24
- _readModelType;
25
- _reducerType;
26
- _dispatcher;
28
+ _processor;
29
+ _modelName;
27
30
  _events = [];
28
31
  _results;
29
- /** Selects the reducer associated with the read model type. */
32
+ /** Selects the reducer (when present) or a single applicable compiled projection. */
30
33
  constructor(readModelType, artifacts) {
31
- this._readModelType = readModelType;
32
- const registered = artifacts ?? {
33
- reducers: TypeDiscoverer.default.getTypesByDecoratorType(DecoratorType.Reducer),
34
- eventTypes: TypeDiscoverer.default.getTypesByDecoratorType(DecoratorType.EventType),
35
- projections: TypeDiscoverer.default.getTypesByDecoratorType(DecoratorType.Projection)
36
- };
34
+ this._modelName = readModelType.name;
35
+ const registered = artifacts ?? new DefaultClientArtifactsProvider(TypeDiscoverer.default);
37
36
  const reducerTypes = registered.reducers.filter(type => getReducerMetadata(type)?.readModel === readModelType);
38
37
  if (reducerTypes.length > 1) {
39
38
  throw new Error(`Multiple reducers found for read model '${readModelType.name}'.`);
40
39
  }
41
- if (reducerTypes.length === 0) {
42
- const projected = getFromEventMetadata(readModelType).length > 0 || hasModelBoundProperties(readModelType) ||
43
- getRemovedWithClassMetadata(readModelType).length > 0 || getRemovedWithJoinClassMetadata(readModelType).length > 0 ||
44
- getClearWithClassMetadata(readModelType).length > 0 || getVariantOfMetadata(readModelType) !== undefined ||
45
- getEntersOnMetadata(readModelType).length > 0 ||
46
- registered.projections.some(type => getProjectionMetadata(type)?.readModelType === readModelType);
47
- if (projected) {
48
- throw new Error(`Projection-backed read model '${readModelType.name}' is not supported yet; use a kernel-backed test.`);
49
- }
50
- throw new Error(`No reducer found for read model '${readModelType.name}'. Pass the read model type to @reducer; projections are not supported yet (use a kernel-backed test).`);
40
+ if (reducerTypes.length) {
41
+ this._processor = new ReducerReadModelProcessor(readModelType, reducerTypes[0], registered.eventTypes);
42
+ return;
43
+ }
44
+ const modelBound = getFromEventMetadata(readModelType).length > 0 || hasModelBoundProperties(readModelType) ||
45
+ getRemovedWithClassMetadata(readModelType).length > 0 || getRemovedWithJoinClassMetadata(readModelType).length > 0 ||
46
+ getClearWithClassMetadata(readModelType).length > 0 || getVariantOfMetadata(readModelType) !== undefined ||
47
+ getEntersOnMetadata(readModelType).length > 0;
48
+ // Definitions without a declared model can be associated by the registration compiler's
49
+ // unambiguous schema inference; never guess their association from decorators here.
50
+ const declarative = registered.projections.filter(type => {
51
+ const metadata = getProjectionMetadata(type);
52
+ return metadata !== undefined && (metadata.readModelType === readModelType ||
53
+ (metadata.readModelType === undefined && registered.readModels?.includes(readModelType)));
54
+ });
55
+ if (Number(modelBound) + declarative.filter(type => getProjectionMetadata(type)?.readModelType === readModelType).length > 1) {
56
+ throw new Error(`Multiple projections found for read model '${readModelType.name}'.`);
57
+ }
58
+ if (!modelBound && !declarative.length) {
59
+ throw new Error(`No reducer or projection found for read model '${readModelType.name}'.`);
60
+ }
61
+ const catalog = {
62
+ eventTypes: registered.eventTypes, reducers: registered.reducers, projections: declarative,
63
+ readModels: [...new Set([...(registered.readModels ?? []), readModelType])],
64
+ globalForHandlers: artifacts?.globalForHandlers ?? [], reactors: [], seeders: [], constraints: [],
65
+ webhooks: [], eventTypeMigrations: []
66
+ };
67
+ const compiled = new ProjectionDefinitionCompiler(catalog, 'scenario').compile(declarative, modelBound ? [readModelType] : []);
68
+ const definitions = compiled.definitions.filter(definition => definition.ReadModel === getReadModelId(readModelType));
69
+ if (definitions.length !== 1) {
70
+ throw new Error(`Expected one projection for read model '${readModelType.name}', found ${definitions.length}.`);
51
71
  }
52
- this._reducerType = reducerTypes[0];
53
- this._dispatcher = new ReducerEventDispatcher(this._reducerType, registered.eventTypes);
72
+ this._processor = new ProjectionReadModelProcessor(readModelType, compiled, definitions[0]);
54
73
  }
55
74
  /** Fluent entry point for event history. */
56
75
  get given() {
@@ -106,32 +125,13 @@ export class ReadModelScenario {
106
125
  process() {
107
126
  if (!this._results) {
108
127
  const events = [...this._events];
109
- this._results = this.reduce(events);
110
- }
111
- return this._results;
112
- }
113
- async reduce(events) {
114
- const results = new Map();
115
- const reducer = new this._reducerType();
116
- for (const event of events) {
117
- const handler = this._dispatcher.handlerFor(event.context.eventType.id.value);
118
- if (!handler)
119
- continue;
120
- const prior = results.get(event.sourceId);
121
- const previous = prior?.deleted ? undefined : prior?.instance;
122
- let next;
123
- try {
124
- next = await this._dispatcher.invoke(reducer, handler, event.content, previous, event.context);
125
- }
126
- catch (cause) {
127
- throw new Error(`Reducer '${this._reducerType.name}' for read model '${this._readModelType.name}' failed`, { cause });
128
- }
129
- results.set(event.sourceId, {
130
- instance: next === undefined ? null : next,
131
- deleted: next === undefined
128
+ this._results = this._processor.process(events).catch(error => {
129
+ if (!(this._processor instanceof ProjectionReadModelProcessor) || error instanceof UnsupportedProjectionOperation)
130
+ throw error;
131
+ throw new Error(`Projection replay for read model '${this._modelName}' failed: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
132
132
  });
133
133
  }
134
- return results;
134
+ return this._results;
135
135
  }
136
136
  }
137
137
  //# sourceMappingURL=ReadModelScenario.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ReadModelScenario.js","sourceRoot":"","sources":["../../testing/ReadModelScenario.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ReadModelScenario.js","sourceRoot":"","sources":["../../testing/ReadModelScenario.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,qGAAqG;AAErG,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,8BAA8B,EAAE,MAAM,gDAAgD,CAAC;AAEhG,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;AACvF,OAAO,EAAE,+BAA+B,EAAE,MAAM,8CAA8C,CAAC;AAC/F,OAAO,EAAE,yBAAyB,EAAE,MAAM,wCAAwC,CAAC;AACnF,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,EAAE,4BAA4B,EAAE,MAAM,gDAAgD,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACrF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC;AAGnF,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAE3E,OAAO,EAAE,4BAA4B,EAAE,MAAM,+CAA+C,CAAC;AAC7F,OAAO,EAAE,8BAA8B,EAAE,MAAM,iDAAiD,CAAC;AAKjG;;;;GAIG;AACH,MAAM,OAAO,iBAAiB;IACT,UAAU,CAAkC;IAC5C,UAAU,CAAS;IACnB,OAAO,GAAoB,EAAE,CAAC;IACvC,QAAQ,CAA+D;IAE/E,qFAAqF;IACrF,YAAY,aAAsC,EAAE,SAA6B;QAC7E,IAAI,CAAC,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC;QACrC,MAAM,UAAU,GAAG,SAAS,IAAI,IAAI,8BAA8B,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAC3F,MAAM,YAAY,GAAG,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,SAAS,KAAK,aAAa,CAAC,CAAC;QAC/G,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,2CAA2C,aAAa,CAAC,IAAI,IAAI,CAAC,CAAC;QACvF,CAAC;QACD,IAAI,YAAY,CAAC,MAAM,EAAE,CAAC;YACtB,IAAI,CAAC,UAAU,GAAG,IAAI,yBAAyB,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;YACvG,OAAO;QACX,CAAC;QACD,MAAM,UAAU,GAAG,oBAAoB,CAAC,aAAa,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,uBAAuB,CAAC,aAAa,CAAC;YACvG,2BAA2B,CAAC,aAAa,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,+BAA+B,CAAC,aAAa,CAAC,CAAC,MAAM,GAAG,CAAC;YAClH,yBAAyB,CAAC,aAAa,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,oBAAoB,CAAC,aAAa,CAAC,KAAK,SAAS;YACxG,mBAAmB,CAAC,aAAa,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QAClD,wFAAwF;QACxF,oFAAoF;QACpF,MAAM,WAAW,GAAG,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;YACrD,MAAM,QAAQ,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;YAC7C,OAAO,QAAQ,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,aAAa,KAAK,aAAa;gBACtE,CAAC,QAAQ,CAAC,aAAa,KAAK,SAAS,IAAI,UAAU,CAAC,UAAU,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QAClG,CAAC,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAAE,aAAa,KAAK,aAAa,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3H,MAAM,IAAI,KAAK,CAAC,8CAA8C,aAAa,CAAC,IAAI,IAAI,CAAC,CAAC;QAC1F,CAAC;QACD,IAAI,CAAC,UAAU,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,kDAAkD,aAAa,CAAC,IAAI,IAAI,CAAC,CAAC;QAC9F,CAAC;QACD,MAAM,OAAO,GAA6B;YACtC,UAAU,EAAE,UAAU,CAAC,UAAU,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,WAAW,EAAE,WAAW;YAC1F,UAAU,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC;YAC3E,iBAAiB,EAAE,SAAS,EAAE,iBAAiB,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE;YACjG,QAAQ,EAAE,EAAE,EAAE,mBAAmB,EAAE,EAAE;SACxC,CAAC;QACF,MAAM,QAAQ,GAAG,IAAI,4BAA4B,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/H,MAAM,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,KAAK,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;QACtH,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,2CAA2C,aAAa,CAAC,IAAI,YAAY,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC;QACpH,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,IAAI,4BAA4B,CAAC,aAAa,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAChG,CAAC;IAED,4CAA4C;IAC5C,IAAI,KAAK;QACL,OAAO,IAAI,6BAA6B,CAAC,IAAI,CAAC,CAAC;IACnD,CAAC;IAED,yEAAyE;IACzE,IAAI,QAAQ;QACR,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;YACjC,MAAM,YAAY,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC;YACtF,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CAAC,+EAA+E,CAAC,CAAC;YACrG,CAAC;YACD,OAAO,YAAY,CAAC,CAAC,CAAC,EAAE,QAAQ,IAAI,IAAI,CAAC;QAC7C,CAAC,CAAC,CAAC;IACP,CAAC;IAED,4FAA4F;IAC5F,KAAK,CAAC,wBAAwB,CAAC,EAAU;QACrC,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,QAAQ,IAAI,IAAI,CAAC;IAC5D,CAAC;IAED,yEAAyE;IACzE,KAAK,CAAC,0BAA0B,CAAC,EAAU;QACvC,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,OAAO,IAAI,KAAK,CAAC;IAC5D,CAAC;IAED,yFAAyF;IACzF,gBAAgB,CAAC,EAAU,EAAE,MAAyB;QAClD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,oBAAoB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YACzD,IAAI,CAAC,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,UAAU,KAAK,CAAC,WAAW,CAAC,IAAI,+BAA+B,CAAC,CAAC;YAChG,8EAA8E;YAC9E,MAAM,OAAO,GAAY,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;YACrE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;gBACd,QAAQ,EAAE,EAAE;gBACZ,OAAO;gBACP,OAAO,EAAE;oBACL,aAAa,EAAE,EAAE;oBACjB,eAAe,EAAE,SAAS;oBAC1B,eAAe,EAAE,KAAK;oBACtB,aAAa,EAAE,SAAS;oBACxB,UAAU,EAAE,UAAU;oBACtB,SAAS,EAAE,UAAU;oBACrB,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;oBAC3C,SAAS,EAAE,QAAQ,CAAC,SAAS;oBAC7B,QAAQ,EAAE,IAAI,IAAI,EAAE;oBACpB,aAAa,EAAE,sCAAsC;oBACrD,SAAS,EAAE,EAAE;oBACb,IAAI,EAAE,EAAE;iBACX;aACJ,CAAC,CAAC;QACP,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;IAC9B,CAAC;IAEO,OAAO;QACX,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;YACjC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;gBAC1D,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,YAAY,4BAA4B,CAAC,IAAI,KAAK,YAAY,8BAA8B;oBAAE,MAAM,KAAK,CAAC;gBAC/H,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,CAAC,UAAU,aAAa,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YACjK,CAAC,CAAC,CAAC;QACP,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC;IACzB,CAAC;CACJ"}
@@ -0,0 +1,6 @@
1
+ /** Materialized state and explicit removal status for one source. */
2
+ export type ReadModelState<T> = {
3
+ instance: T | null;
4
+ deleted: boolean;
5
+ };
6
+ //# sourceMappingURL=ReadModelState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReadModelState.d.ts","sourceRoot":"","sources":["../../testing/ReadModelState.ts"],"names":[],"mappings":"AAGA,qEAAqE;AACrE,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC"}
@@ -0,0 +1,4 @@
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
+ export {};
4
+ //# sourceMappingURL=ReadModelState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReadModelState.js","sourceRoot":"","sources":["../../testing/ReadModelState.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,qGAAqG"}
@@ -0,0 +1,13 @@
1
+ import type { Constructor } from '@cratis/fundamentals';
2
+ import type { IReadModelProcessor } from './IReadModelProcessor.js';
3
+ import type { ReadModelState } from './ReadModelState.js';
4
+ import type { ScenarioEvent } from './ScenarioEvent.js';
5
+ /** Preserves the SDK reducer dispatch and the scenario's null-versus-deletion semantics. */
6
+ export declare class ReducerReadModelProcessor<TReadModel extends object> implements IReadModelProcessor<TReadModel> {
7
+ private readonly _readModelType;
8
+ private readonly _reducerType;
9
+ private readonly _dispatcher;
10
+ constructor(_readModelType: Constructor<TReadModel>, _reducerType: Constructor, eventTypes: Constructor[]);
11
+ process(events: readonly ScenarioEvent[]): Promise<Map<string, ReadModelState<TReadModel>>>;
12
+ }
13
+ //# sourceMappingURL=ReducerReadModelProcessor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReducerReadModelProcessor.d.ts","sourceRoot":"","sources":["../../testing/ReducerReadModelProcessor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,4FAA4F;AAC5F,qBAAa,yBAAyB,CAAC,UAAU,SAAS,MAAM,CAAE,YAAW,mBAAmB,CAAC,UAAU,CAAC;IAG5F,OAAO,CAAC,QAAQ,CAAC,cAAc;IAA2B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAFnG,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyB;IAErD,YAA6B,cAAc,EAAE,WAAW,CAAC,UAAU,CAAC,EAAmB,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,EAE1I;IAEK,OAAO,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,CAoBhG;CACJ"}
@@ -0,0 +1,38 @@
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 { ReducerEventDispatcher } from '../reducers/ReducerEventDispatcher.js';
4
+ /** Preserves the SDK reducer dispatch and the scenario's null-versus-deletion semantics. */
5
+ export class ReducerReadModelProcessor {
6
+ _readModelType;
7
+ _reducerType;
8
+ _dispatcher;
9
+ constructor(_readModelType, _reducerType, eventTypes) {
10
+ this._readModelType = _readModelType;
11
+ this._reducerType = _reducerType;
12
+ this._dispatcher = new ReducerEventDispatcher(_reducerType, eventTypes);
13
+ }
14
+ async process(events) {
15
+ const results = new Map();
16
+ const reducer = new this._reducerType();
17
+ for (const event of events) {
18
+ const handler = this._dispatcher.handlerFor(event.context.eventType.id.value);
19
+ if (!handler)
20
+ continue;
21
+ const prior = results.get(event.sourceId);
22
+ const previous = prior?.deleted ? undefined : prior?.instance;
23
+ let next;
24
+ try {
25
+ next = await this._dispatcher.invoke(reducer, handler, event.content, previous, event.context);
26
+ }
27
+ catch (cause) {
28
+ throw new Error(`Reducer '${this._reducerType.name}' for read model '${this._readModelType.name}' failed`, { cause });
29
+ }
30
+ results.set(event.sourceId, {
31
+ instance: next === undefined ? null : next,
32
+ deleted: next === undefined
33
+ });
34
+ }
35
+ return results;
36
+ }
37
+ }
38
+ //# sourceMappingURL=ReducerReadModelProcessor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReducerReadModelProcessor.js","sourceRoot":"","sources":["../../testing/ReducerReadModelProcessor.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,qGAAqG;AAGrG,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAK/E,4FAA4F;AAC5F,MAAM,OAAO,yBAAyB;IAGL,cAAc;IAA4C,YAAY;IAFlF,WAAW,CAAyB;IAErD,YAA6B,cAAuC,EAAmB,YAAyB,EAAE,UAAyB;8BAA9G,cAAc;4BAA4C,YAAY;QAC/F,IAAI,CAAC,WAAW,GAAG,IAAI,sBAAsB,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAC5E,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,MAAgC;QAC1C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAsC,CAAC;QAC9D,MAAM,OAAO,GAAG,IAAK,IAAI,CAAC,YAAmD,EAAE,CAAC;QAChF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YACzB,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;YAC9E,IAAI,CAAC,OAAO;gBAAE,SAAS;YACvB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC1C,MAAM,QAAQ,GAAG,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC;YAC9D,IAAI,IAAa,CAAC;YAClB,IAAI,CAAC;gBACD,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YACnG,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,CAAC,YAAY,CAAC,IAAI,qBAAqB,IAAI,CAAC,cAAc,CAAC,IAAI,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAC1H,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE;gBACxB,QAAQ,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAyB;gBAC/D,OAAO,EAAE,IAAI,KAAK,SAAS;aAC9B,CAAC,CAAC;QACP,CAAC;QACD,OAAO,OAAO,CAAC;IACnB,CAAC;CACJ"}
@@ -0,0 +1,8 @@
1
+ import type { EventContext } from '../events/EventContext.js';
2
+ /** An event serialized at the scenario boundary. */
3
+ export type ScenarioEvent = {
4
+ sourceId: string;
5
+ content: unknown;
6
+ context: EventContext;
7
+ };
8
+ //# sourceMappingURL=ScenarioEvent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScenarioEvent.d.ts","sourceRoot":"","sources":["../../testing/ScenarioEvent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,oDAAoD;AACpD,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,CAAC"}
@@ -0,0 +1,4 @@
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
+ export {};
4
+ //# sourceMappingURL=ScenarioEvent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScenarioEvent.js","sourceRoot":"","sources":["../../testing/ScenarioEvent.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,qGAAqG"}
@@ -1,4 +1,5 @@
1
1
  export { ReadModelScenario } from './ReadModelScenario.js';
2
2
  export { ReadModelScenarioGiven } from './ReadModelScenarioGiven.js';
3
3
  export { ReadModelScenarioGivenBuilder } from './ReadModelScenarioGivenBuilder.js';
4
+ export { UnsupportedProjectionOperation } from './projections/UnsupportedProjectionOperation.js';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../testing/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../testing/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC;AACnF,OAAO,EAAE,8BAA8B,EAAE,MAAM,iDAAiD,CAAC"}
@@ -3,4 +3,5 @@
3
3
  export { ReadModelScenario } from './ReadModelScenario.js';
4
4
  export { ReadModelScenarioGiven } from './ReadModelScenarioGiven.js';
5
5
  export { ReadModelScenarioGivenBuilder } from './ReadModelScenarioGivenBuilder.js';
6
+ export { UnsupportedProjectionOperation } from './projections/UnsupportedProjectionOperation.js';
6
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../testing/index.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,qGAAqG;AAErG,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../testing/index.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,qGAAqG;AAErG,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC;AACnF,OAAO,EAAE,8BAA8B,EAAE,MAAM,iDAAiD,CAAC"}
@@ -10,8 +10,11 @@ export declare class ProjectionCapabilities {
10
10
  static validate(compiled: CompiledProjectionDefinitions, definition: ProjectionDefinition): void;
11
11
  private static checkAutoMap;
12
12
  private static checkMapping;
13
+ private static compatible;
14
+ private static checkInitialValue;
13
15
  private static propertyAt;
14
16
  private static numeric;
17
+ private static checkProtection;
15
18
  private static checkSchema;
16
19
  }
17
20
  //# sourceMappingURL=ProjectionCapabilities.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectionCapabilities.d.ts","sourceRoot":"","sources":["../../../testing/projections/ProjectionCapabilities.ts"],"names":[],"mappings":"AAGA,OAAO,EAAW,KAAK,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEjF,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AASxG,gGAAgG;AAChG,qBAAa,sBAAsB;IAC/B;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,6BAA6B,EAAE,UAAU,EAAE,oBAAoB,GAAG,IAAI,CA2F/F;IAED,OAAO,CAAC,MAAM,CAAC,YAAY;IAY3B,OAAO,CAAC,MAAM,CAAC,YAAY;IA+D3B,OAAO,CAAC,MAAM,CAAC,UAAU;IAIzB,OAAO,CAAC,MAAM,CAAC,OAAO;IAKtB,OAAO,CAAC,MAAM,CAAC,WAAW;CAc7B"}
1
+ {"version":3,"file":"ProjectionCapabilities.d.ts","sourceRoot":"","sources":["../../../testing/projections/ProjectionCapabilities.ts"],"names":[],"mappings":"AAGA,OAAO,EAAW,KAAK,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEjF,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAOxG,gGAAgG;AAChG,qBAAa,sBAAsB;IAC/B;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,6BAA6B,EAAE,UAAU,EAAE,oBAAoB,GAAG,IAAI,CA6F/F;IAED,OAAO,CAAC,MAAM,CAAC,YAAY;IAa3B,OAAO,CAAC,MAAM,CAAC,YAAY;IA0E3B,OAAO,CAAC,MAAM,CAAC,UAAU;IAczB,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAuBhC,OAAO,CAAC,MAAM,CAAC,UAAU;IAIzB,OAAO,CAAC,MAAM,CAAC,OAAO;IAKtB,OAAO,CAAC,MAAM,CAAC,eAAe;IAS9B,OAAO,CAAC,MAAM,CAAC,WAAW;CAiB7B"}
@@ -3,8 +3,6 @@
3
3
  import { AutoMap } from '@cratis/chronicle.contracts';
4
4
  import { EventSequenceId } from '../../eventSequences/EventSequenceId.js';
5
5
  import { eventContractPath } from '../../projections/eventContractPath.js';
6
- import { eventContextPropertyExpression } from '../../projections/eventContextPropertyExpression.js';
7
- import { InvalidEventContextPropertyError } from '../../projections/InvalidEventContextPropertyError.js';
8
6
  import { getEventTypeMapKey } from '../../projections/modelBound/childrenAndNestedBuilder.js';
9
7
  import { UnsupportedProjectionOperation } from './UnsupportedProjectionOperation.js';
10
8
  /** Validates *all* subscribed operations against the bounded scenario subset, before replay. */
@@ -34,6 +32,8 @@ export class ProjectionCapabilities {
34
32
  reject('Variant', 'variants require a kernel-backed test');
35
33
  if (wire.IsActive === false)
36
34
  reject('IsActive', 'passive projections require a kernel-backed test');
35
+ if (wire.SubscribesToAllEvents === true)
36
+ reject('SubscribesToAllEvents', 'subscribe-to-all projections require a kernel-backed test');
37
37
  if (wire.EventSequenceId !== EventSequenceId.eventLog.value)
38
38
  reject('EventSequenceId', 'non-default event sequences require a kernel-backed test');
39
39
  for (const section of ['Join', 'Children', 'Nested', 'RemovedWithJoin']) {
@@ -61,11 +61,17 @@ export class ProjectionCapabilities {
61
61
  catch {
62
62
  reject('ReadModel.Schema', 'read-model schema is not valid JSON');
63
63
  }
64
- const identifier = schema.properties?.id ?? schema.properties?.Id ?? { type: 'string' };
65
- if (!((identifier.type === 'string' && (!identifier.format || identifier.format === 'guid')) ||
66
- (identifier.type === 'number' && (!identifier.format || identifier.format === 'double')) ||
67
- (identifier.type === 'integer' && ['int32', 'uint32'].includes(identifier.format ?? '')))) {
68
- reject('ReadModel.Schema.id', 'identifier schema must be string, number, or GUID (int32/uint32 identifiers are supported)');
64
+ this.checkProtection(schema, 'ReadModel.Schema', reject);
65
+ const identifier = schema.properties?.id;
66
+ if (!identifier || !((identifier.type === 'string' && (!identifier.format || identifier.format === 'guid')) ||
67
+ (identifier.type === 'number' && identifier.format === 'double'))) {
68
+ reject('ReadModel.Schema.id', 'only a lowercase id with a fixture-backed string, GUID, or number/double schema is supported; other identifiers require a kernel-backed test (ChronicleKernelScenario / live kernel)');
69
+ }
70
+ const names = Object.keys(schema.properties ?? {});
71
+ for (const [index, name] of names.entries()) {
72
+ if (names.slice(index + 1).some(other => other.toLowerCase() === name.toLowerCase())) {
73
+ reject(`ReadModel.Schema.${name}`, 'case-insensitively colliding read-model properties require a kernel-backed test (ChronicleKernelScenario / live kernel)');
74
+ }
69
75
  }
70
76
  let initial;
71
77
  try {
@@ -80,14 +86,7 @@ export class ProjectionCapabilities {
80
86
  const target = schema.properties?.[property]
81
87
  ?? reject(`InitialModelState.${property}`, 'initial value has no read-model schema');
82
88
  this.checkSchema(target, `InitialModelState.${property}`, reject);
83
- if (typeof value === 'number') {
84
- const outsideIntegerRange = !Number.isSafeInteger(value) ||
85
- (target.format === 'int32' && (value < -2147483648 || value > 2147483647)) ||
86
- (target.format === 'uint32' && (value < 0 || value > 4294967295));
87
- if (!Number.isFinite(value) || (target.type === 'integer' && outsideIntegerRange)) {
88
- reject(`InitialModelState.${property}`, 'initial numeric value is outside the supported finite/integer range');
89
- }
90
- }
89
+ this.checkInitialValue(value, target, `InitialModelState.${property}`, reject);
91
90
  }
92
91
  const generations = new Map();
93
92
  const requireEventSchema = (eventType, path) => {
@@ -100,6 +99,11 @@ export class ProjectionCapabilities {
100
99
  };
101
100
  const from = wire.From ?? [];
102
101
  const removedWith = wire.RemovedWith ?? [];
102
+ for (const entry of from) {
103
+ if (removedWith.some(removal => removal.Key.Id === entry.Key.Id)) {
104
+ reject(eventContractPath('From', entry.Key), 'events subscribed through both From and RemovedWith require a kernel-backed test (ChronicleKernelScenario / live kernel)');
105
+ }
106
+ }
103
107
  for (const entry of [...from, ...removedWith]) {
104
108
  const section = from.includes(entry) ? 'From' : 'RemovedWith';
105
109
  const path = eventContractPath(section, entry.Key);
@@ -115,16 +119,15 @@ export class ProjectionCapabilities {
115
119
  const mappingPath = `${path}.Properties.${property}`;
116
120
  this.checkMapping(schema, eventSchema, property, expression, mappingPath, reject);
117
121
  }
118
- const expressions = Object.values(properties);
119
- const aggregateOnly = expressions.length > 0 && expressions.every(expression => /^(?:\$add\([^()]+\)|\$subtract\([^()]+\)|\$count|\$increment|\$decrement)$/.test(expression));
120
- if (wire.AutoMap === AutoMap.Enabled && !aggregateOnly) {
122
+ if (wire.AutoMap !== AutoMap.Disabled) {
121
123
  this.checkAutoMap(schema, eventSchema, properties, wire.NoAutoMapProperties ?? [], path, reject);
122
124
  }
123
125
  }
124
126
  }
125
127
  static checkAutoMap(modelSchema, eventSchema, explicit, exclusions, path, reject) {
126
128
  for (const [destination] of Object.entries(modelSchema.properties ?? {})) {
127
- if (destination in explicit || exclusions.includes(destination))
129
+ if (Object.keys(explicit).some(name => name.toLowerCase() === destination.toLowerCase()) ||
130
+ exclusions.some(name => name.toLowerCase() === destination.toLowerCase()))
128
131
  continue;
129
132
  const candidates = Object.keys(eventSchema.properties ?? {}).filter(source => source.toLowerCase() === destination.toLowerCase());
130
133
  if (candidates.length > 1)
@@ -139,31 +142,41 @@ export class ProjectionCapabilities {
139
142
  ?? fail('dynamic or unknown destination paths require a kernel-backed test');
140
143
  if (destination.includes('.') || destination.includes('$'))
141
144
  fail('dynamic or unknown destination paths require a kernel-backed test');
145
+ if (destination.toLowerCase() === 'id' || Object.keys(modelSchema.properties ?? {}).some(name => name !== destination && name.toLowerCase() === destination.toLowerCase())) {
146
+ fail('identifier or case-insensitively colliding target mappings require a kernel-backed test (ChronicleKernelScenario / live kernel)');
147
+ }
148
+ if (target.type === 'object' || target.type === 'array')
149
+ fail('object/array target mappings require a kernel-backed test (ChronicleKernelScenario / live kernel)');
142
150
  this.checkSchema(target, path, (_path, reason) => fail(reason));
143
- if (expression === '$eventSourceId' || expression === '$null')
151
+ if (expression === '$eventSourceId') {
152
+ if (target.type !== 'string' || (target.format && target.format !== 'guid')) {
153
+ fail('$eventSourceId requires a string or GUID target; other targets require a kernel-backed test (ChronicleKernelScenario / live kernel)');
154
+ }
155
+ return;
156
+ }
157
+ if (expression === '$null')
144
158
  return;
145
159
  if (expression.startsWith('$eventContext(')) {
160
+ if (expression === '$eventContext(Occurred)')
161
+ fail('raw Occurred is converted inconsistently by the kernel; only its Year, Month and Day paths are supported');
146
162
  if (/^\$eventContext\(.+\(\)\)$/.test(expression))
147
163
  fail('derived event-context functions require a kernel-backed test');
148
164
  const path = /^\$eventContext\(([^()]*)\)$/.exec(expression)?.[1];
149
- if (path) {
150
- try {
151
- if (eventContextPropertyExpression(path) === expression)
152
- return;
153
- }
154
- catch (error) {
155
- if (!(error instanceof InvalidEventContextPropertyError))
156
- throw error;
157
- }
158
- }
159
- fail('event-context path is not supported by the client');
165
+ const strings = ['EventSourceId', 'EventStore', 'Namespace', 'EventSourceType', 'EventStreamType',
166
+ 'EventStreamId', 'Subject', 'Subject.Value', 'Hash', 'CorrelationId', 'CorrelationId.Value',
167
+ 'CausedBy.Subject', 'CausedBy.Name', 'CausedBy.UserName', 'EventType.Id.Value', 'SequenceNumber', 'SequenceNumber.Value'];
168
+ const integers = ['Occurred.Year', 'Occurred.Month', 'Occurred.Day', 'EventType.Generation.Value'];
169
+ if (path && ((strings.includes(path) && target.type === 'string' && !target.format) ||
170
+ (integers.includes(path) && target.type === 'integer' && target.format === 'int32')))
171
+ return;
172
+ fail('event-context path and target schema require a kernel-backed test');
160
173
  }
161
174
  if (/^\$value\([\p{L}\p{Mn}\p{Nd}\p{Pc} ._/:*+-]*\)$/u.test(expression)) {
162
175
  const text = expression.slice(7, -1);
163
176
  if (target.format === 'date-time')
164
177
  fail('$value date-time literals require a kernel-backed test');
165
178
  if (this.numeric(target)) {
166
- if (!/^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/.test(text) || !Number.isFinite(Number(text)) ||
179
+ if (!(target.type === 'integer' ? /^[+-]?\d+$/.test(text) : /^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/.test(text)) || !Number.isFinite(Number(text)) ||
167
180
  (target.type === 'integer' && (!Number.isSafeInteger(Number(text)) ||
168
181
  (target.format === 'int32' && (Number(text) < -2147483648 || Number(text) > 2147483647)) ||
169
182
  (target.format === 'uint32' && (Number(text) < 0 || Number(text) > 4294967295))))) {
@@ -183,21 +196,21 @@ export class ProjectionCapabilities {
183
196
  }
184
197
  const operation = /^(\$add|\$subtract)\(([^()]+)\)$/.exec(expression);
185
198
  const arithmetic = operation || ['$count', '$increment', '$decrement'].includes(expression);
186
- if (arithmetic) {
187
- if (!this.numeric(target))
188
- fail('arithmetic requires a supported finite number/double or int32/uint32 schema (not float, decimal, duration, or int64)');
189
- if (operation) {
190
- const operand = this.propertyAt(eventSchema, operation[2]);
191
- if (!operand || !this.numeric(operand))
192
- fail('arithmetic operand requires a supported numeric event schema');
193
- }
194
- return;
195
- }
199
+ if (arithmetic)
200
+ fail('arithmetic requires a kernel-backed test (ChronicleKernelScenario / live kernel)');
196
201
  if (/^[A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)*$/.test(expression)) {
202
+ if (['true', 'True', 'false', 'False'].includes(expression)) {
203
+ fail('kernel resolves this expression as a boolean literal before event content');
204
+ }
205
+ if (expression.includes('.'))
206
+ fail('nested event property paths require a kernel-backed test');
197
207
  const source = this.propertyAt(eventSchema, expression);
198
208
  if (!source)
199
209
  return fail(`event property '${expression}' is absent from the participating event schema`);
200
210
  this.checkSchema(source, path, (_path, reason) => fail(reason));
211
+ if (!this.compatible(source, target)) {
212
+ fail(`source '${expression}' (${source.type}${source.format ? `/${source.format}` : ''}) to target '${destination}' (${target.type}${target.format ? `/${target.format}` : ''}) requires a kernel-backed test`);
213
+ }
201
214
  return;
202
215
  }
203
216
  if (expression.startsWith('$context.')) {
@@ -205,6 +218,52 @@ export class ProjectionCapabilities {
205
218
  }
206
219
  fail('requires a kernel-backed test');
207
220
  }
221
+ static compatible(source, target) {
222
+ if (JSON.stringify(source.type) === JSON.stringify(target.type) && source.format === target.format) {
223
+ return source.type !== 'object' && source.type !== 'array';
224
+ }
225
+ if (source.type === 'string' && !source.format && Array.isArray(target.type) &&
226
+ target.type[0] === 'string' && target.type[1] === 'null')
227
+ return true;
228
+ if (source.type === 'string' && !source.format &&
229
+ ((target.type === 'number' && (!target.format || target.format === 'double')) ||
230
+ (target.type === 'integer' && ['int32', 'uint32'].includes(target.format ?? ''))))
231
+ return true;
232
+ if (source.type === 'string' && source.format === 'guid' && target.type === 'string' && !target.format)
233
+ return true;
234
+ return source.type === 'integer' && ['int32', 'uint32'].includes(source.format ?? '') &&
235
+ target.type === 'number' && (!target.format || target.format === 'double');
236
+ }
237
+ static checkInitialValue(value, target, path, reject) {
238
+ if (value === null && Array.isArray(target.type) && target.type.includes('null'))
239
+ return;
240
+ if (target.type === 'object' || target.type === 'array' || target.format === 'date-time') {
241
+ reject(path, 'initial object, array, or date-time values require a kernel-backed test');
242
+ }
243
+ if (target.type === 'integer' || target.type === 'number') {
244
+ if (typeof value !== 'number')
245
+ reject(path, 'initial numeric value must be a JSON number');
246
+ const outsideIntegerRange = !Number.isSafeInteger(value) ||
247
+ (target.format === 'int32' && (value < -2147483648 || value > 2147483647)) ||
248
+ (target.format === 'uint32' && (value < 0 || value > 4294967295));
249
+ if (!Number.isFinite(value) || (target.type === 'integer' && outsideIntegerRange)) {
250
+ reject(path, 'initial numeric value is outside the supported finite/integer range');
251
+ }
252
+ }
253
+ else if (target.type === 'boolean') {
254
+ if (typeof value !== 'boolean')
255
+ reject(path, 'initial boolean value must be a JSON boolean');
256
+ }
257
+ else if (target.type === 'string' || Array.isArray(target.type)) {
258
+ if (typeof value !== 'string')
259
+ reject(path, 'initial string value must be a JSON string');
260
+ if (target.format === 'guid' && !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(value)) {
261
+ reject(path, 'initial GUID value must be canonical lowercase text');
262
+ }
263
+ }
264
+ else
265
+ reject(path, 'initial value requires a kernel-backed test');
266
+ }
208
267
  static propertyAt(schema, path) {
209
268
  return path.split('.').reduce((current, segment) => current?.properties?.[segment], schema);
210
269
  }
@@ -212,8 +271,23 @@ export class ProjectionCapabilities {
212
271
  return (schema.type === 'number' && (!schema.format || schema.format === 'double')) ||
213
272
  (schema.type === 'integer' && ['int32', 'uint32'].includes(schema.format ?? ''));
214
273
  }
274
+ static checkProtection(schema, path, reject) {
275
+ if (schema.compliance?.length || schema.security?.length)
276
+ reject(path, 'protected fields require a kernel-backed test');
277
+ for (const [name, property] of Object.entries(schema.properties ?? {}))
278
+ this.checkProtection(property, `${path}.${name}`, reject);
279
+ if (schema.items)
280
+ this.checkProtection(schema.items, `${path}.items`, reject);
281
+ if (schema.additionalProperties && typeof schema.additionalProperties === 'object') {
282
+ this.checkProtection(schema.additionalProperties, `${path}.additionalProperties`, reject);
283
+ }
284
+ }
215
285
  static checkSchema(schema, path, reject) {
216
- if (!schema.type || schema.type === 'null' || (schema.format && !['guid', 'date-time', 'double', 'int32', 'uint32'].includes(schema.format)) ||
286
+ if (schema.compliance?.length || schema.security?.length)
287
+ reject(path, 'protected fields require a kernel-backed test');
288
+ if (!schema.type || schema.type === 'null' || (Array.isArray(schema.type) &&
289
+ (schema.type.length !== 2 || schema.type[0] !== 'string' || schema.type[1] !== 'null')) ||
290
+ (schema.format && !['guid', 'date-time', 'double', 'int32', 'uint32'].includes(schema.format)) ||
217
291
  (schema.format === 'date-time' && schema.type !== 'string') ||
218
292
  (schema.format === 'guid' && schema.type !== 'string') ||
219
293
  (schema.format === 'double' && schema.type !== 'number') ||