@cratis/chronicle 6.25.0 → 6.27.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.
@@ -1 +1 @@
1
- {"version":3,"file":"EventScenario.d.ts","sourceRoot":"","sources":["../../testing/EventScenario.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACxF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAQhE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAUzE,sFAAsF;AACtF,qBAAa,aAAa;IACtB,QAAQ,CAAC,aAAa,EAAE,cAAc,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,yBAAyB,CAAC;IAC1C,QAAQ,CAAC,IAAI,EAAE,wBAAwB,CAAC;IAExC,YAAY,OAAO,GAAE,oBAAyB,EAmF7C;IAED,qDAAqD;IACrD,IAAI,QAAQ,IAAI,SAAS,CAKxB;IACK,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAE1F;IACD,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAC/F,UAAU,CAAC,MAAM,EAAE,qBAAqB,EAAE,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAO9F,IAAI,OAAO,IAAI,SAAS,YAAY,EAAE,CAAmE;IACzG,IAAI,cAAc,IAAI,SAAS,aAAa,EAAE,CAA0E;IAExH,+DAA+D;IAC/D,IAAI,IAAI,IAAI;QAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;QAAC,QAAQ,CAAC,cAAc,EAAE,SAAS,aAAa,EAAE,CAAA;KAAE,CAE3G;CACJ"}
1
+ {"version":3,"file":"EventScenario.d.ts","sourceRoot":"","sources":["../../testing/EventScenario.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACxF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAQhE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAUzE,sFAAsF;AACtF,qBAAa,aAAa;IACtB,QAAQ,CAAC,aAAa,EAAE,cAAc,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,yBAAyB,CAAC;IAC1C,QAAQ,CAAC,IAAI,EAAE,wBAAwB,CAAC;IAExC,YAAY,OAAO,GAAE,oBAAyB,EA8F7C;IAED,qDAAqD;IACrD,IAAI,QAAQ,IAAI,SAAS,CAKxB;IACK,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAE1F;IACD,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAC/F,UAAU,CAAC,MAAM,EAAE,qBAAqB,EAAE,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAO9F,IAAI,OAAO,IAAI,SAAS,YAAY,EAAE,CAAmE;IACzG,IAAI,cAAc,IAAI,SAAS,aAAa,EAAE,CAA0E;IAExH,+DAA+D;IAC/D,IAAI,IAAI,IAAI;QAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;QAAC,QAAQ,CAAC,cAAc,EAAE,SAAS,aAAa,EAAE,CAAA;KAAE,CAE3G;CACJ"}
@@ -83,9 +83,18 @@ export class EventScenario {
83
83
  }
84
84
  for (const entry of capture.uniqueConstraint?.eventDefinitions ?? []) {
85
85
  const type = eventTypes.find(type => getEventTypeFor(type).id.value === entry.eventTypeId);
86
- if (!['string', 'boolean'].includes(getEventTypeMetadata(type)?.schema.properties?.[entry.properties[0]]?.type ?? '')) {
86
+ const schemaTypes = entry.properties.map(property => getEventTypeMetadata(type)?.schema.properties?.[property]?.type ?? '');
87
+ if (entry.properties.length === 1 && !['string', 'boolean'].includes(schemaTypes[0])) {
87
88
  throw new UnsupportedEventSequenceOperation('artifacts.constraints', name, 'The constrained property must be a schema-backed string or boolean.');
88
89
  }
90
+ // constraints-ignore-casing.json captures folded string keys only.
91
+ if (capture.uniqueConstraint.ignoreCasing && schemaTypes.some(schemaType => schemaType !== 'string')) {
92
+ throw new UnsupportedEventSequenceOperation('artifacts.constraints', name, 'Case-insensitive keys must be schema-backed strings.');
93
+ }
94
+ // constraints-composite.json captures string components only.
95
+ if (entry.properties.length > 1 && schemaTypes.some(schemaType => schemaType !== 'string')) {
96
+ throw new UnsupportedEventSequenceOperation('artifacts.constraints', name, 'Every property of a composite key must be a schema-backed string.');
97
+ }
89
98
  }
90
99
  }
91
100
  }
@@ -1 +1 @@
1
- {"version":3,"file":"EventScenario.js","sourceRoot":"","sources":["../../testing/EventScenario.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"EventScenario.js","sourceRoot":"","sources":["../../testing/EventScenario.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,7 @@ export declare class InProcessConstraints {
8
8
  private readonly _definitions;
9
9
  private readonly _constrainedProperties;
10
10
  private readonly _removalTypes;
11
+ private readonly _propertyRemovalTypes;
11
12
  constructor(_definitions: ReadonlyMap<string, ConstraintCapture>);
12
13
  isRemovalOnlyType(eventTypeId: string): boolean;
13
14
  hasRemovalType(eventTypeId: string): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"InProcessConstraints.d.ts","sourceRoot":"","sources":["../../testing/InProcessConstraints.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,IAAI,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;AACvG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAEpF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAGpF,MAAM,MAAM,uBAAuB,GAAG,4BAA4B,CAAC;AAiBnE,6FAA6F;AAC7F,qBAAa,oBAAoB;IAIjB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAHzC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAkC;IACzE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IAEnD,YAA6B,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAuDhF;IAED,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE9C;IAED,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAgD;IAE5F,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEpE;IAED,yGAAyG;IACzG,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,EAAE,QAAQ,EAAE,SAAS,aAAa,EAAE,GAAG,uBAAuB,EAAE,CAiEzG;IAED,cAAc,CAAC,SAAS,EAAE,mBAAmB,GAAG,mBAAmB,CAElE;IAED,OAAO,CAAC,WAAW;CAGtB"}
1
+ {"version":3,"file":"InProcessConstraints.d.ts","sourceRoot":"","sources":["../../testing/InProcessConstraints.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,IAAI,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;AACvG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAEpF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAGpF,MAAM,MAAM,uBAAuB,GAAG,4BAA4B,CAAC;AAwCnE,6FAA6F;AAC7F,qBAAa,oBAAoB;IAKjB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAJzC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAkC;IACzE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAqB;IAE3D,YAA6B,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAyEhF;IAED,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE9C;IAED,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAgD;IAE5F,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEpE;IAED,yGAAyG;IACzG,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,EAAE,QAAQ,EAAE,SAAS,aAAa,EAAE,GAAG,uBAAuB,EAAE,CA+FzG;IAED,cAAc,CAAC,SAAS,EAAE,mBAAmB,GAAG,mBAAmB,CAElE;IAED,OAAO,CAAC,WAAW;CAGtB"}
@@ -16,11 +16,32 @@ function kernelKeyString(value, name) {
16
16
  function keyHash(value) {
17
17
  return createHash('sha256').update(value).digest('hex');
18
18
  }
19
+ /**
20
+ * The kernel key: each declared property's string in declared order, joined with a literal '-'
21
+ * (UniqueConstraintDefinitionExtensions.GetPropertiesAndValues/GetValue at Chronicle 8fe5d30). Not a tuple:
22
+ * ['a-b', 'c'] and ['a', 'b-c'] claim the same key, as constraints-composite.json captures.
23
+ */
24
+ function claimOf(name, unique, eventTypeId, content) {
25
+ const properties = unique.eventDefinitions.find(entry => entry.eventTypeId === eventTypeId)?.properties;
26
+ if (properties === undefined)
27
+ return undefined;
28
+ const parts = properties.map(property => ({ property, raw: kernelKeyString(content[property], name) }));
29
+ const joined = parts.map(part => part.raw).join('-');
30
+ if (!unique.ignoreCasing)
31
+ return { key: keyHash(joined), parts };
32
+ // The kernel applies .NET ToLowerInvariant to the joined value. constraints-ignore-casing.json proves
33
+ // only the ASCII key domain, where that is exactly A-Z to a-z; host Unicode case tables are not used.
34
+ if (!/^[\x20-\x7e]*$/.test(joined)) {
35
+ throw new UnsupportedEventSequenceOperation('artifacts.constraints', name, 'Case-insensitive keys outside the ASCII key domain are not fixture-backed.');
36
+ }
37
+ return { key: keyHash(joined.replace(/[A-Z]/g, letter => String.fromCharCode(letter.charCodeAt(0) + 32))), parts };
38
+ }
19
39
  /** Narrow, fixture-backed unscoped constraint validation over serialized event snapshots. */
20
40
  export class InProcessConstraints {
21
41
  _definitions;
22
42
  _constrainedProperties = new Map();
23
43
  _removalTypes = new Set();
44
+ _propertyRemovalTypes = new Set();
24
45
  constructor(_definitions) {
25
46
  this._definitions = _definitions;
26
47
  const coveredTypes = new Map();
@@ -34,10 +55,12 @@ export class InProcessConstraints {
34
55
  }
35
56
  if (capture.uniqueConstraint) {
36
57
  const unique = capture.uniqueConstraint;
37
- if (unique.ignoreCasing || unique.eventDefinitions.length === 0 ||
38
- unique.eventDefinitions.some(entry => entry.properties.length !== 1 ||
39
- !/^[a-z][a-zA-Z0-9]*$/.test(entry.properties[0]))) {
40
- throw this.unsupported(name, 'Case folding and composite or nested keys are not fixture-backed.');
58
+ // constraints-composite.json installs one, two and three flat properties per event type.
59
+ // The kernel keys properties by path with ToDictionary, so duplicate paths are not a key shape.
60
+ if (unique.eventDefinitions.length === 0 || unique.eventDefinitions.some(entry => entry.properties.length < 1 || entry.properties.length > 3 ||
61
+ new Set(entry.properties).size !== entry.properties.length ||
62
+ entry.properties.some(property => !/^[a-z][a-zA-Z0-9]*$/.test(property)))) {
63
+ throw this.unsupported(name, 'Only one to three distinct flat properties per event type are fixture-backed; nested, indexed or repeated key paths are not.');
41
64
  }
42
65
  for (const id of [unique.removedWithEventTypeId, ...(unique.removedWithEventTypeIds ?? [])]) {
43
66
  if (id === undefined)
@@ -48,18 +71,33 @@ export class InProcessConstraints {
48
71
  }
49
72
  removalOwners.set(id, name);
50
73
  this._removalTypes.add(id);
74
+ this._propertyRemovalTypes.add(id);
51
75
  }
52
76
  for (const entry of unique.eventDefinitions) {
53
77
  const properties = this._constrainedProperties.get(entry.eventTypeId) ?? new Set();
54
- properties.add(entry.properties[0]);
78
+ entry.properties.forEach(property => properties.add(property));
55
79
  this._constrainedProperties.set(entry.eventTypeId, properties);
56
80
  }
57
81
  }
58
82
  else if (capture.uniqueEventType) {
59
83
  const unique = capture.uniqueEventType;
60
- if (unique.removedWithEventTypeIds?.length || (unique.eventTypeIds?.length ?? 1) !== 1) {
61
- throw this.unsupported(name, 'Removal and multi-type unique event cycles are not fixture-backed.');
84
+ const ids = unique.eventTypeIds ?? [unique.eventTypeId];
85
+ const removals = unique.removedWithEventTypeIds ?? [];
86
+ const shared = ids.filter(id => removals.includes(id)).length;
87
+ // Exactly the shapes installed by the pinned fixtures: one covered type without removal
88
+ // (constraints.json), two covered types with two separate removers
89
+ // (constraints-event-type-siblings.json), and three covered types with three removers of
90
+ // which one is also covered (constraints-event-type-cycles.json).
91
+ if (!(ids.length === 1 && removals.length === 0) &&
92
+ !(ids.length === 2 && removals.length === 2 && shared === 0) &&
93
+ !(ids.length === 3 && removals.length === 3 && shared === 1)) {
94
+ throw this.unsupported(name, 'This unique event type set and removal combination is not fixture-backed.');
95
+ }
96
+ // Each cycle fixture installs its definition alone; interaction with other definitions is unproven.
97
+ if (removals.length && _definitions.size !== 1) {
98
+ throw this.unsupported(name, 'Unique event cycles alongside other definitions are not fixture-backed.');
62
99
  }
100
+ removals.forEach(id => this._removalTypes.add(id));
63
101
  }
64
102
  else {
65
103
  throw this.unsupported(name, 'Unknown constraint definition.');
@@ -84,7 +122,7 @@ export class InProcessConstraints {
84
122
  }
85
123
  }
86
124
  isRemovalOnlyType(eventTypeId) {
87
- return this._removalTypes.has(eventTypeId) && !this._constrainedProperties.has(eventTypeId);
125
+ return this._propertyRemovalTypes.has(eventTypeId) && !this._constrainedProperties.has(eventTypeId);
88
126
  }
89
127
  hasRemovalType(eventTypeId) { return this._removalTypes.has(eventTypeId); }
90
128
  isConstrainedProperty(eventTypeId, property) {
@@ -109,36 +147,39 @@ export class InProcessConstraints {
109
147
  claims.delete(source);
110
148
  continue;
111
149
  }
112
- const property = unique.eventDefinitions.find(entry => entry.eventTypeId === type)?.properties[0];
113
- if (property === undefined)
150
+ const properties = unique.eventDefinitions.find(entry => entry.eventTypeId === type)?.properties;
151
+ if (properties === undefined)
114
152
  continue;
115
- const value = prior.content[property];
116
- if (typeof value !== 'string' && typeof value !== 'boolean')
153
+ if (properties.some(property => !['string', 'boolean'].includes(typeof prior.content[property]))) {
117
154
  throw this.unsupported(name, 'History has an unproven key.');
118
- claims.set(source, { key: keyHash(kernelKeyString(value, name)),
155
+ }
156
+ claims.set(source, { key: claimOf(name, unique, type, prior.content).key,
119
157
  sequence: prior.context.sequenceNumber.toString() });
120
158
  }
121
159
  owners.set(name, claims);
122
160
  }
123
161
  const stagedKeys = new Map();
124
- const stagedTypes = new Set();
162
+ const stagedCycles = new Map();
125
163
  for (const event of incoming) {
126
164
  const type = event.eventType.id.value;
127
165
  const source = event.context.eventSourceId;
166
+ const violationCount = violations.length;
128
167
  for (const [name, definition] of this._definitions) {
129
- const property = definition.uniqueConstraint?.eventDefinitions.find(entry => entry.eventTypeId === type)?.properties[0];
130
- if (property !== undefined) {
131
- const raw = kernelKeyString(event.content[property], name);
132
- const key = keyHash(raw);
168
+ const claim = definition.uniqueConstraint && claimOf(name, definition.uniqueConstraint, type, event.content);
169
+ if (claim) {
170
+ const key = claim.key;
133
171
  const existing = [...owners.get(name).entries()].find(([, claim]) => claim.key === key);
134
172
  const batchClaims = stagedKeys.get(name) ?? new Map();
135
173
  const batchOwner = batchClaims.get(key);
136
174
  if (existing && existing[0] !== source || batchOwner !== undefined && batchOwner !== source) {
137
- const details = { PropertyName: property, PropertyValue: raw };
138
- violations.push({ EventTypeId: type, SequenceNumber: BigInt(existing?.[1].sequence ?? unavailable), ConstraintType: 1,
139
- ConstraintName: name,
140
- Message: `Event '${type}' on member '${property}' violated a unique constraint on sequence number ${existing?.[1].sequence ?? unavailable}`,
141
- Details: details });
175
+ // One violation per declared property, in declared order, each with its own original value.
176
+ const sequence = existing?.[1].sequence ?? unavailable;
177
+ for (const { property, raw } of claim.parts) {
178
+ violations.push({ EventTypeId: type, SequenceNumber: BigInt(sequence), ConstraintType: 1,
179
+ ConstraintName: name,
180
+ Message: `Event '${type}' on member '${property}' violated a unique constraint on sequence number ${sequence}`,
181
+ Details: { PropertyName: property, PropertyValue: raw } });
182
+ }
142
183
  }
143
184
  else {
144
185
  batchClaims.set(key, source);
@@ -147,17 +188,45 @@ export class InProcessConstraints {
147
188
  }
148
189
  const uniqueType = definition.uniqueEventType;
149
190
  if (uniqueType && (uniqueType.eventTypeIds ?? [uniqueType.eventTypeId]).includes(type)) {
150
- const first = history.find(prior => prior.context.eventSourceId === source &&
151
- (uniqueType.eventTypeIds ?? [uniqueType.eventTypeId]).includes(prior.eventType.id.value));
152
- const staged = stagedTypes.has(`${name}\u0000${source}`);
153
- if (first || staged) {
154
- violations.push({ EventTypeId: type, SequenceNumber: first?.context.sequenceNumber ?? BigInt(unavailable), ConstraintType: 2,
191
+ const covered = uniqueType.eventTypeIds ?? [uniqueType.eventTypeId];
192
+ const removals = uniqueType.removedWithEventTypeIds ?? [];
193
+ // Durable answer: the earliest covered event after the latest removal for this source.
194
+ let first;
195
+ for (const prior of history) {
196
+ if (prior.context.eventSourceId !== source)
197
+ continue;
198
+ const priorType = prior.eventType.id.value;
199
+ if (removals.includes(priorType))
200
+ first = undefined;
201
+ else if (!first && covered.includes(priorType))
202
+ first = prior;
203
+ }
204
+ const cycles = stagedCycles.get(name) ?? new Map();
205
+ const state = cycles.get(source);
206
+ // A cycle state recorded earlier in this append overrides durable history.
207
+ if (state === 'open' || state === undefined && first) {
208
+ // Durable storage supplies the sequence even when a staged claim causes rejection.
209
+ const sequence = first?.context.sequenceNumber.toString() ?? unavailable;
210
+ violations.push({ EventTypeId: type, SequenceNumber: BigInt(sequence), ConstraintType: 2,
155
211
  ConstraintName: name,
156
- Message: `Event '${type}' violated a unique event type constraint on sequence number ${first?.context.sequenceNumber.toString() ?? unavailable}`,
212
+ Message: `Event '${type}' violated a unique event type constraint on sequence number ${sequence}`,
157
213
  Details: {} });
158
214
  }
159
- else
160
- stagedTypes.add(`${name}\u0000${source}`);
215
+ else {
216
+ cycles.set(source, 'open');
217
+ stagedCycles.set(name, cycles);
218
+ }
219
+ }
220
+ }
221
+ // Kernel batch observers release a cycle only after the entire event validates.
222
+ // A covered-and-removal event first attempts its claim, then releases it.
223
+ if (violations.length === violationCount) {
224
+ for (const [name, definition] of this._definitions) {
225
+ if (definition.uniqueEventType?.removedWithEventTypeIds?.includes(type)) {
226
+ const cycles = stagedCycles.get(name) ?? new Map();
227
+ cycles.set(source, 'released');
228
+ stagedCycles.set(name, cycles);
229
+ }
161
230
  }
162
231
  }
163
232
  }
@@ -1 +1 @@
1
- {"version":3,"file":"InProcessConstraints.js","sourceRoot":"","sources":["../../testing/InProcessConstraints.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"InProcessConstraints.js","sourceRoot":"","sources":["../../testing/InProcessConstraints.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cratis/chronicle",
3
- "version": "6.25.0",
3
+ "version": "6.27.0",
4
4
  "description": "Event sourcing for TypeScript and Node.js — the idiomatic client for Cratis Chronicle, the open-source event-sourcing database",
5
5
  "author": "Cratis",
6
6
  "license": "MIT",
@@ -0,0 +1,343 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import 'reflect-metadata';
5
+ import { readFileSync } from 'node:fs';
6
+ import { chai, describe, it, vi } from 'vitest';
7
+ import { field } from '@cratis/fundamentals';
8
+ import { eventType, getEventTypeMetadata } from '../events/eventTypeDecorator.js';
9
+ import { constraint } from '../events/constraints/constraint.js';
10
+ import { compileConstraints } from '../events/constraints/Constraints.js';
11
+ import type { IConstraint } from '../events/constraints/IConstraint.js';
12
+ import type { IConstraintBuilder } from '../events/constraints/IConstraintBuilder.js';
13
+ import { EventScenario, UnsupportedEventSequenceOperation } from './index.js';
14
+ import { InProcessConstraints } from './InProcessConstraints.js';
15
+ import { ReactorScenario } from './ReactorScenario.js';
16
+ import { reactor } from '../reactors/reactor.js';
17
+
18
+ chai.should();
19
+ class OracleDomainText { @field(String) key: string; constructor(key: string) { this.key = key; } }
20
+ eventType('OracleDomainText')(OracleDomainText);
21
+ class OracleDomainRemoved { @field(String) label: string; constructor(label: string) { this.label = label; } }
22
+ eventType('OracleDomainRemoved')(OracleDomainRemoved);
23
+ class OracleCompositeName {
24
+ @field(String) first: string; @field(String) last: string;
25
+ constructor(first: string, last: string) { this.first = first; this.last = last; }
26
+ }
27
+ eventType('OracleCompositeName')(OracleCompositeName);
28
+ class OracleCompositeAlias {
29
+ @field(String) given: string; @field(String) family: string;
30
+ constructor(given: string, family: string) { this.given = given; this.family = family; }
31
+ }
32
+ eventType('OracleCompositeAlias')(OracleCompositeAlias);
33
+ class OracleCompositeTriple {
34
+ @field(String) first: string; @field(String) middle: string; @field(String) last: string;
35
+ constructor(first: string, middle: string, last: string) { this.first = first; this.middle = middle; this.last = last; }
36
+ }
37
+ eventType('OracleCompositeTriple')(OracleCompositeTriple);
38
+
39
+ // Declared order differs from field order on the alias type: family, then given.
40
+ class FullName implements IConstraint {
41
+ define(builder: IConstraintBuilder) {
42
+ builder.unique(key => key.on(OracleCompositeName, event => event.first, event => event.last)
43
+ .on(OracleCompositeAlias, event => event.family, event => event.given)
44
+ .on(OracleDomainText, event => event.key)
45
+ .removedWith(OracleDomainRemoved).withMessage('Taken: {PropertyName}={PropertyValue}'));
46
+ }
47
+ }
48
+ constraint('OracleFullName')(FullName);
49
+ class Triple implements IConstraint {
50
+ define(builder: IConstraintBuilder) {
51
+ builder.unique(key => key.on(OracleCompositeTriple, event => event.first, event => event.middle, event => event.last));
52
+ }
53
+ }
54
+ constraint('OracleTriple')(Triple);
55
+ class Handle implements IConstraint {
56
+ define(builder: IConstraintBuilder) {
57
+ builder.unique(key => key.on(OracleDomainText, event => event.key).ignoreCasing()
58
+ .removedWith(OracleDomainRemoved).withMessage('Handle taken: {PropertyValue}'));
59
+ }
60
+ }
61
+ constraint('OracleHandle')(Handle);
62
+ class CasedName implements IConstraint {
63
+ define(builder: IConstraintBuilder) {
64
+ builder.unique(key => key.on(OracleCompositeName, event => event.first, event => event.last).ignoreCasing());
65
+ }
66
+ }
67
+ constraint('OracleCasedName')(CasedName);
68
+
69
+ const aliases = { string: OracleDomainText, removed: OracleDomainRemoved, name: OracleCompositeName,
70
+ alias: OracleCompositeAlias, triple: OracleCompositeTriple };
71
+ type Alias = keyof typeof aliases;
72
+ type Input = { source: string; type: Alias; value?: string; values?: Record<string, string> };
73
+ type History = { sequence: string; source: string; sourceType: string; streamType: string; streamId: string;
74
+ type: string; content: Record<string, string>; hash: string };
75
+ type Violation = { EventTypeId: string; SequenceNumber: string; ConstraintType: number; ConstraintName: string;
76
+ Message: string; Details: Record<string, string> };
77
+ type Outcome = { success: boolean; sequences: string[]; violations: Array<{ id: string; message: string; details: Record<string, string> }>;
78
+ wireViolations: Violation[]; errors: string[]; history: History[]; next: string };
79
+ type Fixture = { eventSchemas: Record<string, { eventTypeId: string; properties: Record<string, string> }>;
80
+ constraintDefinitions: Array<{ kind: string; name: string; events: Array<{ type: string; properties: string[] }>;
81
+ ignoreCasing: boolean; removedWithEventTypeIds: string[]; message: string }>;
82
+ isolatedConstraintOperations: Array<{ mode: 'single' | 'batch'; events: Input[] }>;
83
+ expected: { outcomes: Outcome[] } };
84
+ const aliasById = Object.fromEntries(Object.entries(aliases).map(([alias, type]) => [getEventTypeMetadata(type)!.eventType.id.value, alias]));
85
+ const makeEvent = ({ type, value, values }: Input): object => {
86
+ switch (type) {
87
+ case 'string': return new OracleDomainText(value!);
88
+ case 'removed': return new OracleDomainRemoved(value!);
89
+ case 'name': return new OracleCompositeName(values!.first, values!.last);
90
+ case 'alias': return new OracleCompositeAlias(values!.given, values!.family);
91
+ case 'triple': return new OracleCompositeTriple(values!.first, values!.middle, values!.last);
92
+ }
93
+ };
94
+ const historyOf = (scenario: EventScenario): History[] => scenario.appendedEvents.map(entry => ({
95
+ sequence: entry.context.sequenceNumber.toString(), source: entry.context.eventSourceId,
96
+ sourceType: entry.context.eventSourceType, streamType: entry.context.eventStreamType, streamId: entry.context.eventStreamId,
97
+ type: entry.eventType.id.value, content: entry.content as Record<string, string>, hash: entry.context.hash
98
+ }));
99
+ const unsupported = async (action: () => unknown, operation: string, reason: string) => {
100
+ await Promise.resolve().then(action).then(() => { throw new Error('Expected rejection'); }, error => {
101
+ (error instanceof UnsupportedEventSequenceOperation).should.be.true;
102
+ (error as Error).message.should.include(`UnsupportedEventSequenceOperation: ${operation}:`);
103
+ (error as Error).message.should.include(reason);
104
+ (error as Error).message.should.include('Use a kernel-backed test.');
105
+ });
106
+ };
107
+ const compositeTypes = Object.values(aliases);
108
+ const shapeReason = 'Only one to three distinct flat properties per event type are fixture-backed; nested, indexed or repeated key paths are not.';
109
+
110
+ async function matchesFixture(name: string, selected: readonly Function[], constraints: readonly Function[]) {
111
+ const fixture = JSON.parse(readFileSync(new URL(`./fixtures/${name}.json`, import.meta.url), 'utf8')) as Fixture;
112
+ const ids = new Set(selected);
113
+ Object.fromEntries(Object.entries(aliases).filter(([, type]) => ids.has(type)).map(([alias, type]) => {
114
+ const metadata = getEventTypeMetadata(type)!;
115
+ return [alias, { eventTypeId: metadata.eventType.id.value,
116
+ properties: Object.fromEntries(Object.entries(metadata.schema.properties ?? {}).map(([key, property]) => [key, property.type])) }];
117
+ })).should.deep.equal(fixture.eventSchemas);
118
+ const compiled = compileConstraints({ eventTypes: [...selected], constraints: [...constraints] });
119
+ [...compiled].map(([key, capture]) => ({ kind: 'uniqueProperty', name: key,
120
+ events: capture.uniqueConstraint!.eventDefinitions.map(entry => ({ type: aliasById[entry.eventTypeId], properties: entry.properties })),
121
+ ignoreCasing: capture.uniqueConstraint!.ignoreCasing,
122
+ removedWithEventTypeIds: (capture.uniqueConstraint!.removedWithEventTypeIds ?? []).map(id => aliasById[id]),
123
+ message: capture.uniqueConstraint!.message ?? ''
124
+ })).should.deep.equal(fixture.constraintDefinitions);
125
+ fixture.isolatedConstraintOperations.length.should.be.greaterThan(0);
126
+ fixture.expected.outcomes.length.should.equal(fixture.isolatedConstraintOperations.length);
127
+ fixture.expected.outcomes.some(outcome => !outcome.success).should.be.true;
128
+ const scenario = new EventScenario({ artifacts: { eventTypes: [...selected], constraints: [...constraints] } });
129
+ const notifications = scenario.eventSequence.appendOperations[Symbol.asyncIterator]();
130
+ const validate = vi.spyOn(InProcessConstraints.prototype, 'validate');
131
+ let count = 0;
132
+ try {
133
+ for (const [index, operation] of fixture.isolatedConstraintOperations.entries()) {
134
+ const expected = fixture.expected.outcomes[index];
135
+ const before = historyOf(scenario);
136
+ const calls = validate.mock.calls.length;
137
+ const nextNotification = notifications.next();
138
+ const results = operation.mode === 'single'
139
+ ? [await scenario.append(operation.events[0].source, makeEvent(operation.events[0]))]
140
+ : await scenario.appendMany(operation.events.map(entry => ({ eventSourceId: entry.source, event: makeEvent(entry) })));
141
+ const notification = (await nextNotification).value!;
142
+ validate.mock.calls.length.should.equal(calls + 1);
143
+ const raw = validate.mock.results.at(-1)?.value as ReturnType<InProcessConstraints['validate']>;
144
+ raw.map(({ SequenceNumber, ...violation }) => ({ ...violation, SequenceNumber: SequenceNumber.toString() }))
145
+ .should.deep.equal(expected.wireViolations);
146
+ notification.length.should.equal(operation.events.length);
147
+ notification.every(item => item.result.isSuccess === expected.success).should.be.true;
148
+ count += operation.events.length;
149
+ scenario.results.length.should.equal(count);
150
+ results.length.should.equal(operation.events.length);
151
+ for (const [position, result] of results.entries()) {
152
+ result.isSuccess.should.equal(expected.success);
153
+ result.sequenceNumber.value.toString().should.equal(expected.success ? expected.sequences[position] : '0');
154
+ result.constraintViolations.map(item => ({ id: item.constraintId, message: item.message, details: item.details }))
155
+ .should.deep.equal(expected.violations);
156
+ result.errors.map(item => item.message).should.deep.equal(expected.errors);
157
+ }
158
+ if (!expected.success) historyOf(scenario).should.deep.equal(before);
159
+ historyOf(scenario).should.deep.equal(expected.history);
160
+ (await scenario.eventSequence.getNextSequenceNumber()).value.toString().should.equal(expected.next);
161
+ }
162
+ } finally {
163
+ validate.mockRestore();
164
+ await notifications.return?.();
165
+ }
166
+ }
167
+
168
+ describe('fixture-backed composite unique-property keys', () => {
169
+ it('matches the pinned kernel constraints-composite: declared order, dash joining, per-property violations, hashes and rollback',
170
+ () => matchesFixture('constraints-composite', compositeTypes, [FullName, Triple]));
171
+
172
+ it('rejects a repeated key path at the key-shape check, while the distinct shape is accepted', async () => {
173
+ class Repeated implements IConstraint {
174
+ define(builder: IConstraintBuilder) {
175
+ builder.unique(key => key.on(OracleCompositeName, event => event.first, event => event.first));
176
+ }
177
+ }
178
+ constraint('OracleRepeatedPath')(Repeated);
179
+ class Distinct implements IConstraint {
180
+ define(builder: IConstraintBuilder) {
181
+ builder.unique(key => key.on(OracleCompositeName, event => event.first, event => event.last));
182
+ }
183
+ }
184
+ constraint('OracleRepeatedPath')(Distinct);
185
+ new EventScenario({ artifacts: { eventTypes: [OracleCompositeName], constraints: [Distinct] } }).should.be.instanceOf(EventScenario);
186
+ await unsupported(() => new EventScenario({ artifacts: { eventTypes: [OracleCompositeName], constraints: [Repeated] } }),
187
+ 'artifacts.constraints (OracleRepeatedPath)', shapeReason);
188
+ });
189
+
190
+ it('rejects four key properties at the key-shape check, while three are accepted', async () => {
191
+ class Quad {
192
+ @field(String) a = 'a'; @field(String) b = 'b'; @field(String) c = 'c'; @field(String) d = 'd';
193
+ }
194
+ eventType('OracleCompositeQuad')(Quad);
195
+ class Four implements IConstraint {
196
+ define(builder: IConstraintBuilder) {
197
+ builder.unique(key => key.on(Quad, event => event.a, event => event.b, event => event.c, event => event.d));
198
+ }
199
+ }
200
+ constraint('OracleQuad')(Four);
201
+ class Three implements IConstraint {
202
+ define(builder: IConstraintBuilder) { builder.unique(key => key.on(Quad, event => event.a, event => event.b, event => event.c)); }
203
+ }
204
+ constraint('OracleQuad')(Three);
205
+ new EventScenario({ artifacts: { eventTypes: [Quad], constraints: [Three] } }).should.be.instanceOf(EventScenario);
206
+ await unsupported(() => new EventScenario({ artifacts: { eventTypes: [Quad], constraints: [Four] } }),
207
+ 'artifacts.constraints (OracleQuad)', shapeReason);
208
+ });
209
+
210
+ it('rejects nested and indexed key paths at the key-shape check, while the flat path is accepted', async () => {
211
+ // The event schema must stay flat to pass the schema check; the accessors reach the paths 'a.b' and 'items.0'.
212
+ class Nested { @field(String) a = 'a'; }
213
+ eventType('OracleCompositeNested')(Nested);
214
+ type Shaped = { a: { b: string }; items: string[] };
215
+ class Deep implements IConstraint {
216
+ define(builder: IConstraintBuilder) {
217
+ builder.unique(key => key.on(Nested, event => event.a, event => (event as unknown as Shaped).a.b));
218
+ }
219
+ }
220
+ constraint('OracleNestedPath')(Deep);
221
+ class Indexed implements IConstraint {
222
+ define(builder: IConstraintBuilder) {
223
+ builder.unique(key => key.on(Nested, event => event.a, event => (event as unknown as Shaped).items[0]));
224
+ }
225
+ }
226
+ constraint('OracleNestedPath')(Indexed);
227
+ class Flat implements IConstraint {
228
+ define(builder: IConstraintBuilder) { builder.unique(key => key.on(Nested, event => event.a)); }
229
+ }
230
+ constraint('OracleNestedPath')(Flat);
231
+ new EventScenario({ artifacts: { eventTypes: [Nested], constraints: [Flat] } }).should.be.instanceOf(EventScenario);
232
+ await unsupported(() => new EventScenario({ artifacts: { eventTypes: [Nested], constraints: [Deep] } }),
233
+ 'artifacts.constraints (OracleNestedPath)', shapeReason);
234
+ await unsupported(() => new EventScenario({ artifacts: { eventTypes: [Nested], constraints: [Indexed] } }),
235
+ 'artifacts.constraints (OracleNestedPath)', shapeReason);
236
+ });
237
+
238
+ it('rejects a composite with a boolean component, while the boolean alone is accepted', async () => {
239
+ class Mixed { @field(String) label = 'x'; @field(Boolean) active = true; }
240
+ eventType('OracleCompositeMixed')(Mixed);
241
+ class Composite implements IConstraint {
242
+ define(builder: IConstraintBuilder) { builder.unique(key => key.on(Mixed, event => event.label, event => event.active)); }
243
+ }
244
+ constraint('OracleMixed')(Composite);
245
+ class Single implements IConstraint {
246
+ define(builder: IConstraintBuilder) { builder.unique(key => key.on(Mixed, event => event.active)); }
247
+ }
248
+ constraint('OracleMixed')(Single);
249
+ new EventScenario({ artifacts: { eventTypes: [Mixed], constraints: [Single] } }).should.be.instanceOf(EventScenario);
250
+ await unsupported(() => new EventScenario({ artifacts: { eventTypes: [Mixed], constraints: [Composite] } }),
251
+ 'artifacts.constraints (OracleMixed)', 'Every property of a composite key must be a schema-backed string.');
252
+ });
253
+
254
+ it('rejects an unproven component value in a batch and leaves history, results and claims unchanged', async () => {
255
+ const scenario = new EventScenario({ artifacts: { eventTypes: compositeTypes, constraints: [FullName, Triple] } });
256
+ (await scenario.append('A', new OracleCompositeName('Ada', 'Lovelace'))).isSuccess.should.be.true;
257
+ const before = historyOf(scenario);
258
+ const results = [...scenario.results];
259
+ await unsupported(() => scenario.appendMany([{ eventSourceId: 'B', event: new OracleCompositeName('Grace', 'Hopper') },
260
+ { eventSourceId: 'C', event: new OracleCompositeName('Ada', 'Love!') }]), 'artifacts.constraints (OracleFullName)',
261
+ 'Only fixture-backed scalar string and boolean keys are supported.');
262
+ historyOf(scenario).should.deep.equal(before);
263
+ scenario.results.should.deep.equal(results);
264
+ (await scenario.append('D', new OracleCompositeName('Grace', 'Hopper'))).isSuccess.should.be.true;
265
+ (await scenario.append('E', new OracleCompositeName('Ada', 'Lovelace'))).constraintViolations
266
+ .map(item => item.message).should.deep.equal(['Taken: first=Ada', 'Taken: last=Lovelace']);
267
+ });
268
+
269
+ it('rolls back a setup call that ends in a delimiter collision', async () => {
270
+ const scenario = new EventScenario({ artifacts: { eventTypes: compositeTypes, constraints: [FullName, Triple] } });
271
+ await scenario.given.forEventSource('A').events(new OracleCompositeName('a-b', 'c'));
272
+ await scenario.given.forEventSource('B').events(new OracleCompositeName('x', 'y'), new OracleCompositeName('a', 'b-c'))
273
+ .then(() => { throw new Error('Invalid setup succeeded'); }, error => {
274
+ (error as Error).message.should.include('EventScenario given setup failed');
275
+ });
276
+ historyOf(scenario).map(entry => entry.source).should.deep.equal(['A']);
277
+ (await scenario.append('C', new OracleCompositeName('x', 'y'))).isSuccess.should.be.true;
278
+ });
279
+
280
+ it('delivers only committed composite claims to a reactor', async () => {
281
+ const calls: string[] = [];
282
+ @reactor('composite-reactor')
283
+ class CompositeReactor {
284
+ oracleCompositeName(event: OracleCompositeName) { calls.push(`${event.first}/${event.last}`); }
285
+ oracleCompositeAlias(event: OracleCompositeAlias) { calls.push(`${event.family}/${event.given}`); }
286
+ }
287
+ const subject = new ReactorScenario(CompositeReactor, { artifacts: { eventTypes: compositeTypes, constraints: [FullName, Triple] } });
288
+ await subject.when.forEventSource('A').events(new OracleCompositeName('Ada', 'Lovelace'));
289
+ await subject.when.forEventSource('B').events(new OracleCompositeAlias('Lovelace', 'Ada'))
290
+ .then(() => { throw new Error('Colliding alias delivered'); }, error => {
291
+ (error as Error).message.should.include('ReactorScenario action append failed');
292
+ });
293
+ await subject.when.forEventSource('B').events(new OracleCompositeAlias('Ada', 'Lovelace'));
294
+ calls.should.deep.equal(['Ada/Lovelace', 'Lovelace/Ada']);
295
+ });
296
+ });
297
+
298
+ describe('fixture-backed ASCII case-insensitive unique-property keys', () => {
299
+ const casingTypes = [OracleDomainText, OracleDomainRemoved, OracleCompositeName];
300
+ const asciiReason = 'Case-insensitive keys outside the ASCII key domain are not fixture-backed.';
301
+
302
+ it('matches the pinned kernel constraints-ignore-casing: ASCII folding, original-cased details, ownership, hashes and rollback',
303
+ () => matchesFixture('constraints-ignore-casing', casingTypes, [Handle, CasedName]));
304
+
305
+ it('rejects a non-ASCII key under ignoreCasing before mutation, while the case-sensitive definition accepts it', async () => {
306
+ const sensitive = new EventScenario({ artifacts: { eventTypes: compositeTypes, constraints: [FullName, Triple] } });
307
+ (await sensitive.append('A', new OracleCompositeName('Zo\u00e9', 'X'))).isSuccess.should.be.true;
308
+ const scenario = new EventScenario({ artifacts: { eventTypes: casingTypes, constraints: [Handle, CasedName] } });
309
+ (await scenario.append('A', new OracleDomainText('Alice'))).isSuccess.should.be.true;
310
+ const before = historyOf(scenario);
311
+ const results = [...scenario.results];
312
+ const notifications = scenario.eventSequence.appendOperations[Symbol.asyncIterator]();
313
+ try {
314
+ const notification = notifications.next();
315
+ await unsupported(() => scenario.append('B', new OracleDomainText('Zo\u00e9')), 'artifacts.constraints (OracleHandle)', asciiReason);
316
+ await unsupported(() => scenario.appendMany([{ eventSourceId: 'C', event: new OracleDomainText('Bob') },
317
+ { eventSourceId: 'D', event: new OracleCompositeName('zo\u00e9', 'x') }]), 'artifacts.constraints (OracleCasedName)', asciiReason);
318
+ await unsupported(() => scenario.append('E', new OracleCompositeName('Zo\u00e9', 'X')), 'artifacts.constraints (OracleCasedName)', asciiReason);
319
+ historyOf(scenario).should.deep.equal(before);
320
+ scenario.results.should.deep.equal(results);
321
+ (await scenario.append('C', new OracleDomainText('BOB'))).isSuccess.should.be.true;
322
+ (await notification).value!.map(item => item.event.content).should.deep.equal([{ key: 'BOB' }]);
323
+ } finally { await notifications.return?.(); }
324
+ (await scenario.append('F', new OracleDomainText('bob'))).constraintViolations.map(item => item.message)
325
+ .should.deep.equal(['Handle taken: bob']);
326
+ });
327
+
328
+ it('rejects ignoreCasing on a boolean key, while the case-sensitive boolean key is accepted', async () => {
329
+ class Flagged { @field(Boolean) active = true; }
330
+ eventType('OracleCasedFlag')(Flagged);
331
+ class Folded implements IConstraint {
332
+ define(builder: IConstraintBuilder) { builder.unique(key => key.on(Flagged, event => event.active).ignoreCasing()); }
333
+ }
334
+ constraint('OracleCasedFlag')(Folded);
335
+ class Exact implements IConstraint {
336
+ define(builder: IConstraintBuilder) { builder.unique(key => key.on(Flagged, event => event.active)); }
337
+ }
338
+ constraint('OracleCasedFlag')(Exact);
339
+ new EventScenario({ artifacts: { eventTypes: [Flagged], constraints: [Exact] } }).should.be.instanceOf(EventScenario);
340
+ await unsupported(() => new EventScenario({ artifacts: { eventTypes: [Flagged], constraints: [Folded] } }),
341
+ 'artifacts.constraints (OracleCasedFlag)', 'Case-insensitive keys must be schema-backed strings.');
342
+ });
343
+ });
@@ -0,0 +1,236 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import 'reflect-metadata';
5
+ import { readFileSync } from 'node:fs';
6
+ import { chai, describe, it, vi } from 'vitest';
7
+ import { field } from '@cratis/fundamentals';
8
+ import { eventType, getEventTypeMetadata } from '../events/eventTypeDecorator.js';
9
+ import { constraint } from '../events/constraints/constraint.js';
10
+ import { compileConstraints } from '../events/constraints/Constraints.js';
11
+ import type { IConstraint } from '../events/constraints/IConstraint.js';
12
+ import type { IConstraintBuilder } from '../events/constraints/IConstraintBuilder.js';
13
+ import { removeConstraint } from '../events/constraints/removeConstraint.js';
14
+ import { EventScenario, UnsupportedEventSequenceOperation } from './index.js';
15
+ import { InProcessConstraints } from './InProcessConstraints.js';
16
+ import { ReactorScenario } from './ReactorScenario.js';
17
+ import { reactor } from '../reactors/reactor.js';
18
+
19
+ chai.should();
20
+ class OracleCycleFirst { @field(String) label: string; constructor(label: string) { this.label = label; } }
21
+ eventType('OracleCycleFirst')(OracleCycleFirst);
22
+ class OracleCycleSibling { @field(String) label: string; constructor(label: string) { this.label = label; } }
23
+ eventType('OracleCycleSibling')(OracleCycleSibling);
24
+ class OracleCycleRemoved { @field(String) label: string; constructor(label: string) { this.label = label; } }
25
+ eventType('OracleCycleRemoved')(OracleCycleRemoved);
26
+ removeConstraint('OracleCycle')(OracleCycleRemoved);
27
+ class OracleCycleExpired { @field(String) label: string; constructor(label: string) { this.label = label; } }
28
+ eventType('OracleCycleExpired')(OracleCycleExpired);
29
+ removeConstraint('OracleCycle')(OracleCycleExpired);
30
+ class OracleCycleRenewed { @field(String) label: string; constructor(label: string) { this.label = label; } }
31
+ eventType('OracleCycleRenewed')(OracleCycleRenewed);
32
+ removeConstraint('OracleCycle')(OracleCycleRenewed);
33
+ // Same-named uniqueFor declarations merge into one definition covering the whole set.
34
+ class CycleFirst implements IConstraint {
35
+ define(builder: IConstraintBuilder) { builder.uniqueFor(OracleCycleFirst, 'Cycle occupied', 'OracleCycle'); }
36
+ }
37
+ constraint('CycleFirst')(CycleFirst);
38
+ class CycleSibling implements IConstraint {
39
+ define(builder: IConstraintBuilder) { builder.uniqueFor(OracleCycleSibling, undefined, 'OracleCycle'); }
40
+ }
41
+ constraint('CycleSibling')(CycleSibling);
42
+ class CycleRenewed implements IConstraint {
43
+ define(builder: IConstraintBuilder) { builder.uniqueFor(OracleCycleRenewed, undefined, 'OracleCycle'); }
44
+ }
45
+ constraint('CycleRenewed')(CycleRenewed);
46
+
47
+ const aliases = { first: OracleCycleFirst, sibling: OracleCycleSibling, cycleRemoved: OracleCycleRemoved,
48
+ cycleExpired: OracleCycleExpired, renewed: OracleCycleRenewed };
49
+ type Alias = keyof typeof aliases;
50
+ type Input = { source: string; type: Alias; value: string };
51
+ type History = { sequence: string; source: string; sourceType: string; streamType: string; streamId: string;
52
+ type: string; content: Record<string, string>; hash: string };
53
+ type Violation = { EventTypeId: string; SequenceNumber: string; ConstraintType: number; ConstraintName: string;
54
+ Message: string; Details: Record<string, string> };
55
+ type Outcome = { success: boolean; sequences: string[]; violations: Array<{ id: string; message: string; details: Record<string, string> }>;
56
+ wireViolations: Violation[]; errors: string[]; history: History[]; next: string };
57
+ type Fixture = { eventSchemas: Record<string, { eventTypeId: string; properties: Record<string, string> }>;
58
+ constraintDefinitions: Array<{ kind: string; name: string; eventTypes: string[]; removedWithEventTypeIds: string[]; message: string }>;
59
+ isolatedConstraintOperations: Array<{ mode: 'single' | 'batch'; events: Input[] }>;
60
+ expected: { outcomes: Outcome[] } };
61
+ const aliasById = Object.fromEntries(Object.entries(aliases).map(([alias, type]) => [getEventTypeMetadata(type)!.eventType.id.value, alias]));
62
+ const makeEvent = ({ type, value }: Input): object => new aliases[type](value);
63
+ const historyOf = (scenario: EventScenario): History[] => scenario.appendedEvents.map(entry => ({
64
+ sequence: entry.context.sequenceNumber.toString(), source: entry.context.eventSourceId,
65
+ sourceType: entry.context.eventSourceType, streamType: entry.context.eventStreamType, streamId: entry.context.eventStreamId,
66
+ type: entry.eventType.id.value, content: entry.content as Record<string, string>, hash: entry.context.hash
67
+ }));
68
+ const unsupported = async (action: () => unknown, operation: string, reason: string) => {
69
+ await Promise.resolve().then(action).then(() => { throw new Error('Expected rejection'); }, error => {
70
+ (error instanceof UnsupportedEventSequenceOperation).should.be.true;
71
+ (error as Error).message.should.include(`UnsupportedEventSequenceOperation: ${operation}:`);
72
+ (error as Error).message.should.include(reason);
73
+ (error as Error).message.should.include('Use a kernel-backed test.');
74
+ });
75
+ };
76
+ const siblingTypes = [OracleCycleFirst, OracleCycleSibling, OracleCycleRemoved, OracleCycleExpired];
77
+ const cycleTypes = [...siblingTypes, OracleCycleRenewed];
78
+ const shapeReason = 'This unique event type set and removal combination is not fixture-backed.';
79
+
80
+ describe('fixture-backed unique event type cycles', () => {
81
+ for (const [name, selected, constraints] of [
82
+ ['constraints-event-type-siblings', siblingTypes, [CycleFirst, CycleSibling]],
83
+ ['constraints-event-type-cycles', cycleTypes, [CycleFirst, CycleSibling, CycleRenewed]]
84
+ ] as const) {
85
+ it(`matches the pinned kernel ${name}: raw and mapped violations, cycles, hashes and rollback`, async () => {
86
+ const fixture = JSON.parse(readFileSync(new URL(`./fixtures/${name}.json`, import.meta.url), 'utf8')) as Fixture;
87
+ Object.fromEntries(selected.map(type => {
88
+ const metadata = getEventTypeMetadata(type)!;
89
+ return [aliasById[metadata.eventType.id.value], { eventTypeId: metadata.eventType.id.value,
90
+ properties: Object.fromEntries(Object.entries(metadata.schema.properties ?? {}).map(([key, property]) => [key, property.type])) }];
91
+ })).should.deep.equal(fixture.eventSchemas);
92
+ const compiled = compileConstraints({ eventTypes: [...selected], constraints: [...constraints] });
93
+ [...compiled].map(([key, capture]) => ({ kind: 'uniqueEventType', name: key,
94
+ eventTypes: capture.uniqueEventType!.eventTypeIds!.map(id => aliasById[id]),
95
+ removedWithEventTypeIds: capture.uniqueEventType!.removedWithEventTypeIds!.map(id => aliasById[id]),
96
+ message: capture.uniqueEventType!.message ?? ''
97
+ })).should.deep.equal(fixture.constraintDefinitions);
98
+ fixture.isolatedConstraintOperations.length.should.be.greaterThan(0);
99
+ fixture.expected.outcomes.length.should.equal(fixture.isolatedConstraintOperations.length);
100
+ fixture.expected.outcomes.some(outcome => !outcome.success).should.be.true;
101
+ const scenario = new EventScenario({ artifacts: { eventTypes: [...selected], constraints: [...constraints] } });
102
+ const notifications = scenario.eventSequence.appendOperations[Symbol.asyncIterator]();
103
+ const validate = vi.spyOn(InProcessConstraints.prototype, 'validate');
104
+ let count = 0;
105
+ try {
106
+ for (const [index, operation] of fixture.isolatedConstraintOperations.entries()) {
107
+ const expected = fixture.expected.outcomes[index];
108
+ const before = historyOf(scenario);
109
+ const calls = validate.mock.calls.length;
110
+ const nextNotification = notifications.next();
111
+ const results = operation.mode === 'single'
112
+ ? [await scenario.append(operation.events[0].source, makeEvent(operation.events[0]))]
113
+ : await scenario.appendMany(operation.events.map(entry => ({ eventSourceId: entry.source, event: makeEvent(entry) })));
114
+ const notification = (await nextNotification).value!;
115
+ validate.mock.calls.length.should.equal(calls + 1);
116
+ const raw = validate.mock.results.at(-1)?.value as ReturnType<InProcessConstraints['validate']>;
117
+ raw.map(({ SequenceNumber, ...violation }) => ({ ...violation, SequenceNumber: SequenceNumber.toString() }))
118
+ .should.deep.equal(expected.wireViolations);
119
+ notification.length.should.equal(operation.events.length);
120
+ notification.every(item => item.result.isSuccess === expected.success).should.be.true;
121
+ count += operation.events.length;
122
+ scenario.results.length.should.equal(count);
123
+ results.length.should.equal(operation.events.length);
124
+ for (const [position, result] of results.entries()) {
125
+ result.isSuccess.should.equal(expected.success);
126
+ result.sequenceNumber.value.toString().should.equal(expected.success ? expected.sequences[position] : '0');
127
+ result.constraintViolations.map(item => ({ id: item.constraintId, message: item.message, details: item.details }))
128
+ .should.deep.equal(expected.violations);
129
+ result.errors.map(item => item.message).should.deep.equal(expected.errors);
130
+ }
131
+ if (!expected.success) historyOf(scenario).should.deep.equal(before);
132
+ historyOf(scenario).should.deep.equal(expected.history);
133
+ (await scenario.eventSequence.getNextSequenceNumber()).value.toString().should.equal(expected.next);
134
+ }
135
+ } finally {
136
+ validate.mockRestore();
137
+ await notifications.return?.();
138
+ }
139
+ });
140
+ }
141
+
142
+ it('rolls back a release and reclaim in a failing setup call without results or notifications', async () => {
143
+ const scenario = new EventScenario({ artifacts: { eventTypes: siblingTypes, constraints: [CycleFirst, CycleSibling] } });
144
+ const notifications = scenario.eventSequence.appendOperations[Symbol.asyncIterator]();
145
+ try {
146
+ const first = notifications.next();
147
+ await scenario.given.forEventSource('A').events(new OracleCycleFirst('claim'));
148
+ (await first).value!.length.should.equal(1);
149
+ const before = historyOf(scenario);
150
+ await scenario.given.forEventSource('A').events(new OracleCycleRemoved('release'), new OracleCycleFirst('reopen'),
151
+ new OracleCycleSibling('blocked'))
152
+ .then(() => { throw new Error('Invalid setup succeeded'); }, error => {
153
+ (error as Error).message.should.include('EventScenario given setup failed');
154
+ });
155
+ historyOf(scenario).should.deep.equal(before);
156
+ scenario.results.length.should.equal(0);
157
+ (await scenario.eventSequence.getNextSequenceNumber()).value.should.equal(1n);
158
+ const next = notifications.next();
159
+ const blocked = await scenario.append('A', new OracleCycleSibling('still held'));
160
+ blocked.constraintViolations.map(item => item.constraintId).should.deep.equal(['OracleCycle']);
161
+ (await next).value!.map(item => item.event.eventType.id.value).should.deep.equal(['OracleCycleSibling']);
162
+ } finally { await notifications.return?.(); }
163
+ });
164
+
165
+ it('delivers only committed claim and release events to a reactor', async () => {
166
+ const calls: string[] = [];
167
+ @reactor('cycle-reactor')
168
+ class CycleReactor {
169
+ oracleCycleFirst(event: OracleCycleFirst) { calls.push(event.label); }
170
+ oracleCycleSibling(event: OracleCycleSibling) { calls.push(event.label); }
171
+ oracleCycleExpired(event: OracleCycleExpired) { calls.push(event.label); }
172
+ }
173
+ const subject = new ReactorScenario(CycleReactor,
174
+ { artifacts: { eventTypes: siblingTypes, constraints: [CycleFirst, CycleSibling] } });
175
+ await subject.when.forEventSource('A').events(new OracleCycleFirst('claim'));
176
+ await subject.when.forEventSource('A').events(new OracleCycleSibling('blocked'))
177
+ .then(() => { throw new Error('Occupied cycle delivered'); }, error => {
178
+ (error as Error).message.should.include('ReactorScenario action append failed');
179
+ });
180
+ calls.should.deep.equal(['claim']);
181
+ subject.results.length.should.equal(1);
182
+ await subject.when.forEventSource('A').events(new OracleCycleExpired('release'), new OracleCycleSibling('reclaim'));
183
+ calls.should.deep.equal(['claim', 'release', 'reclaim']);
184
+ subject.results.length.should.equal(2);
185
+ });
186
+
187
+ it('rejects unsupported input in a batch and leaves history and results unchanged', async () => {
188
+ const scenario = new EventScenario({ artifacts: { eventTypes: siblingTypes, constraints: [CycleFirst, CycleSibling] } });
189
+ (await scenario.append('A', new OracleCycleFirst('claim'))).isSuccess.should.be.true;
190
+ const before = historyOf(scenario);
191
+ const results = scenario.results;
192
+ await unsupported(() => scenario.appendMany([{ eventSourceId: 'A', event: new OracleCycleExpired('release') },
193
+ { eventSourceId: 'B!', event: new OracleCycleFirst('claim') }]), 'appendMany.source (B!)',
194
+ 'Only simple source identifiers are fixture-backed.');
195
+ historyOf(scenario).should.deep.equal(before);
196
+ scenario.results.should.deep.equal(results);
197
+ const blocked = await scenario.append('A', new OracleCycleSibling('still held'));
198
+ blocked.isSuccess.should.be.false;
199
+ blocked.constraintViolations.map(item => item.message).should.deep.equal(['Cycle occupied']);
200
+ });
201
+
202
+ it('rejects covered and removal type shapes that no fixture installs, at the shape check', async () => {
203
+ await unsupported(() => new EventScenario({ artifacts: {
204
+ eventTypes: [OracleCycleFirst, OracleCycleSibling, OracleCycleRemoved], constraints: [CycleFirst, CycleSibling]
205
+ } }), 'artifacts.constraints (OracleCycle)', shapeReason);
206
+ await unsupported(() => new EventScenario({ artifacts: {
207
+ eventTypes: [OracleCycleFirst, OracleCycleRemoved, OracleCycleRenewed], constraints: [CycleFirst, CycleRenewed]
208
+ } }), 'artifacts.constraints (OracleCycle)', shapeReason);
209
+ await unsupported(() => new EventScenario({ artifacts: {
210
+ eventTypes: [OracleCycleFirst, OracleCycleSibling, OracleCycleRemoved, OracleCycleRenewed],
211
+ constraints: [CycleFirst, CycleSibling, CycleRenewed]
212
+ } }), 'artifacts.constraints (OracleCycle)', shapeReason);
213
+ });
214
+
215
+ it('rejects a fixture-shaped cycle installed alongside another definition', async () => {
216
+ class OtherOnce { @field(String) label = 'other'; }
217
+ eventType('OracleCycleOtherOnce')(OtherOnce);
218
+ class OtherOnceConstraint implements IConstraint {
219
+ define(builder: IConstraintBuilder) { builder.uniqueFor(OtherOnce, undefined, 'OracleCycleOther'); }
220
+ }
221
+ constraint('OracleCycleOther')(OtherOnceConstraint);
222
+ await unsupported(() => new EventScenario({ artifacts: {
223
+ eventTypes: [...siblingTypes, OtherOnce], constraints: [CycleFirst, CycleSibling, OtherOnceConstraint]
224
+ } }), 'artifacts.constraints (OracleCycle)', 'Unique event cycles alongside other definitions are not fixture-backed.');
225
+ });
226
+
227
+ it('rejects a fieldless event-type remover at the schema check', async () => {
228
+ class FieldlessRemoval {}
229
+ eventType('OracleCycleFieldless')(FieldlessRemoval);
230
+ removeConstraint('OracleCycle')(FieldlessRemoval);
231
+ await unsupported(() => new EventScenario({ artifacts: {
232
+ eventTypes: [OracleCycleFirst, OracleCycleSibling, OracleCycleRemoved, FieldlessRemoval],
233
+ constraints: [CycleFirst, CycleSibling]
234
+ } }), 'artifacts.eventTypes.schema (FieldlessRemoval)', 'Only flat, unclassified string/boolean fields are fixture-backed.');
235
+ });
236
+ });
@@ -172,7 +172,7 @@ describe('fixture-backed unscoped constraints', () => {
172
172
  const production = compileConstraints({ eventTypes: [GlobalOnce, SelectedOnce], constraints: [] });
173
173
  production.get('ScenarioSharedOnce')!.uniqueEventType!.eventTypeIds.should.deep.equal(['GlobalOnce', 'SelectedOnce']);
174
174
  unsupported(() => new EventScenario({ artifacts: { eventTypes: [SelectedOnce] } }), 'artifacts.constraints')
175
- .message.should.include('multi-type unique event cycles');
175
+ .message.should.include('This unique event type set and removal combination is not fixture-backed.');
176
176
  });
177
177
 
178
178
  it('rejects removal of a selected unique-event-type constraint at construction', () => {
@@ -183,8 +183,8 @@ describe('fixture-backed unscoped constraints', () => {
183
183
  eventType('OnceNameRemoval')(Removal);
184
184
  removeConstraint('OnceName')(Removal);
185
185
  const message = unsupported(() => new EventScenario({ artifacts: { eventTypes: [Once, Removal] } }), 'artifacts.constraints').message;
186
- message.should.include('Removal');
187
- message.should.include('Use a kernel-backed test.');
186
+ message.should.include('OnceName');
187
+ message.should.include('This unique event type set and removal combination is not fixture-backed.');
188
188
  });
189
189
 
190
190
  it('rejects a global removal decorator targeting a selected constraint', () => {
@@ -367,14 +367,14 @@ describe('fixture-backed unscoped constraints', () => {
367
367
  scoped(builder => builder.perEventStreamType().unique(key => key.on(OracleKeyClaimed, event => event.key)));
368
368
  scoped(builder => builder.perEventSourceType().unique(key => key.on(OracleKeyClaimed, event => event.key)));
369
369
  scoped(builder => builder.unique(key => key.on(OracleKeyClaimed, event => event.key, event => event.key)));
370
- scoped(builder => builder.unique(key => key.on(OracleKeyClaimed, event => event.key).ignoreCasing()));
370
+ scoped(builder => builder.perEventStreamId().unique(key => key.on(OracleKeyClaimed, event => event.key).ignoreCasing()));
371
371
  scoped(builder => builder.unique(key => key.on(OracleKeyClaimed, event => event.key))
372
372
  .uniqueFor(OracleOnceRecorded));
373
373
  class AnotherOnce { @field(String) label = 'second'; }
374
374
  eventType('AnotherOnce')(AnotherOnce);
375
375
  unique('OracleOnce')(AnotherOnce);
376
376
  unsupported(() => new EventScenario({ artifacts: { eventTypes: [...types, AnotherOnce], constraints: [OracleFluentOnceConstraint] } }), 'artifacts.constraints')
377
- .message.should.include('multi-type unique event cycles');
377
+ .message.should.include('This unique event type set and removal combination is not fixture-backed.');
378
378
  const subject = scenario();
379
379
  await subject.append('A', new OracleKeyClaimed('Alpha'));
380
380
  await subject.append('B', new OracleKeyClaimed('beta'));
@@ -57,7 +57,9 @@ interface Fixture {
57
57
  const directory = new URL('./fixtures/', import.meta.url);
58
58
  const fixtures = readdirSync(directory).filter(name => name.endsWith('.json') &&
59
59
  !['batches.json', 'batch-omitted-routes.json', 'batch-rollback.json', 'builders.json', 'constraints.json', 'constraints-isolation.json', 'constraints-key-domain.json',
60
- 'constraints-property-lifecycle.json', 'constraints-property-covered-removal.json'].includes(name)).map(name => ({
60
+ 'constraints-property-lifecycle.json', 'constraints-property-covered-removal.json', 'constraints-event-type-cycles.json',
61
+ 'constraints-event-type-siblings.json', 'constraints-composite.json',
62
+ 'constraints-ignore-casing.json'].includes(name)).map(name => ({
61
63
  name, fixture: JSON.parse(readFileSync(new URL(name, directory), 'utf8')) as Fixture
62
64
  }));
63
65
  const artifacts = { eventTypes: [OracleEventRecorded, AlternateRecorded, BoundaryRecorded], constraints: [] };
@@ -98,10 +98,21 @@ export class EventScenario {
98
98
  }
99
99
  for (const entry of capture.uniqueConstraint?.eventDefinitions ?? []) {
100
100
  const type = eventTypes.find(type => getEventTypeFor(type).id.value === entry.eventTypeId)!;
101
- if (!['string', 'boolean'].includes(getEventTypeMetadata(type)?.schema.properties?.[entry.properties[0]]?.type ?? '')) {
101
+ const schemaTypes = entry.properties.map(property => getEventTypeMetadata(type)?.schema.properties?.[property]?.type ?? '');
102
+ if (entry.properties.length === 1 && !['string', 'boolean'].includes(schemaTypes[0])) {
102
103
  throw new UnsupportedEventSequenceOperation('artifacts.constraints', name,
103
104
  'The constrained property must be a schema-backed string or boolean.');
104
105
  }
106
+ // constraints-ignore-casing.json captures folded string keys only.
107
+ if (capture.uniqueConstraint!.ignoreCasing && schemaTypes.some(schemaType => schemaType !== 'string')) {
108
+ throw new UnsupportedEventSequenceOperation('artifacts.constraints', name,
109
+ 'Case-insensitive keys must be schema-backed strings.');
110
+ }
111
+ // constraints-composite.json captures string components only.
112
+ if (entry.properties.length > 1 && schemaTypes.some(schemaType => schemaType !== 'string')) {
113
+ throw new UnsupportedEventSequenceOperation('artifacts.constraints', name,
114
+ 'Every property of a composite key must be a schema-backed string.');
115
+ }
105
116
  }
106
117
  }
107
118
  } catch (error) {
@@ -26,10 +26,34 @@ function keyHash(value: string): string {
26
26
  return createHash('sha256').update(value).digest('hex');
27
27
  }
28
28
 
29
+ type UniqueCapture = NonNullable<ConstraintCapture['uniqueConstraint']>;
30
+ type Claim = { key: string; parts: Array<{ property: string; raw: string }> };
31
+
32
+ /**
33
+ * The kernel key: each declared property's string in declared order, joined with a literal '-'
34
+ * (UniqueConstraintDefinitionExtensions.GetPropertiesAndValues/GetValue at Chronicle 8fe5d30). Not a tuple:
35
+ * ['a-b', 'c'] and ['a', 'b-c'] claim the same key, as constraints-composite.json captures.
36
+ */
37
+ function claimOf(name: string, unique: UniqueCapture, eventTypeId: string, content: Record<string, unknown>): Claim | undefined {
38
+ const properties = unique.eventDefinitions.find(entry => entry.eventTypeId === eventTypeId)?.properties;
39
+ if (properties === undefined) return undefined;
40
+ const parts = properties.map(property => ({ property, raw: kernelKeyString(content[property], name) }));
41
+ const joined = parts.map(part => part.raw).join('-');
42
+ if (!unique.ignoreCasing) return { key: keyHash(joined), parts };
43
+ // The kernel applies .NET ToLowerInvariant to the joined value. constraints-ignore-casing.json proves
44
+ // only the ASCII key domain, where that is exactly A-Z to a-z; host Unicode case tables are not used.
45
+ if (!/^[\x20-\x7e]*$/.test(joined)) {
46
+ throw new UnsupportedEventSequenceOperation('artifacts.constraints', name,
47
+ 'Case-insensitive keys outside the ASCII key domain are not fixture-backed.');
48
+ }
49
+ return { key: keyHash(joined.replace(/[A-Z]/g, letter => String.fromCharCode(letter.charCodeAt(0) + 32))), parts };
50
+ }
51
+
29
52
  /** Narrow, fixture-backed unscoped constraint validation over serialized event snapshots. */
30
53
  export class InProcessConstraints {
31
54
  private readonly _constrainedProperties = new Map<string, Set<string>>();
32
55
  private readonly _removalTypes = new Set<string>();
56
+ private readonly _propertyRemovalTypes = new Set<string>();
33
57
 
34
58
  constructor(private readonly _definitions: ReadonlyMap<string, ConstraintCapture>) {
35
59
  const coveredTypes = new Map<string, string>();
@@ -43,10 +67,13 @@ export class InProcessConstraints {
43
67
  }
44
68
  if (capture.uniqueConstraint) {
45
69
  const unique = capture.uniqueConstraint;
46
- if (unique.ignoreCasing || unique.eventDefinitions.length === 0 ||
47
- unique.eventDefinitions.some(entry => entry.properties.length !== 1 ||
48
- !/^[a-z][a-zA-Z0-9]*$/.test(entry.properties[0]))) {
49
- throw this.unsupported(name, 'Case folding and composite or nested keys are not fixture-backed.');
70
+ // constraints-composite.json installs one, two and three flat properties per event type.
71
+ // The kernel keys properties by path with ToDictionary, so duplicate paths are not a key shape.
72
+ if (unique.eventDefinitions.length === 0 || unique.eventDefinitions.some(entry =>
73
+ entry.properties.length < 1 || entry.properties.length > 3 ||
74
+ new Set(entry.properties).size !== entry.properties.length ||
75
+ entry.properties.some(property => !/^[a-z][a-zA-Z0-9]*$/.test(property)))) {
76
+ throw this.unsupported(name, 'Only one to three distinct flat properties per event type are fixture-backed; nested, indexed or repeated key paths are not.');
50
77
  }
51
78
  for (const id of [unique.removedWithEventTypeId, ...(unique.removedWithEventTypeIds ?? [])]) {
52
79
  if (id === undefined) continue;
@@ -56,17 +83,32 @@ export class InProcessConstraints {
56
83
  }
57
84
  removalOwners.set(id, name);
58
85
  this._removalTypes.add(id);
86
+ this._propertyRemovalTypes.add(id);
59
87
  }
60
88
  for (const entry of unique.eventDefinitions) {
61
89
  const properties = this._constrainedProperties.get(entry.eventTypeId) ?? new Set<string>();
62
- properties.add(entry.properties[0]);
90
+ entry.properties.forEach(property => properties.add(property));
63
91
  this._constrainedProperties.set(entry.eventTypeId, properties);
64
92
  }
65
93
  } else if (capture.uniqueEventType) {
66
94
  const unique = capture.uniqueEventType;
67
- if (unique.removedWithEventTypeIds?.length || (unique.eventTypeIds?.length ?? 1) !== 1) {
68
- throw this.unsupported(name, 'Removal and multi-type unique event cycles are not fixture-backed.');
95
+ const ids = unique.eventTypeIds ?? [unique.eventTypeId];
96
+ const removals = unique.removedWithEventTypeIds ?? [];
97
+ const shared = ids.filter(id => removals.includes(id)).length;
98
+ // Exactly the shapes installed by the pinned fixtures: one covered type without removal
99
+ // (constraints.json), two covered types with two separate removers
100
+ // (constraints-event-type-siblings.json), and three covered types with three removers of
101
+ // which one is also covered (constraints-event-type-cycles.json).
102
+ if (!(ids.length === 1 && removals.length === 0) &&
103
+ !(ids.length === 2 && removals.length === 2 && shared === 0) &&
104
+ !(ids.length === 3 && removals.length === 3 && shared === 1)) {
105
+ throw this.unsupported(name, 'This unique event type set and removal combination is not fixture-backed.');
69
106
  }
107
+ // Each cycle fixture installs its definition alone; interaction with other definitions is unproven.
108
+ if (removals.length && _definitions.size !== 1) {
109
+ throw this.unsupported(name, 'Unique event cycles alongside other definitions are not fixture-backed.');
110
+ }
111
+ removals.forEach(id => this._removalTypes.add(id));
70
112
  } else {
71
113
  throw this.unsupported(name, 'Unknown constraint definition.');
72
114
  }
@@ -89,7 +131,7 @@ export class InProcessConstraints {
89
131
  }
90
132
 
91
133
  isRemovalOnlyType(eventTypeId: string): boolean {
92
- return this._removalTypes.has(eventTypeId) && !this._constrainedProperties.has(eventTypeId);
134
+ return this._propertyRemovalTypes.has(eventTypeId) && !this._constrainedProperties.has(eventTypeId);
93
135
  }
94
136
 
95
137
  hasRemovalType(eventTypeId: string): boolean { return this._removalTypes.has(eventTypeId); }
@@ -116,34 +158,38 @@ export class InProcessConstraints {
116
158
  claims.delete(source);
117
159
  continue;
118
160
  }
119
- const property = unique.eventDefinitions.find(entry => entry.eventTypeId === type)?.properties[0];
120
- if (property === undefined) continue;
121
- const value = prior.content[property];
122
- if (typeof value !== 'string' && typeof value !== 'boolean') throw this.unsupported(name, 'History has an unproven key.');
123
- claims.set(source, { key: keyHash(kernelKeyString(value, name)),
161
+ const properties = unique.eventDefinitions.find(entry => entry.eventTypeId === type)?.properties;
162
+ if (properties === undefined) continue;
163
+ if (properties.some(property => !['string', 'boolean'].includes(typeof prior.content[property]))) {
164
+ throw this.unsupported(name, 'History has an unproven key.');
165
+ }
166
+ claims.set(source, { key: claimOf(name, unique, type, prior.content)!.key,
124
167
  sequence: prior.context.sequenceNumber.toString() });
125
168
  }
126
169
  owners.set(name, claims);
127
170
  }
128
171
  const stagedKeys = new Map<string, Map<string, string>>();
129
- const stagedTypes = new Set<string>();
172
+ const stagedCycles = new Map<string, Map<string, 'open' | 'released'>>();
130
173
  for (const event of incoming) {
131
174
  const type = event.eventType.id.value;
132
175
  const source = event.context.eventSourceId;
176
+ const violationCount = violations.length;
133
177
  for (const [name, definition] of this._definitions) {
134
- const property = definition.uniqueConstraint?.eventDefinitions.find(entry => entry.eventTypeId === type)?.properties[0];
135
- if (property !== undefined) {
136
- const raw = kernelKeyString(event.content[property], name);
137
- const key = keyHash(raw);
178
+ const claim = definition.uniqueConstraint && claimOf(name, definition.uniqueConstraint, type, event.content);
179
+ if (claim) {
180
+ const key = claim.key;
138
181
  const existing = [...owners.get(name)!.entries()].find(([, claim]) => claim.key === key);
139
182
  const batchClaims = stagedKeys.get(name) ?? new Map<string, string>();
140
183
  const batchOwner = batchClaims.get(key);
141
184
  if (existing && existing[0] !== source || batchOwner !== undefined && batchOwner !== source) {
142
- const details = { PropertyName: property, PropertyValue: raw };
143
- violations.push({ EventTypeId: type, SequenceNumber: BigInt(existing?.[1].sequence ?? unavailable), ConstraintType: 1,
144
- ConstraintName: name,
145
- Message: `Event '${type}' on member '${property}' violated a unique constraint on sequence number ${existing?.[1].sequence ?? unavailable}`,
146
- Details: details });
185
+ // One violation per declared property, in declared order, each with its own original value.
186
+ const sequence = existing?.[1].sequence ?? unavailable;
187
+ for (const { property, raw } of claim.parts) {
188
+ violations.push({ EventTypeId: type, SequenceNumber: BigInt(sequence), ConstraintType: 1,
189
+ ConstraintName: name,
190
+ Message: `Event '${type}' on member '${property}' violated a unique constraint on sequence number ${sequence}`,
191
+ Details: { PropertyName: property, PropertyValue: raw } });
192
+ }
147
193
  } else {
148
194
  batchClaims.set(key, source);
149
195
  stagedKeys.set(name, batchClaims);
@@ -151,15 +197,41 @@ export class InProcessConstraints {
151
197
  }
152
198
  const uniqueType = definition.uniqueEventType;
153
199
  if (uniqueType && (uniqueType.eventTypeIds ?? [uniqueType.eventTypeId]).includes(type)) {
154
- const first = history.find(prior => prior.context.eventSourceId === source &&
155
- (uniqueType.eventTypeIds ?? [uniqueType.eventTypeId]).includes(prior.eventType.id.value));
156
- const staged = stagedTypes.has(`${name}\u0000${source}`);
157
- if (first || staged) {
158
- violations.push({ EventTypeId: type, SequenceNumber: first?.context.sequenceNumber ?? BigInt(unavailable), ConstraintType: 2,
200
+ const covered = uniqueType.eventTypeIds ?? [uniqueType.eventTypeId];
201
+ const removals = uniqueType.removedWithEventTypeIds ?? [];
202
+ // Durable answer: the earliest covered event after the latest removal for this source.
203
+ let first: AppendedEvent | undefined;
204
+ for (const prior of history) {
205
+ if (prior.context.eventSourceId !== source) continue;
206
+ const priorType = prior.eventType.id.value;
207
+ if (removals.includes(priorType)) first = undefined;
208
+ else if (!first && covered.includes(priorType)) first = prior;
209
+ }
210
+ const cycles = stagedCycles.get(name) ?? new Map<string, 'open' | 'released'>();
211
+ const state = cycles.get(source);
212
+ // A cycle state recorded earlier in this append overrides durable history.
213
+ if (state === 'open' || state === undefined && first) {
214
+ // Durable storage supplies the sequence even when a staged claim causes rejection.
215
+ const sequence = first?.context.sequenceNumber.toString() ?? unavailable;
216
+ violations.push({ EventTypeId: type, SequenceNumber: BigInt(sequence), ConstraintType: 2,
159
217
  ConstraintName: name,
160
- Message: `Event '${type}' violated a unique event type constraint on sequence number ${first?.context.sequenceNumber.toString() ?? unavailable}`,
218
+ Message: `Event '${type}' violated a unique event type constraint on sequence number ${sequence}`,
161
219
  Details: {} });
162
- } else stagedTypes.add(`${name}\u0000${source}`);
220
+ } else {
221
+ cycles.set(source, 'open');
222
+ stagedCycles.set(name, cycles);
223
+ }
224
+ }
225
+ }
226
+ // Kernel batch observers release a cycle only after the entire event validates.
227
+ // A covered-and-removal event first attempts its claim, then releases it.
228
+ if (violations.length === violationCount) {
229
+ for (const [name, definition] of this._definitions) {
230
+ if (definition.uniqueEventType?.removedWithEventTypeIds?.includes(type)) {
231
+ const cycles = stagedCycles.get(name) ?? new Map<string, 'open' | 'released'>();
232
+ cycles.set(source, 'released');
233
+ stagedCycles.set(name, cycles);
234
+ }
163
235
  }
164
236
  }
165
237
  }