@shirudo/ddd-kit 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -17,6 +17,7 @@ var BUILT_IN_TAGS = /* @__PURE__ */ new Set([
17
17
  "[object Boolean]",
18
18
  "[object Number]",
19
19
  "[object String]",
20
+ "[object BigInt]",
20
21
  "[object ArrayBuffer]",
21
22
  "[object SharedArrayBuffer]",
22
23
  "[object DataView]"
@@ -37,11 +38,117 @@ var arrayBufferByteLengthGet = intrinsicGetter(
37
38
  ArrayBuffer.prototype,
38
39
  "byteLength"
39
40
  );
41
+ var sharedArrayBufferByteLengthGet = typeof SharedArrayBuffer === "undefined" ? void 0 : intrinsicGetter(SharedArrayBuffer.prototype, "byteLength");
40
42
  var regExpSourceGet = intrinsicGetter(RegExp.prototype, "source");
41
43
  var booleanValueOf = Boolean.prototype.valueOf;
42
44
  var numberValueOf = Number.prototype.valueOf;
43
45
  var stringValueOf = String.prototype.valueOf;
46
+ var bigIntValueOf = BigInt.prototype.valueOf;
47
+ var functionToString = Function.prototype.toString;
44
48
  var PROBE_KEY = {};
49
+ var INTRINSIC_CONSTRUCTOR_NAMES = [
50
+ "Object",
51
+ "Array",
52
+ "Date",
53
+ "RegExp",
54
+ "Map",
55
+ "Set",
56
+ "WeakMap",
57
+ "WeakSet",
58
+ "Promise",
59
+ "Error",
60
+ "EvalError",
61
+ "RangeError",
62
+ "ReferenceError",
63
+ "SyntaxError",
64
+ "TypeError",
65
+ "URIError",
66
+ "AggregateError",
67
+ "Boolean",
68
+ "Number",
69
+ "String",
70
+ "BigInt",
71
+ "ArrayBuffer",
72
+ "SharedArrayBuffer",
73
+ "DataView",
74
+ "Int8Array",
75
+ "Uint8Array",
76
+ "Uint8ClampedArray",
77
+ "Int16Array",
78
+ "Uint16Array",
79
+ "Int32Array",
80
+ "Uint32Array",
81
+ "Float32Array",
82
+ "Float64Array",
83
+ "BigInt64Array",
84
+ "BigUint64Array"
85
+ ];
86
+ var intrinsicConstructorSources = new Map(
87
+ INTRINSIC_CONSTRUCTOR_NAMES.flatMap((name) => {
88
+ const descriptor = Object.getOwnPropertyDescriptor(globalThis, name);
89
+ const intrinsic = descriptor?.value;
90
+ return typeof intrinsic === "function" ? [[name, functionToString.call(intrinsic)]] : [];
91
+ })
92
+ );
93
+ var intrinsicConstructorSourceSet = new Set(
94
+ intrinsicConstructorSources.values()
95
+ );
96
+ var ERROR_INTRINSIC_NAMES = [
97
+ "Error",
98
+ "EvalError",
99
+ "RangeError",
100
+ "ReferenceError",
101
+ "SyntaxError",
102
+ "TypeError",
103
+ "URIError",
104
+ "AggregateError"
105
+ ];
106
+ function isIntrinsicConstructorPrototype(prototype, expectedName) {
107
+ const constructorDescriptor = Object.getOwnPropertyDescriptor(
108
+ prototype,
109
+ "constructor"
110
+ );
111
+ const candidateConstructor = constructorDescriptor?.value;
112
+ if (constructorDescriptor === void 0 || !("value" in constructorDescriptor) || typeof candidateConstructor !== "function") {
113
+ return false;
114
+ }
115
+ let candidateSource;
116
+ try {
117
+ candidateSource = functionToString.call(candidateConstructor);
118
+ } catch {
119
+ return false;
120
+ }
121
+ const expectedSource = expectedName === void 0 ? void 0 : intrinsicConstructorSources.get(expectedName);
122
+ if (expectedSource !== void 0 && candidateSource !== expectedSource || expectedSource === void 0 && !intrinsicConstructorSourceSet.has(candidateSource)) {
123
+ return false;
124
+ }
125
+ const nameDescriptor = Object.getOwnPropertyDescriptor(
126
+ candidateConstructor,
127
+ "name"
128
+ );
129
+ const candidateName = nameDescriptor !== void 0 && "value" in nameDescriptor && typeof nameDescriptor.value === "string" ? nameDescriptor.value : void 0;
130
+ const intrinsicName = expectedName ?? candidateName;
131
+ const intrinsicSource = intrinsicName === void 0 ? void 0 : intrinsicConstructorSources.get(intrinsicName);
132
+ return candidateName === intrinsicName && intrinsicSource !== void 0 && candidateSource === intrinsicSource && Object.getOwnPropertyDescriptor(candidateConstructor, "prototype")?.value === prototype;
133
+ }
134
+ __name(isIntrinsicConstructorPrototype, "isIntrinsicConstructorPrototype");
135
+ function hasIntrinsicPrototypeChain(value, expectedName) {
136
+ const visited = /* @__PURE__ */ new WeakSet();
137
+ let prototype = Object.getPrototypeOf(value);
138
+ while (prototype !== null && !visited.has(prototype)) {
139
+ visited.add(prototype);
140
+ if (Object.hasOwn(prototype, "constructor")) {
141
+ return isIntrinsicConstructorPrototype(prototype, expectedName);
142
+ }
143
+ const ownKeys2 = Reflect.ownKeys(prototype);
144
+ if (ownKeys2.length !== 1 || ownKeys2[0] !== Symbol.toStringTag) {
145
+ return false;
146
+ }
147
+ prototype = Object.getPrototypeOf(prototype);
148
+ }
149
+ return false;
150
+ }
151
+ __name(hasIntrinsicPrototypeChain, "hasIntrinsicPrototypeChain");
45
152
  var REFERENCE_COMPARED_TAGS = /* @__PURE__ */ new Set([
46
153
  "[object Error]",
47
154
  "[object ArrayBuffer]",
@@ -50,6 +157,72 @@ var REFERENCE_COMPARED_TAGS = /* @__PURE__ */ new Set([
50
157
  "[object WeakMap]",
51
158
  "[object WeakSet]"
52
159
  ]);
160
+ function findPropertyDescriptor(value, key) {
161
+ const visited = /* @__PURE__ */ new WeakSet();
162
+ let current = value;
163
+ while (current !== null && !visited.has(current)) {
164
+ visited.add(current);
165
+ const descriptor = Object.getOwnPropertyDescriptor(current, key);
166
+ if (descriptor !== void 0) return descriptor;
167
+ current = Object.getPrototypeOf(current);
168
+ }
169
+ return void 0;
170
+ }
171
+ __name(findPropertyDescriptor, "findPropertyDescriptor");
172
+ function builtInTagWithoutInvokingAccessors(value) {
173
+ if (ArrayBuffer.isView(value)) {
174
+ return hasBrand(value, "[object DataView]") ? "[object DataView]" : "[object TypedArray]";
175
+ }
176
+ const descriptor = findPropertyDescriptor(value, Symbol.toStringTag);
177
+ if (descriptor !== void 0 && !("value" in descriptor)) {
178
+ return builtInTagFromBrand(value);
179
+ }
180
+ const tag = Object.prototype.toString.call(value);
181
+ if (BUILT_IN_TAGS.has(tag) && hasBrand(value, tag)) {
182
+ return tag;
183
+ }
184
+ return descriptor === void 0 ? void 0 : builtInTagFromBrand(value);
185
+ }
186
+ __name(builtInTagWithoutInvokingAccessors, "builtInTagWithoutInvokingAccessors");
187
+ function mutableBuiltInTagWithoutInvokingAccessors(value) {
188
+ const tag = builtInTagWithoutInvokingAccessors(value);
189
+ return tag === "[object Date]" || tag === "[object Map]" || tag === "[object Set]" ? tag : void 0;
190
+ }
191
+ __name(mutableBuiltInTagWithoutInvokingAccessors, "mutableBuiltInTagWithoutInvokingAccessors");
192
+ function builtInTagFromBrand(value) {
193
+ if (hasBrand(value, "[object Date]")) return "[object Date]";
194
+ if (hasBrand(value, "[object RegExp]")) return "[object RegExp]";
195
+ if (hasBrand(value, "[object Map]")) return "[object Map]";
196
+ if (hasBrand(value, "[object Set]")) return "[object Set]";
197
+ if (hasBrand(value, "[object WeakMap]")) return "[object WeakMap]";
198
+ if (hasBrand(value, "[object WeakSet]")) return "[object WeakSet]";
199
+ if (hasBrand(value, "[object DataView]")) return "[object DataView]";
200
+ if (hasBrand(value, "[object ArrayBuffer]")) return "[object ArrayBuffer]";
201
+ if (hasBrand(value, "[object SharedArrayBuffer]")) {
202
+ return "[object SharedArrayBuffer]";
203
+ }
204
+ if (hasBrand(value, "[object Boolean]")) return "[object Boolean]";
205
+ if (hasBrand(value, "[object Number]")) return "[object Number]";
206
+ if (hasBrand(value, "[object String]")) return "[object String]";
207
+ if (hasBrand(value, "[object BigInt]")) return "[object BigInt]";
208
+ if (hasNativePrototype(value, "Promise")) return "[object Promise]";
209
+ if (hasNativePrototype(value, "Error")) return "[object Error]";
210
+ return void 0;
211
+ }
212
+ __name(builtInTagFromBrand, "builtInTagFromBrand");
213
+ function hasNativePrototype(value, expectedName) {
214
+ const visited = /* @__PURE__ */ new WeakSet();
215
+ let prototype = Object.getPrototypeOf(value);
216
+ while (prototype !== null && !visited.has(prototype)) {
217
+ visited.add(prototype);
218
+ if (isIntrinsicConstructorPrototype(prototype, expectedName)) {
219
+ return true;
220
+ }
221
+ prototype = Object.getPrototypeOf(prototype);
222
+ }
223
+ return false;
224
+ }
225
+ __name(hasNativePrototype, "hasNativePrototype");
53
226
  function hasBrand(obj, tag) {
54
227
  try {
55
228
  switch (tag) {
@@ -77,6 +250,10 @@ function hasBrand(obj, tag) {
77
250
  case "[object ArrayBuffer]":
78
251
  arrayBufferByteLengthGet.call(obj);
79
252
  return true;
253
+ case "[object SharedArrayBuffer]":
254
+ if (!sharedArrayBufferByteLengthGet) return false;
255
+ sharedArrayBufferByteLengthGet.call(obj);
256
+ return true;
80
257
  case "[object Boolean]":
81
258
  booleanValueOf.call(obj);
82
259
  return true;
@@ -86,8 +263,17 @@ function hasBrand(obj, tag) {
86
263
  case "[object String]":
87
264
  stringValueOf.call(obj);
88
265
  return true;
89
- default:
266
+ case "[object BigInt]":
267
+ bigIntValueOf.call(obj);
90
268
  return true;
269
+ case "[object Promise]":
270
+ return hasNativePrototype(obj, "Promise");
271
+ case "[object Error]":
272
+ return ERROR_INTRINSIC_NAMES.some(
273
+ (name) => hasNativePrototype(obj, name)
274
+ );
275
+ default:
276
+ return false;
91
277
  }
92
278
  } catch {
93
279
  return false;
@@ -160,12 +346,17 @@ function deepEqualInner(a, b, visited) {
160
346
  }
161
347
  if (Array.isArray(objA) || Array.isArray(objB)) {
162
348
  if (!Array.isArray(objA) || !Array.isArray(objB)) return false;
349
+ if (objA.length !== objB.length) return false;
350
+ const keysA = Reflect.ownKeys(objA).filter((key) => key !== "length");
351
+ const keysB = Reflect.ownKeys(objB).filter((key) => key !== "length");
352
+ if (keysA.length !== keysB.length) return false;
163
353
  const arrA = objA;
164
354
  const arrB = objB;
165
- const len = arrA.length;
166
- if (len !== arrB.length) return false;
167
- for (let i = 0; i < len; i++) {
168
- if (!deepEqualInner(arrA[i], arrB[i], visited)) return false;
355
+ for (const key of keysA) {
356
+ if (!objHasOwn.call(objB, key)) return false;
357
+ if (!deepEqualInner(arrA[key], arrB[key], visited)) {
358
+ return false;
359
+ }
169
360
  }
170
361
  return true;
171
362
  }
@@ -209,7 +400,8 @@ function deepEqualInner(a, b, visited) {
209
400
  }
210
401
  case "[object Boolean]":
211
402
  case "[object Number]":
212
- case "[object String]": {
403
+ case "[object String]":
404
+ case "[object BigInt]": {
213
405
  return sameValueZero(
214
406
  objA.valueOf(),
215
407
  objB.valueOf()
@@ -224,8 +416,8 @@ __name(deepEqualInner, "deepEqualInner");
224
416
  function comparePlainObjects(objA, objB, visited) {
225
417
  const recA = objA;
226
418
  const recB = objB;
227
- const stringKeysA = Object.keys(objA);
228
- const stringKeysB = Object.keys(objB);
419
+ const stringKeysA = Object.getOwnPropertyNames(objA);
420
+ const stringKeysB = Object.getOwnPropertyNames(objB);
229
421
  if (stringKeysA.length !== stringKeysB.length) return false;
230
422
  const symbolKeysA = Object.getOwnPropertySymbols(objA);
231
423
  const symbolKeysB = Object.getOwnPropertySymbols(objB);
@@ -275,19 +467,33 @@ function omitInternal(value, options, ignoreKeys, path, visited, budget) {
275
467
  if (Array.isArray(obj)) {
276
468
  const arr = obj;
277
469
  const clone2 = new Array(arr.length);
470
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(arr, "length");
278
471
  visited.set(obj, clone2);
279
- for (let i = 0; i < arr.length; i++) {
280
- path.push(i);
281
- clone2[i] = omitInternal(
282
- arr[i],
283
- options,
284
- ignoreKeys,
285
- path,
286
- visited,
287
- budget
288
- );
472
+ for (const key of Reflect.ownKeys(arr)) {
473
+ if (key === "length") continue;
474
+ const segment = arrayPathSegment(key);
475
+ if (typeof segment !== "number" && shouldIgnoreKey(key, path, ignoreKeys, options)) {
476
+ continue;
477
+ }
478
+ const descriptor = Object.getOwnPropertyDescriptor(arr, key);
479
+ if (descriptor === void 0) continue;
480
+ path.push(segment);
481
+ if ("value" in descriptor) {
482
+ descriptor.value = omitInternal(
483
+ descriptor.value,
484
+ options,
485
+ ignoreKeys,
486
+ path,
487
+ visited,
488
+ budget
489
+ );
490
+ }
491
+ Object.defineProperty(clone2, key, descriptor);
289
492
  path.pop();
290
493
  }
494
+ if (lengthDescriptor !== void 0) {
495
+ Object.defineProperty(clone2, "length", lengthDescriptor);
496
+ }
291
497
  if (budget) visited.delete(obj);
292
498
  return clone2;
293
499
  }
@@ -339,6 +545,12 @@ function omitInternal(value, options, ignoreKeys, path, visited, budget) {
339
545
  return clone;
340
546
  }
341
547
  __name(omitInternal, "omitInternal");
548
+ function arrayPathSegment(key) {
549
+ if (typeof key === "symbol") return key;
550
+ const index = Number(key);
551
+ return Number.isInteger(index) && index >= 0 && index < 4294967295 && String(index) === key ? index : key;
552
+ }
553
+ __name(arrayPathSegment, "arrayPathSegment");
342
554
  function assignOwn(target, key, value) {
343
555
  Object.defineProperty(target, key, {
344
556
  value,
@@ -405,6 +617,8 @@ var DATE_MUTATORS = [
405
617
  "setYear"
406
618
  ];
407
619
  var mutationThrowers = /* @__PURE__ */ new Map();
620
+ var mapEntries = Map.prototype.entries;
621
+ var setValues = Set.prototype.values;
408
622
  function mutationThrower(typeName, method) {
409
623
  const key = `${typeName}.${method}`;
410
624
  let thrower = mutationThrowers.get(key);
@@ -444,17 +658,19 @@ function deepFreeze(obj, visited = /* @__PURE__ */ new WeakSet()) {
444
658
  return obj;
445
659
  }
446
660
  visited.add(obj);
447
- const tag = Object.prototype.toString.call(obj);
448
- if (isBuiltInObject(obj, tag)) {
449
- if (tag === "[object Date]") {
661
+ const mutableBuiltInTag = mutableBuiltInTagWithoutInvokingAccessors(
662
+ obj
663
+ );
664
+ if (mutableBuiltInTag !== void 0) {
665
+ if (mutableBuiltInTag === "[object Date]") {
450
666
  shadowMutators(obj, "Date", DATE_MUTATORS);
451
- } else if (tag === "[object Map]") {
667
+ } else if (mutableBuiltInTag === "[object Map]") {
452
668
  for (const [key, value] of obj) {
453
669
  deepFreeze(key, visited);
454
670
  deepFreeze(value, visited);
455
671
  }
456
672
  shadowMutators(obj, "Map", ["set", "delete", "clear"]);
457
- } else if (tag === "[object Set]") {
673
+ } else if (mutableBuiltInTag === "[object Set]") {
458
674
  for (const member of obj) {
459
675
  deepFreeze(member, visited);
460
676
  }
@@ -481,32 +697,63 @@ function cloneForVo(value, visited) {
481
697
  return value;
482
698
  }
483
699
  const obj = value;
700
+ if (ArrayBuffer.isView(obj)) {
701
+ throwUnsupportedValueSemantics(
702
+ builtInTagWithoutInvokingAccessors(obj) ?? "[object ArrayBuffer view]"
703
+ );
704
+ }
484
705
  if (visited.has(obj)) {
485
706
  return visited.get(obj);
486
707
  }
487
708
  if (Array.isArray(obj)) {
709
+ if (!hasIntrinsicPrototypeChain(obj, "Array")) {
710
+ throwUnsupportedClassInstance();
711
+ }
488
712
  const clone2 = new Array(obj.length);
489
713
  visited.set(obj, clone2);
490
- for (let i = 0; i < obj.length; i++) {
491
- clone2[i] = cloneForVo(obj[i], visited);
714
+ for (const key of Reflect.ownKeys(obj)) {
715
+ if (key === "length") continue;
716
+ const descriptor = Object.getOwnPropertyDescriptor(obj, key);
717
+ if (descriptor === void 0) continue;
718
+ if (!("value" in descriptor)) {
719
+ throwUnsupportedAccessorProperty();
720
+ }
721
+ if (typeof key === "string" && !descriptor.enumerable) continue;
722
+ descriptor.value = cloneForVo(descriptor.value, visited);
723
+ Object.defineProperty(clone2, key, descriptor);
492
724
  }
493
725
  return clone2;
494
726
  }
495
- const tag = Object.prototype.toString.call(obj);
496
- if (isBuiltInObject(obj, tag)) {
727
+ const tag = builtInTagWithoutInvokingAccessors(obj);
728
+ if (tag !== void 0) {
729
+ if (!hasIntrinsicPrototypeChain(obj)) {
730
+ throwUnsupportedClassInstance();
731
+ }
497
732
  if (tag === "[object Map]") {
498
733
  const clone2 = /* @__PURE__ */ new Map();
499
734
  visited.set(obj, clone2);
500
- for (const [key, entry] of obj) {
501
- clone2.set(cloneForVo(key, visited), cloneForVo(entry, visited));
735
+ for (const [key, entry] of mapEntries.call(
736
+ obj
737
+ )) {
738
+ if (!isPrimitiveValue(key)) {
739
+ throw new TypeError(
740
+ "vo() Map keys must be primitive values to preserve value equality"
741
+ );
742
+ }
743
+ clone2.set(key, cloneForVo(entry, visited));
502
744
  }
503
745
  return clone2;
504
746
  }
505
747
  if (tag === "[object Set]") {
506
748
  const clone2 = /* @__PURE__ */ new Set();
507
749
  visited.set(obj, clone2);
508
- for (const member of obj) {
509
- clone2.add(cloneForVo(member, visited));
750
+ for (const member of setValues.call(obj)) {
751
+ if (!isPrimitiveValue(member)) {
752
+ throw new TypeError(
753
+ "vo() Set members must be primitive values to preserve value equality"
754
+ );
755
+ }
756
+ clone2.add(member);
510
757
  }
511
758
  return clone2;
512
759
  }
@@ -515,28 +762,67 @@ function cloneForVo(value, visited) {
515
762
  `vo() cannot clone a ${tag.slice(8, -1)}: Value Objects are plain data`
516
763
  );
517
764
  }
765
+ if (tag === "[object Error]" || tag === "[object ArrayBuffer]" || tag === "[object SharedArrayBuffer]") {
766
+ throwUnsupportedValueSemantics(tag);
767
+ }
768
+ if (tag === "[object RegExp]") {
769
+ const regExp = obj;
770
+ if (regExp.global || regExp.sticky) {
771
+ throw new TypeError(
772
+ "vo() cannot accept a global or sticky RegExp: its lastIndex is mutable scan state, not an immutable value"
773
+ );
774
+ }
775
+ }
518
776
  const builtInClone = structuredClone(obj);
519
777
  visited.set(obj, builtInClone);
520
778
  return builtInClone;
521
779
  }
522
- const clone = Object.create(Object.getPrototypeOf(obj));
780
+ const prototype = Object.getPrototypeOf(obj);
781
+ if (prototype !== null && (!isIntrinsicConstructorPrototype(prototype, "Object") || Object.getPrototypeOf(prototype) !== null)) {
782
+ throwUnsupportedClassInstance();
783
+ }
784
+ const clone = Object.create(prototype === null ? null : Object.prototype);
523
785
  visited.set(obj, clone);
524
786
  for (const key of Reflect.ownKeys(obj)) {
525
787
  const descriptor = Object.getOwnPropertyDescriptor(obj, key);
526
- if (!descriptor?.enumerable) continue;
788
+ if (descriptor === void 0) continue;
789
+ if (!("value" in descriptor)) {
790
+ throwUnsupportedAccessorProperty();
791
+ }
792
+ if (typeof key === "string" && !descriptor.enumerable) continue;
527
793
  Object.defineProperty(clone, key, {
528
- value: cloneForVo(
529
- obj[key],
530
- visited
531
- ),
794
+ value: cloneForVo(descriptor.value, visited),
532
795
  writable: true,
533
- enumerable: true,
796
+ enumerable: descriptor.enumerable,
534
797
  configurable: true
535
798
  });
536
799
  }
537
800
  return clone;
538
801
  }
539
802
  __name(cloneForVo, "cloneForVo");
803
+ function throwUnsupportedClassInstance() {
804
+ throw new TypeError(
805
+ "vo() cannot clone custom class instances: Value Objects are plain data"
806
+ );
807
+ }
808
+ __name(throwUnsupportedClassInstance, "throwUnsupportedClassInstance");
809
+ function throwUnsupportedAccessorProperty() {
810
+ throw new TypeError(
811
+ "vo() cannot clone accessor properties: Value Objects are plain data"
812
+ );
813
+ }
814
+ __name(throwUnsupportedAccessorProperty, "throwUnsupportedAccessorProperty");
815
+ function throwUnsupportedValueSemantics(tag) {
816
+ const name = tag.startsWith("[object ") ? tag.slice(8, -1) : tag;
817
+ throw new TypeError(
818
+ `vo() cannot accept ${name} values: Value Objects require immutable value semantics`
819
+ );
820
+ }
821
+ __name(throwUnsupportedValueSemantics, "throwUnsupportedValueSemantics");
822
+ function isPrimitiveValue(value) {
823
+ return value === null || typeof value !== "object" && typeof value !== "function";
824
+ }
825
+ __name(isPrimitiveValue, "isPrimitiveValue");
540
826
  function vo(t) {
541
827
  return deepFreeze(cloneForVo(t, /* @__PURE__ */ new WeakMap()));
542
828
  }
@@ -574,7 +860,7 @@ var ValueObject = class {
574
860
  props;
575
861
  /**
576
862
  * Creates a new ValueObject.
577
- * The properties are deep-cloned (prototype-preserving) and then deeply
863
+ * The plain-data properties are deep-cloned and then deeply
578
864
  * frozen, so the caller's own object graph is never frozen or mutated,
579
865
  * and later mutation of the input does not bleed into the value object.
580
866
  *
@@ -629,7 +915,19 @@ var ValueObject = class {
629
915
  */
630
916
  clone(props) {
631
917
  const Constructor = this.constructor;
632
- return new Constructor({ ...this.props, ...props || {} });
918
+ const merged = { ...this.props, ...props || {} };
919
+ for (const key of Reflect.ownKeys(this.props)) {
920
+ const descriptor = Object.getOwnPropertyDescriptor(this.props, key);
921
+ if (descriptor === void 0 || descriptor.enumerable) continue;
922
+ if (props && Object.hasOwn(props, key)) continue;
923
+ Object.defineProperty(merged, key, {
924
+ value: this.props[key],
925
+ writable: true,
926
+ enumerable: false,
927
+ configurable: true
928
+ });
929
+ }
930
+ return new Constructor(merged);
633
931
  }
634
932
  /**
635
933
  * Serializes the value object to its raw properties for JSON operations.
@@ -641,13 +939,33 @@ var ValueObject = class {
641
939
  }
642
940
  };
643
941
 
644
- // src/aggregate/domain-event.ts
645
- var defaultEventIdFactory = /* @__PURE__ */ __name(() => crypto.randomUUID(), "defaultEventIdFactory");
646
- var currentEventIdFactory = defaultEventIdFactory;
647
- function setEventIdFactory(factory) {
648
- currentEventIdFactory = factory;
942
+ // src/aggregate/clock.ts
943
+ var defaultClockFactory = /* @__PURE__ */ __name(() => /* @__PURE__ */ new Date(), "defaultClockFactory");
944
+ var currentClockFactory = defaultClockFactory;
945
+ function now() {
946
+ return currentClockFactory();
649
947
  }
650
- __name(setEventIdFactory, "setEventIdFactory");
948
+ __name(now, "now");
949
+ function setClockFactory(factory) {
950
+ currentClockFactory = factory;
951
+ }
952
+ __name(setClockFactory, "setClockFactory");
953
+ function withClockFactory(factory, fn) {
954
+ const previous = currentClockFactory;
955
+ currentClockFactory = factory;
956
+ try {
957
+ const result = fn();
958
+ assertNotThenable(result, "withClockFactory");
959
+ return result;
960
+ } finally {
961
+ currentClockFactory = previous;
962
+ }
963
+ }
964
+ __name(withClockFactory, "withClockFactory");
965
+ function resetClockFactory() {
966
+ currentClockFactory = defaultClockFactory;
967
+ }
968
+ __name(resetClockFactory, "resetClockFactory");
651
969
  function assertNotThenable(result, helperName) {
652
970
  if (result !== null && (typeof result === "object" || typeof result === "function") && typeof result.then === "function") {
653
971
  throw new Error(
@@ -656,6 +974,14 @@ function assertNotThenable(result, helperName) {
656
974
  }
657
975
  }
658
976
  __name(assertNotThenable, "assertNotThenable");
977
+
978
+ // src/aggregate/domain-event.ts
979
+ var defaultEventIdFactory = /* @__PURE__ */ __name(() => crypto.randomUUID(), "defaultEventIdFactory");
980
+ var currentEventIdFactory = defaultEventIdFactory;
981
+ function setEventIdFactory(factory) {
982
+ currentEventIdFactory = factory;
983
+ }
984
+ __name(setEventIdFactory, "setEventIdFactory");
659
985
  function withEventIdFactory(factory, fn) {
660
986
  const previous = currentEventIdFactory;
661
987
  currentEventIdFactory = factory;
@@ -672,45 +998,46 @@ function resetEventIdFactory() {
672
998
  currentEventIdFactory = defaultEventIdFactory;
673
999
  }
674
1000
  __name(resetEventIdFactory, "resetEventIdFactory");
675
- var defaultClockFactory = /* @__PURE__ */ __name(() => /* @__PURE__ */ new Date(), "defaultClockFactory");
676
- var currentClockFactory = defaultClockFactory;
677
- function setClockFactory(factory) {
678
- currentClockFactory = factory;
679
- }
680
- __name(setClockFactory, "setClockFactory");
681
- function withClockFactory(factory, fn) {
682
- const previous = currentClockFactory;
683
- currentClockFactory = factory;
684
- try {
685
- const result = fn();
686
- assertNotThenable(result, "withClockFactory");
687
- return result;
688
- } finally {
689
- currentClockFactory = previous;
690
- }
691
- }
692
- __name(withClockFactory, "withClockFactory");
693
- function resetClockFactory() {
694
- currentClockFactory = defaultClockFactory;
695
- }
696
- __name(resetClockFactory, "resetClockFactory");
697
1001
  function createDomainEvent(type, payload, options) {
698
1002
  const event = {
699
1003
  eventId: options?.eventId ?? currentEventIdFactory(),
700
1004
  type,
701
1005
  aggregateId: options?.aggregateId,
702
1006
  aggregateType: options?.aggregateType,
703
- payload,
704
- // Defensive copy: the event must not share the caller's live Date
705
- // instance, or a later mutation of it would bleed into the event.
706
- occurredAt: options?.occurredAt ? new Date(options.occurredAt.getTime()) : currentClockFactory(),
1007
+ // Defensive copies throughout: the deep-freeze below must never
1008
+ // reach the caller's own object graph. Without the clone, passing
1009
+ // (parts of) live aggregate state as payload, or reusing a metadata
1010
+ // object across events, would freeze the caller's objects in place;
1011
+ // the next mutation then throws far away from the cause. Same
1012
+ // ownership contract as `vo()` and the occurredAt copy.
1013
+ payload: cloneOwnedEventData(payload, "payload"),
1014
+ occurredAt: options?.occurredAt ? new Date(options.occurredAt.getTime()) : now(),
707
1015
  version: options?.version ?? 1,
708
1016
  aggregateVersion: options?.aggregateVersion,
709
- metadata: options?.metadata
1017
+ metadata: cloneOwnedEventData(options?.metadata, "metadata")
710
1018
  };
711
1019
  return deepFreeze(event);
712
1020
  }
713
1021
  __name(createDomainEvent, "createDomainEvent");
1022
+ function cloneOwnedEventData(value, field) {
1023
+ if (typeof value === "function") {
1024
+ throw new TypeError(
1025
+ `createDomainEvent: ${field} must not be a function: domain events are plain data`
1026
+ );
1027
+ }
1028
+ if (value === null || typeof value !== "object") {
1029
+ return value;
1030
+ }
1031
+ try {
1032
+ return structuredClone(value);
1033
+ } catch (cause) {
1034
+ throw new TypeError(
1035
+ `createDomainEvent: ${field} must be plain, structured-cloneable data (no functions, Promises, or WeakMap/WeakSet values): domain events are plain data`,
1036
+ { cause }
1037
+ );
1038
+ }
1039
+ }
1040
+ __name(cloneOwnedEventData, "cloneOwnedEventData");
714
1041
  function copyMetadata(sourceEvent, additionalMetadata) {
715
1042
  return {
716
1043
  ...sourceEvent.metadata ?? {},
@@ -742,93 +1069,275 @@ function sameVersion(a, b) {
742
1069
  return a.id === b.id && a.version === b.version;
743
1070
  }
744
1071
  __name(sameVersion, "sameVersion");
745
-
746
- // src/entity/entity.ts
747
- var Entity = class {
1072
+ var DomainError = class extends BaseError {
748
1073
  static {
749
- __name(this, "Entity");
1074
+ __name(this, "DomainError");
750
1075
  }
751
- id;
752
- /**
753
- * Returns the current state of the entity.
754
- *
755
- * The state object is **shallowly frozen**: direct property writes
756
- * (`entity.state.foo = …`) throw in strict mode, but writes to nested
757
- * objects (`entity.state.address.zip = …`) bypass the freeze. For deep
758
- * immutability either model nested data with `vo()` (which freezes
759
- * deeply) or reach for a structural-sharing library like Immer at the
760
- * App layer. The shallow contract is intentional: deep freezing on
761
- * every state write is too expensive for hot paths, and DDD aggregates
762
- * normally treat their own state as private (`Tell, Don't Ask`).
763
- */
764
- get state() {
765
- return this._state;
1076
+ };
1077
+ var InfrastructureError = class extends BaseError {
1078
+ static {
1079
+ __name(this, "InfrastructureError");
766
1080
  }
767
- /**
768
- * The state is 'protected' so that only the subclass can modify it.
769
- * Subclasses can mutate this directly or use helper methods.
770
- */
771
- _state;
772
- /**
773
- * **State ownership.** Plain-object and array states are shallow-copied
774
- * before the freeze, so the caller's own object stays mutable. A CLASS
775
- * INSTANCE passed as state is an ownership transfer: it is frozen
776
- * in place (a copy would strip its prototype). Do not keep mutating
777
- * the instance after handing it to the entity. The same contract
778
- * applies to {@link setState}.
779
- */
780
- constructor(id, initialState) {
781
- if (id === null || id === void 0) {
782
- throw new Error("Entity ID cannot be null or undefined");
783
- }
784
- this.id = id;
785
- this._state = freezeShallow(shallowCopyOwned(initialState));
786
- this.validateState(this._state);
1081
+ };
1082
+ var MissingHandlerError = class extends BaseError {
1083
+ constructor(eventType, cause) {
1084
+ super(`Missing handler for event type: ${eventType}`, cause, {
1085
+ name: "MissingHandlerError"
1086
+ });
1087
+ this.eventType = eventType;
787
1088
  }
788
- /**
789
- * Optional validation hook to ensure state invariants. Called during
790
- * construction (from `Entity`'s constructor) and again on every
791
- * `setState()` call. Throw to reject invalid state.
792
- *
793
- * **⚠️ Must not read subclass instance fields via `this`.** The
794
- * constructor calls `validateState(initialState)` BEFORE the subclass's
795
- * field initializers run, so `this.someField` is `undefined` at that
796
- * point, a classic TypeScript/JavaScript constructor-ordering footgun.
797
- * The `state` argument is the single source of truth; treat the method
798
- * as pure with respect to `this`.
799
- *
800
- * If your invariants genuinely depend on per-instance configuration
801
- * that isn't part of the state, pass that configuration into the state
802
- * itself (DDD-canonical: the aggregate's state contains everything it
803
- * needs) or perform the additional check after construction in a
804
- * dedicated factory method.
805
- *
806
- * @param state - The state to validate
807
- * @throws Error (or `DomainError` subclass) if validation fails
808
- */
809
- validateState(_state) {
1089
+ static {
1090
+ __name(this, "MissingHandlerError");
810
1091
  }
811
- /**
812
- * Sets the state of the entity.
813
- * This is a convenience method for state mutations.
814
- * Automatically validates the newState using `validateState()`.
815
- *
816
- * Plain-object and array states are shallow-copied before the freeze
817
- * (the caller's object stays mutable); a class-instance state is an
818
- * ownership transfer and is frozen in place; see the constructor.
819
- *
820
- * @param newState - The new state
821
- */
822
- setState(newState) {
823
- this.validateState(newState);
824
- this._state = freezeShallow(shallowCopyOwned(newState));
1092
+ };
1093
+ var UnreplayableAggregateError = class extends BaseError {
1094
+ constructor(aggregateId, reason) {
1095
+ super(
1096
+ `Cannot replay history onto aggregate ${aggregateId}: ${reason}. Reconstitute on a fresh instance (no factory-recorded events, no unpersisted mutations), or markPersisted the aggregate before a catch-up replay.`,
1097
+ void 0,
1098
+ { name: "UnreplayableAggregateError" }
1099
+ );
1100
+ this.aggregateId = aggregateId;
1101
+ }
1102
+ static {
1103
+ __name(this, "UnreplayableAggregateError");
825
1104
  }
826
1105
  };
827
- function freezeShallow(value) {
828
- if (value !== null && typeof value === "object") {
829
- return Object.freeze(value);
1106
+ var EventHarvestError = class extends BaseError {
1107
+ constructor(message, eventType) {
1108
+ super(message, void 0, { name: "EventHarvestError" });
1109
+ this.eventType = eventType;
830
1110
  }
831
- return value;
1111
+ static {
1112
+ __name(this, "EventHarvestError");
1113
+ }
1114
+ };
1115
+ var UnregisteredHandlerError = class extends BaseError {
1116
+ static {
1117
+ __name(this, "UnregisteredHandlerError");
1118
+ }
1119
+ busKind;
1120
+ messageType;
1121
+ constructor(options) {
1122
+ super(
1123
+ `No handler registered for ${options.busKind} type: ${options.messageType}`,
1124
+ void 0,
1125
+ { name: "UnregisteredHandlerError" }
1126
+ );
1127
+ this.busKind = options.busKind;
1128
+ this.messageType = options.messageType;
1129
+ }
1130
+ };
1131
+ var UnenrolledChangesError = class extends BaseError {
1132
+ constructor(aggregateId) {
1133
+ super(
1134
+ `Aggregate ${aggregateId} was loaded in this unit of work and has pending events, but was never enrolled (no save), so its events would be silently dropped. Call repository.save(aggregate), and ensure save() calls session.enrollSaved before the row write.`,
1135
+ void 0,
1136
+ { name: "UnenrolledChangesError" }
1137
+ );
1138
+ this.aggregateId = aggregateId;
1139
+ }
1140
+ static {
1141
+ __name(this, "UnenrolledChangesError");
1142
+ }
1143
+ };
1144
+ var AggregateDeletedError = class extends BaseError {
1145
+ constructor(aggregateId) {
1146
+ super(
1147
+ `Aggregate ${aggregateId} was deleted in this unit of work and cannot be saved or registered again. Deletion is final within an operation; if the aggregate must live, do not delete it.`,
1148
+ void 0,
1149
+ { name: "AggregateDeletedError" }
1150
+ );
1151
+ this.aggregateId = aggregateId;
1152
+ }
1153
+ static {
1154
+ __name(this, "AggregateDeletedError");
1155
+ }
1156
+ };
1157
+ var AggregateNotFoundError = class extends InfrastructureError {
1158
+ static {
1159
+ __name(this, "AggregateNotFoundError");
1160
+ }
1161
+ aggregateType;
1162
+ id;
1163
+ constructor(options) {
1164
+ super(
1165
+ `Aggregate not found: ${options.aggregateType}(${options.id})`,
1166
+ options.cause,
1167
+ { name: "AggregateNotFoundError" }
1168
+ );
1169
+ this.aggregateType = options.aggregateType;
1170
+ this.id = options.id;
1171
+ }
1172
+ };
1173
+ var DuplicateAggregateError = class extends InfrastructureError {
1174
+ static {
1175
+ __name(this, "DuplicateAggregateError");
1176
+ }
1177
+ aggregateType;
1178
+ aggregateId;
1179
+ constructor(options) {
1180
+ super(
1181
+ `Duplicate aggregate: ${options.aggregateType}(${options.aggregateId}) already exists`,
1182
+ options.cause,
1183
+ { name: "DuplicateAggregateError" }
1184
+ );
1185
+ this.aggregateType = options.aggregateType;
1186
+ this.aggregateId = options.aggregateId;
1187
+ }
1188
+ };
1189
+ var SnapshotSchemaMismatchError = class extends InfrastructureError {
1190
+ static {
1191
+ __name(this, "SnapshotSchemaMismatchError");
1192
+ }
1193
+ aggregateType;
1194
+ aggregateId;
1195
+ expectedSchemaVersion;
1196
+ actualSchemaVersion;
1197
+ constructor(options) {
1198
+ super(
1199
+ `Snapshot schema mismatch on ${options.aggregateType}(${options.aggregateId}): the aggregate expects snapshot schema ${options.expectedSchemaVersion}, the stored snapshot carries ${options.actualSchemaVersion}. Override migrateSnapshotState to upgrade old snapshots, or discard the snapshot and refold from the full event stream.`,
1200
+ void 0,
1201
+ { name: "SnapshotSchemaMismatchError" }
1202
+ );
1203
+ this.aggregateType = options.aggregateType;
1204
+ this.aggregateId = options.aggregateId;
1205
+ this.expectedSchemaVersion = options.expectedSchemaVersion;
1206
+ this.actualSchemaVersion = options.actualSchemaVersion;
1207
+ }
1208
+ };
1209
+ var ConcurrencyConflictError = class extends InfrastructureError {
1210
+ static {
1211
+ __name(this, "ConcurrencyConflictError");
1212
+ }
1213
+ /**
1214
+ * Marks this error as retryable so `isRetryable(err)` returns
1215
+ * true. The canonical OCC pattern is to reload the aggregate, re-apply
1216
+ * the use case, and retry on this exception.
1217
+ */
1218
+ retryable = true;
1219
+ aggregateType;
1220
+ aggregateId;
1221
+ expectedVersion;
1222
+ actualVersion;
1223
+ constructor(options) {
1224
+ super(
1225
+ `Concurrency conflict on ${options.aggregateType}(${options.aggregateId}): expected version ${options.expectedVersion}, actual ${options.actualVersion}`,
1226
+ options.cause,
1227
+ { name: "ConcurrencyConflictError" }
1228
+ );
1229
+ this.aggregateType = options.aggregateType;
1230
+ this.aggregateId = options.aggregateId;
1231
+ this.expectedVersion = options.expectedVersion;
1232
+ this.actualVersion = options.actualVersion;
1233
+ }
1234
+ };
1235
+
1236
+ // src/entity/entity.ts
1237
+ var Entity = class {
1238
+ static {
1239
+ __name(this, "Entity");
1240
+ }
1241
+ id;
1242
+ /**
1243
+ * Returns the current state of the entity.
1244
+ *
1245
+ * By default the state object is **shallowly frozen**: direct property
1246
+ * writes (`entity.state.foo = …`) throw in strict mode, but writes to
1247
+ * nested objects (`entity.state.address.zip = …`) bypass the freeze.
1248
+ * For deep immutability either enable the opt-in
1249
+ * {@link EntityConfig.deepFreezeState} (plain-data states only), model
1250
+ * nested data with `vo()` (which freezes deeply), or reach for a
1251
+ * structural-sharing library like Immer at the App layer. The shallow
1252
+ * default is intentional: deep freezing on every state write is too
1253
+ * expensive for hot paths, and DDD aggregates normally treat their own
1254
+ * state as private (`Tell, Don't Ask`).
1255
+ */
1256
+ get state() {
1257
+ return this._state;
1258
+ }
1259
+ /**
1260
+ * The state is 'protected' so that only the subclass can modify it.
1261
+ * Subclasses can mutate this directly or use helper methods; direct
1262
+ * assignments should freeze through {@link freezeState} so the
1263
+ * configured freeze mode is honored.
1264
+ */
1265
+ _state;
1266
+ _deepFreezeState;
1267
+ /**
1268
+ * **State ownership.** Plain-object and array states are shallow-copied
1269
+ * before the freeze, so the caller's own object stays mutable. A CLASS
1270
+ * INSTANCE passed as state is an ownership transfer: it is frozen
1271
+ * in place (a copy would strip its prototype). Do not keep mutating
1272
+ * the instance after handing it to the entity. The same contract
1273
+ * applies to {@link setState}. With
1274
+ * {@link EntityConfig.deepFreezeState} enabled, the ownership transfer
1275
+ * widens to the whole graph: NESTED objects are frozen in place too.
1276
+ */
1277
+ constructor(id, initialState, config) {
1278
+ if (id === null || id === void 0) {
1279
+ throw new Error("Entity ID cannot be null or undefined");
1280
+ }
1281
+ this.id = id;
1282
+ this._deepFreezeState = config?.deepFreezeState ?? false;
1283
+ this._state = this.freezeState(shallowCopyOwned(initialState));
1284
+ this.validateState(this._state);
1285
+ }
1286
+ /**
1287
+ * Freezes a state value according to this entity's configured freeze
1288
+ * mode: the default shallow freeze, or `deepFreeze` when
1289
+ * {@link EntityConfig.deepFreezeState} was enabled at construction.
1290
+ * Subclass code that assigns `this._state` directly (bypassing
1291
+ * `setState`) must freeze through this method, not `freezeShallow`,
1292
+ * or the opt-in silently degrades to shallow for that path.
1293
+ */
1294
+ freezeState(value) {
1295
+ return this._deepFreezeState ? deepFreeze(value) : freezeShallow(value);
1296
+ }
1297
+ /**
1298
+ * Optional validation hook to ensure state invariants. Called during
1299
+ * construction (from `Entity`'s constructor) and again on every
1300
+ * `setState()` call. Throw to reject invalid state.
1301
+ *
1302
+ * **⚠️ Must not read subclass instance fields via `this`.** The
1303
+ * constructor calls `validateState(initialState)` BEFORE the subclass's
1304
+ * field initializers run, so `this.someField` is `undefined` at that
1305
+ * point, a classic TypeScript/JavaScript constructor-ordering footgun.
1306
+ * The `state` argument is the single source of truth; treat the method
1307
+ * as pure with respect to `this`.
1308
+ *
1309
+ * If your invariants genuinely depend on per-instance configuration
1310
+ * that isn't part of the state, pass that configuration into the state
1311
+ * itself (DDD-canonical: the aggregate's state contains everything it
1312
+ * needs) or perform the additional check after construction in a
1313
+ * dedicated factory method.
1314
+ *
1315
+ * @param state - The state to validate
1316
+ * @throws Error (or `DomainError` subclass) if validation fails
1317
+ */
1318
+ validateState(_state) {
1319
+ }
1320
+ /**
1321
+ * Sets the state of the entity.
1322
+ * This is a convenience method for state mutations.
1323
+ * Automatically validates the newState using `validateState()`.
1324
+ *
1325
+ * Plain-object and array states are shallow-copied before the freeze
1326
+ * (the caller's object stays mutable); a class-instance state is an
1327
+ * ownership transfer and is frozen in place; see the constructor.
1328
+ *
1329
+ * @param newState - The new state
1330
+ */
1331
+ setState(newState) {
1332
+ this.validateState(newState);
1333
+ this._state = this.freezeState(shallowCopyOwned(newState));
1334
+ }
1335
+ };
1336
+ function freezeShallow(value) {
1337
+ if (value !== null && typeof value === "object") {
1338
+ return Object.freeze(value);
1339
+ }
1340
+ return value;
832
1341
  }
833
1342
  __name(freezeShallow, "freezeShallow");
834
1343
  function shallowCopyOwned(value) {
@@ -1037,14 +1546,66 @@ var BaseAggregate = class extends Entity {
1037
1546
  *
1038
1547
  * The state is converted via {@link toSnapshotState}; the default
1039
1548
  * requires plain, serialisable data and fails fast otherwise.
1549
+ *
1550
+ * `snapshotAt` is read from the kit's swappable clock, the same one
1551
+ * `createDomainEvent` stamps `occurredAt` from, so
1552
+ * `setClockFactory` / `withClockFactory` pin snapshot timestamps in
1553
+ * deterministic tests too. `schemaVersion` is stamped from
1554
+ * {@link snapshotSchemaVersion} so a later restore can detect
1555
+ * snapshots written against an older `TSnapshotState` shape.
1040
1556
  */
1041
1557
  createSnapshot() {
1042
1558
  return {
1043
1559
  state: this.toSnapshotState(this._state),
1044
1560
  version: this.version,
1045
- snapshotAt: /* @__PURE__ */ new Date()
1561
+ snapshotAt: now(),
1562
+ schemaVersion: this.snapshotSchemaVersion
1046
1563
  };
1047
1564
  }
1565
+ /**
1566
+ * Schema version of the shape {@link toSnapshotState} produces.
1567
+ * Defaults to `1`. Bump it whenever `TSnapshotState` changes
1568
+ * incompatibly (renamed or removed fields, changed representations):
1569
+ * `createSnapshot` stamps it onto every snapshot, and the restore
1570
+ * paths compare it, so an outdated stored snapshot surfaces as a
1571
+ * `SnapshotSchemaMismatchError` at restore time (or is upgraded via
1572
+ * {@link migrateSnapshotState}) instead of crashing on the first
1573
+ * method call much later.
1574
+ */
1575
+ snapshotSchemaVersion = 1;
1576
+ /**
1577
+ * Resolves a stored snapshot's state against the aggregate's current
1578
+ * snapshot schema: pass-through when the versions match (a missing
1579
+ * `schemaVersion` counts as `1`, the pre-versioning era), otherwise
1580
+ * routed through {@link migrateSnapshotState}. Called by both restore
1581
+ * paths BEFORE anything is assigned, so a rejected snapshot leaves
1582
+ * the aggregate untouched.
1583
+ */
1584
+ resolveSnapshotState(snapshot) {
1585
+ const storedSchemaVersion = snapshot.schemaVersion ?? 1;
1586
+ if (storedSchemaVersion === this.snapshotSchemaVersion) {
1587
+ return snapshot.state;
1588
+ }
1589
+ return this.migrateSnapshotState(snapshot.state, storedSchemaVersion);
1590
+ }
1591
+ /**
1592
+ * Upgrade hook for snapshots written against an older
1593
+ * `TSnapshotState` shape. Receives the stored state as `unknown`
1594
+ * (its shape is, by definition, not the current `TSnapshotState`)
1595
+ * plus the schema version it was written with, and returns the
1596
+ * current shape. The default rejects with
1597
+ * `SnapshotSchemaMismatchError`: discard-and-refold from the full
1598
+ * event stream is the safe default strategy; override this only when
1599
+ * upgrading in place is cheaper than refolding.
1600
+ */
1601
+ migrateSnapshotState(_stored, storedSchemaVersion) {
1602
+ throw new SnapshotSchemaMismatchError({
1603
+ aggregateType: this.aggregateType,
1604
+ aggregateId: String(this.id),
1605
+ expectedSchemaVersion: this.snapshotSchemaVersion,
1606
+ actualSchemaVersion: storedSchemaVersion
1607
+ });
1608
+ }
1048
1609
  /**
1049
1610
  * Converts live aggregate state into the plain-data shape stored in a
1050
1611
  * snapshot. The default validates that the state graph is plain,
@@ -1213,7 +1774,7 @@ var AggregateRoot = class extends BaseAggregate {
1213
1774
  */
1214
1775
  _hasBaseline = false;
1215
1776
  constructor(id, initialState, config) {
1216
- super(id, initialState);
1777
+ super(id, initialState, config);
1217
1778
  this._autoVersionBump = config?.autoVersionBump ?? false;
1218
1779
  }
1219
1780
  /**
@@ -1368,6 +1929,14 @@ var AggregateRoot = class extends BaseAggregate {
1368
1929
  * Sets the state and optionally bumps the version automatically.
1369
1930
  * Validates `newState` via `validateState()`.
1370
1931
  *
1932
+ * **State the OCC intent explicitly.** With neither the per-call
1933
+ * `bumpVersion` argument nor an `autoVersionBump` config, the version
1934
+ * does not move, and an un-bumped mutation can be silently overwritten
1935
+ * by a concurrent writer (see the {@link AggregateConfig.autoVersionBump}
1936
+ * warning for the mechanics). Prefer `commit()` for domain mutations,
1937
+ * or pass `bumpVersion` explicitly; relying on the implicit default is
1938
+ * deprecated and v3 makes the argument required.
1939
+ *
1371
1940
  * @param newState - The new state
1372
1941
  * @param bumpVersion - Whether to bump the version (defaults to autoVersionBump config)
1373
1942
  */
@@ -1386,9 +1955,9 @@ var AggregateRoot = class extends BaseAggregate {
1386
1955
  * @param snapshot - The snapshot to restore from
1387
1956
  */
1388
1957
  restoreFromSnapshot(snapshot) {
1389
- const restored = this.fromSnapshotState(snapshot.state);
1958
+ const restored = this.fromSnapshotState(this.resolveSnapshotState(snapshot));
1390
1959
  this.validateState(restored);
1391
- this._state = freezeShallow(restored);
1960
+ this._state = this.freezeState(restored);
1392
1961
  this.markRestored(snapshot.version);
1393
1962
  }
1394
1963
  };
@@ -1419,153 +1988,37 @@ function computeChangedKeys(baseline, current) {
1419
1988
  return dirty;
1420
1989
  }
1421
1990
  __name(computeChangedKeys, "computeChangedKeys");
1422
- var DomainError = class extends BaseError {
1423
- static {
1424
- __name(this, "DomainError");
1425
- }
1426
- };
1427
- var InfrastructureError = class extends BaseError {
1428
- static {
1429
- __name(this, "InfrastructureError");
1430
- }
1431
- };
1432
- var MissingHandlerError = class extends BaseError {
1433
- constructor(eventType, cause) {
1434
- super(`Missing handler for event type: ${eventType}`, cause, {
1435
- name: "MissingHandlerError"
1436
- });
1437
- this.eventType = eventType;
1438
- }
1991
+ var EventSourcedAggregate = class extends BaseAggregate {
1439
1992
  static {
1440
- __name(this, "MissingHandlerError");
1993
+ __name(this, "EventSourcedAggregate");
1441
1994
  }
1442
- };
1443
- var EventHarvestError = class extends BaseError {
1444
- constructor(message, eventType) {
1445
- super(message, void 0, { name: "EventHarvestError" });
1446
- this.eventType = eventType;
1995
+ /**
1996
+ * Validates an event before it is applied. Default is no-op.
1997
+ * Subclasses override to throw a concrete `DomainError` subclass when
1998
+ * the event violates an invariant in the current state.
1999
+ */
2000
+ validateEvent(_event) {
1447
2001
  }
1448
- static {
1449
- __name(this, "EventHarvestError");
1450
- }
1451
- };
1452
- var UnenrolledChangesError = class extends BaseError {
1453
- constructor(aggregateId) {
1454
- super(
1455
- `Aggregate ${aggregateId} was loaded in this unit of work and has pending events, but was never enrolled (no save), so its events would be silently dropped. Call repository.save(aggregate), and ensure save() calls session.enrollSaved before the row write.`,
1456
- void 0,
1457
- { name: "UnenrolledChangesError" }
1458
- );
1459
- this.aggregateId = aggregateId;
1460
- }
1461
- static {
1462
- __name(this, "UnenrolledChangesError");
1463
- }
1464
- };
1465
- var AggregateDeletedError = class extends BaseError {
1466
- constructor(aggregateId) {
1467
- super(
1468
- `Aggregate ${aggregateId} was deleted in this unit of work and cannot be saved or registered again. Deletion is final within an operation; if the aggregate must live, do not delete it.`,
1469
- void 0,
1470
- { name: "AggregateDeletedError" }
1471
- );
1472
- this.aggregateId = aggregateId;
1473
- }
1474
- static {
1475
- __name(this, "AggregateDeletedError");
1476
- }
1477
- };
1478
- var AggregateNotFoundError = class extends InfrastructureError {
1479
- static {
1480
- __name(this, "AggregateNotFoundError");
1481
- }
1482
- aggregateType;
1483
- id;
1484
- constructor(options) {
1485
- super(
1486
- `Aggregate not found: ${options.aggregateType}(${options.id})`,
1487
- options.cause,
1488
- { name: "AggregateNotFoundError" }
1489
- );
1490
- this.aggregateType = options.aggregateType;
1491
- this.id = options.id;
1492
- }
1493
- };
1494
- var DuplicateAggregateError = class extends InfrastructureError {
1495
- static {
1496
- __name(this, "DuplicateAggregateError");
1497
- }
1498
- aggregateType;
1499
- aggregateId;
1500
- constructor(options) {
1501
- super(
1502
- `Duplicate aggregate: ${options.aggregateType}(${options.aggregateId}) already exists`,
1503
- options.cause,
1504
- { name: "DuplicateAggregateError" }
1505
- );
1506
- this.aggregateType = options.aggregateType;
1507
- this.aggregateId = options.aggregateId;
1508
- }
1509
- };
1510
- var ConcurrencyConflictError = class extends InfrastructureError {
1511
- static {
1512
- __name(this, "ConcurrencyConflictError");
1513
- }
1514
- /**
1515
- * Marks this error as retryable so `isRetryable(err)` returns
1516
- * true. The canonical OCC pattern is to reload the aggregate, re-apply
1517
- * the use case, and retry on this exception.
1518
- */
1519
- retryable = true;
1520
- aggregateType;
1521
- aggregateId;
1522
- expectedVersion;
1523
- actualVersion;
1524
- constructor(options) {
1525
- super(
1526
- `Concurrency conflict on ${options.aggregateType}(${options.aggregateId}): expected version ${options.expectedVersion}, actual ${options.actualVersion}`,
1527
- options.cause,
1528
- { name: "ConcurrencyConflictError" }
1529
- );
1530
- this.aggregateType = options.aggregateType;
1531
- this.aggregateId = options.aggregateId;
1532
- this.expectedVersion = options.expectedVersion;
1533
- this.actualVersion = options.actualVersion;
1534
- }
1535
- };
1536
-
1537
- // src/aggregate/event-sourced-aggregate.ts
1538
- var EventSourcedAggregate = class extends BaseAggregate {
1539
- static {
1540
- __name(this, "EventSourcedAggregate");
1541
- }
1542
- /**
1543
- * Validates an event before it is applied. Default is no-op.
1544
- * Subclasses override to throw a concrete `DomainError` subclass when
1545
- * the event violates an invariant in the current state.
1546
- */
1547
- validateEvent(_event) {
1548
- }
1549
- /**
1550
- * Applies an event: validates, locates the handler, computes the next
1551
- * state, then commits state + pending event + version bump atomically.
1552
- *
1553
- * Throws `DomainError` (or a subclass) on validation failure.
1554
- * Throws `MissingHandlerError` if no handler is registered for `event.type`.
1555
- *
1556
- * State is not mutated if any step throws: the handler is invoked into
1557
- * a local and only assigned to `_state` once all checks pass.
1558
- *
1559
- * The method is generic in the event tag `K`, so concrete callers
1560
- * (`this.apply(orderCreated)`) narrow to the literal tag and the
1561
- * dispatched handler is typed as `Handler<TState, Extract<TEvent, { type: K }>>`,
1562
- * with no `as` cast required at the call site.
1563
- *
1564
- * @param event - The domain event to apply
1565
- * @param isNew - Whether the event is new (needs persisting) or replayed from history
1566
- */
1567
- apply(event, isNew = true) {
1568
- this.dispatchAndCommit(event, isNew);
2002
+ /**
2003
+ * Applies an event: validates, locates the handler, computes the next
2004
+ * state, then commits state + pending event + version bump atomically.
2005
+ *
2006
+ * Throws `DomainError` (or a subclass) on validation failure.
2007
+ * Throws `MissingHandlerError` if no handler is registered for `event.type`.
2008
+ *
2009
+ * State is not mutated if any step throws: the handler is invoked into
2010
+ * a local and only assigned to `_state` once all checks pass.
2011
+ *
2012
+ * The method is generic in the event tag `K`, so concrete callers
2013
+ * (`this.apply(orderCreated)`) narrow to the literal tag and the
2014
+ * dispatched handler is typed as `Handler<TState, Extract<TEvent, { type: K }>>`,
2015
+ * with no `as` cast required at the call site.
2016
+ *
2017
+ * @param event - The domain event to apply
2018
+ * @param isNew - Whether the event is new (needs persisting) or replayed from history
2019
+ */
2020
+ apply(event, isNew = true) {
2021
+ this.dispatchAndCommit(event, isNew);
1569
2022
  }
1570
2023
  /**
1571
2024
  * Internal dispatch path used by `apply()` and the replay methods
@@ -1581,7 +2034,7 @@ var EventSourcedAggregate = class extends BaseAggregate {
1581
2034
  throw new MissingHandlerError(event.type);
1582
2035
  }
1583
2036
  const nextState = handler(this._state, event);
1584
- this._state = freezeShallow(nextState);
2037
+ this._state = this.freezeState(nextState);
1585
2038
  if (isNew) {
1586
2039
  this.addDomainEvent(event);
1587
2040
  this.bumpVersion();
@@ -1600,11 +2053,24 @@ var EventSourcedAggregate = class extends BaseAggregate {
1600
2053
  * which never bumps it; only the final `markRestored` advances it.)
1601
2054
  *
1602
2055
  * Version advances additively: the aggregate's pre-existing version plus
1603
- * `history.length`. A fresh aggregate (v=0) loading 3 events ends at v=3;
1604
- * an aggregate already at v=1 (e.g. after a creation event) loading
1605
- * 2 events ends at v=3, not v=2.
2056
+ * `history.length`. A fresh aggregate (v=0) loading 3 events ends at
2057
+ * v=3; a PERSISTED aggregate at v=P (`persistedVersion === P`) catching
2058
+ * up on M newer events ends at v=P+M.
2059
+ *
2060
+ * **The replay target must be fresh or persisted.** An aggregate with
2061
+ * unflushed `pendingEvents`, or with an in-memory version that was
2062
+ * never persisted (a factory-created instance), throws
2063
+ * {@link UnreplayableAggregateError} BEFORE anything moves: replaying
2064
+ * onto it would `markRestored` a `persistedVersion` that counts
2065
+ * unpersisted history, flipping repository routing from INSERT to
2066
+ * UPDATE (or appending with a wrong expected version). The throw is
2067
+ * deliberate (crash-loud programming bug), never a `Result` `Err`, and
2068
+ * runs before the empty-history fast path so the misuse is caught
2069
+ * deterministically rather than only when the stream happens to be
2070
+ * non-empty.
1606
2071
  */
1607
2072
  loadFromHistory(history) {
2073
+ this.assertReplayTargetFresh(true);
1608
2074
  if (history.length === 0) return ok();
1609
2075
  const previousState = this._state;
1610
2076
  const startVersion = this.version;
@@ -1629,11 +2095,29 @@ var EventSourcedAggregate = class extends BaseAggregate {
1629
2095
  * All-or-nothing: if any event mid-stream throws a `DomainError`, the
1630
2096
  * aggregate is rolled back to its pre-call state + version. Partial
1631
2097
  * restoration is never observable to the caller.
2098
+ *
2099
+ * **The restore target must not carry pending events.** Events recorded
2100
+ * before the restore are unrelated to the restored stream; harvesting
2101
+ * them after `markRestored` would emit them with a version baseline
2102
+ * they were never part of. Such a target throws
2103
+ * {@link UnreplayableAggregateError} before anything moves (crash-loud
2104
+ * programming bug, never a `Result` `Err`). Unlike `loadFromHistory`,
2105
+ * a never-persisted in-memory version is fine here: the snapshot
2106
+ * overwrites state and version entirely instead of adding to them.
1632
2107
  */
1633
2108
  restoreFromSnapshotWithEvents(snapshot, eventsAfterSnapshot) {
2109
+ this.assertReplayTargetFresh(false);
1634
2110
  const previousState = this._state;
1635
2111
  const previousVersion = this.version;
1636
- this._state = freezeShallow(this.fromSnapshotState(snapshot.state));
2112
+ let restored;
2113
+ try {
2114
+ restored = this.fromSnapshotState(this.resolveSnapshotState(snapshot));
2115
+ this.validateState(restored);
2116
+ } catch (e) {
2117
+ if (e instanceof DomainError) return err(e);
2118
+ throw e;
2119
+ }
2120
+ this._state = this.freezeState(restored);
1637
2121
  this.setVersion(snapshot.version);
1638
2122
  for (const event of eventsAfterSnapshot) {
1639
2123
  try {
@@ -1650,6 +2134,29 @@ var EventSourcedAggregate = class extends BaseAggregate {
1650
2134
  );
1651
2135
  return ok();
1652
2136
  }
2137
+ /**
2138
+ * Replay-target freshness guard shared by `loadFromHistory` and
2139
+ * `restoreFromSnapshotWithEvents`. See {@link UnreplayableAggregateError}
2140
+ * for the desync both conditions prevent. `checkUnpersistedVersion` is
2141
+ * `true` for the additive `loadFromHistory` path only: the snapshot
2142
+ * restore overwrites version wholesale, so a never-persisted in-memory
2143
+ * version is harmless there.
2144
+ */
2145
+ assertReplayTargetFresh(checkUnpersistedVersion) {
2146
+ const pending = this.pendingEvents.length;
2147
+ if (pending > 0) {
2148
+ throw new UnreplayableAggregateError(
2149
+ String(this.id),
2150
+ `it carries ${pending} unflushed pending event(s) that are not part of the persisted stream`
2151
+ );
2152
+ }
2153
+ if (checkUnpersistedVersion && this.version > 0 && this.persistedVersion === void 0) {
2154
+ throw new UnreplayableAggregateError(
2155
+ String(this.id),
2156
+ `its in-memory version (${this.version}) was never persisted (persistedVersion is undefined), so additive replay would mark unpersisted history as persisted`
2157
+ );
2158
+ }
2159
+ }
1653
2160
  };
1654
2161
 
1655
2162
  // src/app/describe-thrown.ts
@@ -1687,7 +2194,10 @@ var CommandBus = class {
1687
2194
  if (!handler) {
1688
2195
  return err(
1689
2196
  this.errorMapper(
1690
- new Error(`No handler registered for command type: ${command.type}`)
2197
+ new UnregisteredHandlerError({
2198
+ busKind: "command",
2199
+ messageType: command.type
2200
+ })
1691
2201
  )
1692
2202
  );
1693
2203
  }
@@ -1705,6 +2215,21 @@ function abortReason(signal, fallbackMessage) {
1705
2215
  }
1706
2216
  __name(abortReason, "abortReason");
1707
2217
 
2218
+ // src/utils/observer.ts
2219
+ function reportToObserver(invoke) {
2220
+ let result;
2221
+ try {
2222
+ result = invoke();
2223
+ } catch {
2224
+ return;
2225
+ }
2226
+ if (result !== null && typeof result === "object" && typeof result.then === "function") {
2227
+ result.then(void 0, () => {
2228
+ });
2229
+ }
2230
+ }
2231
+ __name(reportToObserver, "reportToObserver");
2232
+
1708
2233
  // src/app/handler.ts
1709
2234
  async function withCommit(deps, fn) {
1710
2235
  if (deps.signal?.aborted) {
@@ -1763,22 +2288,67 @@ async function withCommit(deps, fn) {
1763
2288
  } else {
1764
2289
  agg.markPersisted(agg.version);
1765
2290
  }
1766
- } catch {
2291
+ } catch (error) {
2292
+ reportToObserver(() => deps.onPersistError?.(error, agg));
1767
2293
  }
1768
2294
  }
1769
2295
  if (deps.bus && events.length > 0) {
1770
2296
  try {
1771
2297
  await deps.bus.publish(events);
1772
2298
  } catch (error) {
1773
- try {
1774
- deps.onPublishError?.(error, events);
1775
- } catch {
1776
- }
2299
+ reportToObserver(() => deps.onPublishError?.(error, events));
1777
2300
  }
1778
2301
  }
1779
2302
  return result;
1780
2303
  }
1781
2304
  __name(withCommit, "withCommit");
2305
+ var QueryBus = class {
2306
+ static {
2307
+ __name(this, "QueryBus");
2308
+ }
2309
+ handlers = /* @__PURE__ */ new Map();
2310
+ errorMapper;
2311
+ constructor(...args) {
2312
+ this.errorMapper = args[0]?.errorMapper ?? describeThrown;
2313
+ }
2314
+ register(queryType, handler) {
2315
+ if (this.handlers.has(queryType)) {
2316
+ throw new Error(
2317
+ `QueryBus: a handler for query type "${queryType}" is already registered`
2318
+ );
2319
+ }
2320
+ this.handlers.set(queryType, (query) => handler(query));
2321
+ }
2322
+ async execute(query) {
2323
+ const handler = this.handlers.get(query.type);
2324
+ if (!handler) {
2325
+ return err(
2326
+ this.errorMapper(
2327
+ new UnregisteredHandlerError({
2328
+ busKind: "query",
2329
+ messageType: query.type
2330
+ })
2331
+ )
2332
+ );
2333
+ }
2334
+ try {
2335
+ const result = await handler(query);
2336
+ return ok(result);
2337
+ } catch (error) {
2338
+ return err(this.errorMapper(error));
2339
+ }
2340
+ }
2341
+ async executeUnsafe(query) {
2342
+ const handler = this.handlers.get(query.type);
2343
+ if (!handler) {
2344
+ throw new UnregisteredHandlerError({
2345
+ busKind: "query",
2346
+ messageType: query.type
2347
+ });
2348
+ }
2349
+ return await handler(query);
2350
+ }
2351
+ };
1782
2352
 
1783
2353
  // src/repo/identity-map.ts
1784
2354
  var IdentityMap = class {
@@ -1983,6 +2553,7 @@ var UnitOfWork = class {
1983
2553
  bus: this.deps.bus,
1984
2554
  scope: this.deps.scope,
1985
2555
  onPublishError: this.deps.onPublishError,
2556
+ onPersistError: this.deps.onPersistError,
1986
2557
  signal: options?.signal
1987
2558
  },
1988
2559
  async (tx) => {
@@ -2010,20 +2581,11 @@ var UnitOfWork = class {
2010
2581
  }
2011
2582
  );
2012
2583
  } catch (error) {
2013
- if (workThrew) {
2014
- if (error === workError || causeChainContains(error, workError)) {
2015
- throw error;
2016
- }
2017
- throw new RollbackError(workError, error);
2018
- }
2019
- if (workCompleted) {
2020
- const harvestError = findHarvestErrorInChain(error);
2021
- if (harvestError) {
2022
- throw harvestError;
2023
- }
2024
- throw new CommitError(error);
2025
- }
2026
- throw error;
2584
+ throw classifyRunError(error, {
2585
+ workThrew,
2586
+ workCompleted,
2587
+ workError
2588
+ });
2027
2589
  } finally {
2028
2590
  session?.close();
2029
2591
  this._active = false;
@@ -2121,6 +2683,23 @@ function makeContext(repositories, transaction, session, signal) {
2121
2683
  };
2122
2684
  }
2123
2685
  __name(makeContext, "makeContext");
2686
+ function classifyRunError(error, state) {
2687
+ if (state.workThrew) {
2688
+ if (error === state.workError || causeChainContains(error, state.workError)) {
2689
+ return error;
2690
+ }
2691
+ return new RollbackError(state.workError, error);
2692
+ }
2693
+ if (state.workCompleted) {
2694
+ const harvestError = findHarvestErrorInChain(error);
2695
+ if (harvestError) {
2696
+ return harvestError;
2697
+ }
2698
+ return new CommitError(error);
2699
+ }
2700
+ return error;
2701
+ }
2702
+ __name(classifyRunError, "classifyRunError");
2124
2703
  function findHarvestErrorInChain(error) {
2125
2704
  const seen = /* @__PURE__ */ new Set();
2126
2705
  let current = error;
@@ -2162,82 +2741,1057 @@ function causeChainContains(error, target) {
2162
2741
  return false;
2163
2742
  }
2164
2743
  __name(causeChainContains, "causeChainContains");
2165
- var QueryBus = class {
2744
+ var InvalidDomainTransitionError = class extends DomainError {
2745
+ constructor(state, inputType) {
2746
+ super(`No domain transition from "${state}" on "${inputType}".`);
2747
+ this.state = state;
2748
+ this.inputType = inputType;
2749
+ }
2166
2750
  static {
2167
- __name(this, "QueryBus");
2751
+ __name(this, "InvalidDomainTransitionError");
2168
2752
  }
2169
- handlers = /* @__PURE__ */ new Map();
2170
- errorMapper;
2171
- constructor(...args) {
2172
- this.errorMapper = args[0]?.errorMapper ?? describeThrown;
2753
+ };
2754
+ var DomainTransitionGuardRejectedError = class extends DomainError {
2755
+ constructor(state, inputType) {
2756
+ super(`Domain transition guard rejected "${inputType}" from "${state}".`);
2757
+ this.state = state;
2758
+ this.inputType = inputType;
2173
2759
  }
2174
- register(queryType, handler) {
2175
- if (this.handlers.has(queryType)) {
2176
- throw new Error(
2177
- `QueryBus: a handler for query type "${queryType}" is already registered`
2178
- );
2179
- }
2180
- this.handlers.set(queryType, (query) => handler(query));
2760
+ static {
2761
+ __name(this, "DomainTransitionGuardRejectedError");
2181
2762
  }
2182
- async execute(query) {
2183
- const handler = this.handlers.get(query.type);
2184
- if (!handler) {
2185
- return err(
2186
- this.errorMapper(
2187
- new Error(`No handler registered for query type: ${query.type}`)
2188
- )
2189
- );
2190
- }
2191
- try {
2192
- const result = await handler(query);
2193
- return ok(result);
2194
- } catch (error) {
2195
- return err(this.errorMapper(error));
2196
- }
2763
+ };
2764
+ var InvalidDomainMachineDefinitionError = class extends BaseError {
2765
+ static {
2766
+ __name(this, "InvalidDomainMachineDefinitionError");
2197
2767
  }
2198
- async executeUnsafe(query) {
2199
- const handler = this.handlers.get(query.type);
2200
- if (!handler) {
2201
- throw new Error(`No handler registered for query type: ${query.type}`);
2202
- }
2203
- return await handler(query);
2768
+ constructor(message, cause) {
2769
+ super(message, cause, { name: "InvalidDomainMachineDefinitionError" });
2204
2770
  }
2205
2771
  };
2206
-
2207
- // src/events/event-bus.ts
2208
- var EventBusImpl = class {
2772
+ var InvalidDomainMachineContextError = class extends BaseError {
2209
2773
  static {
2210
- __name(this, "EventBusImpl");
2774
+ __name(this, "InvalidDomainMachineContextError");
2211
2775
  }
2212
- handlers = /* @__PURE__ */ new Map();
2213
- subscribe(eventType, handler) {
2214
- const type = eventType;
2215
- if (!this.handlers.has(type)) {
2216
- this.handlers.set(type, []);
2217
- }
2218
- const handlersForType = this.handlers.get(type);
2219
- const casted = handler;
2220
- handlersForType.push(casted);
2221
- let removed = false;
2222
- return () => {
2223
- if (removed) return;
2224
- const idx = handlersForType.indexOf(casted);
2225
- if (idx !== -1) {
2226
- handlersForType.splice(idx, 1);
2227
- removed = true;
2228
- }
2229
- if (handlersForType.length === 0) {
2230
- this.handlers.delete(type);
2231
- }
2232
- };
2776
+ constructor(message, cause) {
2777
+ super(message, cause, { name: "InvalidDomainMachineContextError" });
2233
2778
  }
2234
- once(eventType, options) {
2235
- return new Promise((resolve, reject) => {
2236
- if (options?.signal?.aborted) {
2237
- reject(abortReason(options.signal, "EventBus.once aborted"));
2238
- return;
2239
- }
2240
- let timer;
2779
+ };
2780
+ var InvalidDomainMachineSnapshotError = class extends BaseError {
2781
+ static {
2782
+ __name(this, "InvalidDomainMachineSnapshotError");
2783
+ }
2784
+ constructor(message, cause) {
2785
+ super(message, cause, { name: "InvalidDomainMachineSnapshotError" });
2786
+ }
2787
+ };
2788
+ var InvalidDomainMachineInputError = class extends BaseError {
2789
+ static {
2790
+ __name(this, "InvalidDomainMachineInputError");
2791
+ }
2792
+ constructor(message, cause) {
2793
+ super(message, cause, { name: "InvalidDomainMachineInputError" });
2794
+ }
2795
+ };
2796
+ var InvalidDomainTransitionGuardResultError = class extends BaseError {
2797
+ static {
2798
+ __name(this, "InvalidDomainTransitionGuardResultError");
2799
+ }
2800
+ constructor(message, cause) {
2801
+ super(message, cause, { name: "InvalidDomainTransitionGuardResultError" });
2802
+ }
2803
+ };
2804
+ var InvalidDomainTransitionResultError = class extends BaseError {
2805
+ static {
2806
+ __name(this, "InvalidDomainTransitionResultError");
2807
+ }
2808
+ constructor(message, cause) {
2809
+ super(message, cause, { name: "InvalidDomainTransitionResultError" });
2810
+ }
2811
+ };
2812
+ var ReentrantDomainStateMachineEvaluationError = class extends BaseError {
2813
+ static {
2814
+ __name(this, "ReentrantDomainStateMachineEvaluationError");
2815
+ }
2816
+ constructor() {
2817
+ super(
2818
+ "Domain state machine callbacks cannot evaluate the same machine.",
2819
+ void 0,
2820
+ { name: "ReentrantDomainStateMachineEvaluationError" }
2821
+ );
2822
+ }
2823
+ };
2824
+
2825
+ // src/domain-state-machine/machine-data.ts
2826
+ var DOMAIN_MACHINE_DATA_MAX_DEPTH = 256;
2827
+ var DOMAIN_MACHINE_DATA_MAX_NODES = 1e4;
2828
+ var DOMAIN_MACHINE_DATA_MAX_PROPERTIES = 1e5;
2829
+ function copyDomainMachineOutputs(outputs) {
2830
+ try {
2831
+ const copiedOutputs = cloneDomainMachineDataValue(
2832
+ outputs ?? [],
2833
+ createDomainTransitionOutputError
2834
+ );
2835
+ return deepFreeze(
2836
+ copiedOutputs
2837
+ );
2838
+ } catch (cause) {
2839
+ if (cause instanceof InvalidDomainTransitionResultError) {
2840
+ throw cause;
2841
+ }
2842
+ throw new InvalidDomainTransitionResultError(
2843
+ "Domain transition result outputs must contain cloneable, deeply immutable data.",
2844
+ cause
2845
+ );
2846
+ }
2847
+ }
2848
+ __name(copyDomainMachineOutputs, "copyDomainMachineOutputs");
2849
+ function copyDomainMachineInput(input) {
2850
+ try {
2851
+ return deepFreeze(
2852
+ cloneDomainMachineDataValue(input, createDomainMachineInputError)
2853
+ );
2854
+ } catch (cause) {
2855
+ if (cause instanceof InvalidDomainMachineInputError) {
2856
+ throw cause;
2857
+ }
2858
+ throw new InvalidDomainMachineInputError(
2859
+ "Domain machine input must contain cloneable, deeply immutable data.",
2860
+ cause
2861
+ );
2862
+ }
2863
+ }
2864
+ __name(copyDomainMachineInput, "copyDomainMachineInput");
2865
+ function copyDomainMachineContext(context) {
2866
+ try {
2867
+ return deepFreeze(
2868
+ cloneDomainMachineDataValue(context, createDomainMachineContextError)
2869
+ );
2870
+ } catch (cause) {
2871
+ if (cause instanceof InvalidDomainMachineContextError) {
2872
+ throw cause;
2873
+ }
2874
+ throw new InvalidDomainMachineContextError(
2875
+ "Domain machine context must contain cloneable, deeply immutable data.",
2876
+ cause
2877
+ );
2878
+ }
2879
+ }
2880
+ __name(copyDomainMachineContext, "copyDomainMachineContext");
2881
+ function createDomainMachineContextError(message, cause) {
2882
+ return new InvalidDomainMachineContextError(message, cause);
2883
+ }
2884
+ __name(createDomainMachineContextError, "createDomainMachineContextError");
2885
+ function createDomainMachineInputError(message, cause) {
2886
+ return new InvalidDomainMachineInputError(message, cause);
2887
+ }
2888
+ __name(createDomainMachineInputError, "createDomainMachineInputError");
2889
+ function createDomainTransitionOutputError(message, cause) {
2890
+ return new InvalidDomainTransitionResultError(message, cause);
2891
+ }
2892
+ __name(createDomainTransitionOutputError, "createDomainTransitionOutputError");
2893
+ function cloneDomainMachineDataValue(value, errorFactory, seen = /* @__PURE__ */ new WeakMap(), traversal = { nodes: 0, properties: 0 }, depth = 0) {
2894
+ if (typeof value === "function") {
2895
+ throw errorFactory("Domain machine data cannot contain function values.");
2896
+ }
2897
+ if (value === null || typeof value !== "object") return value;
2898
+ const source = value;
2899
+ if (depth > DOMAIN_MACHINE_DATA_MAX_DEPTH) {
2900
+ throw errorFactory(
2901
+ `Domain machine data exceeds the maximum depth of ${DOMAIN_MACHINE_DATA_MAX_DEPTH}.`
2902
+ );
2903
+ }
2904
+ const existing = seen.get(source);
2905
+ if (existing !== void 0) return existing;
2906
+ traversal.nodes += 1;
2907
+ if (traversal.nodes > DOMAIN_MACHINE_DATA_MAX_NODES) {
2908
+ throw errorFactory(
2909
+ `Domain machine data contains more than ${DOMAIN_MACHINE_DATA_MAX_NODES.toLocaleString("en-US")} object nodes.`
2910
+ );
2911
+ }
2912
+ const toStringTagDescriptor = findPropertyDescriptor(
2913
+ source,
2914
+ Symbol.toStringTag
2915
+ );
2916
+ if (toStringTagDescriptor !== void 0 && !("value" in toStringTagDescriptor)) {
2917
+ throw errorFactory(
2918
+ "Domain machine data cannot contain accessor properties."
2919
+ );
2920
+ }
2921
+ if (Array.isArray(value)) {
2922
+ if (!isIntrinsicArrayPrototype(Object.getPrototypeOf(value))) {
2923
+ throw errorFactory(
2924
+ "Domain machine data cannot contain custom Array instances."
2925
+ );
2926
+ }
2927
+ const cloned2 = new Array(value.length);
2928
+ seen.set(source, cloned2);
2929
+ for (const key of readDomainMachineDataKeys(
2930
+ source,
2931
+ errorFactory,
2932
+ traversal
2933
+ )) {
2934
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
2935
+ if (!descriptor) continue;
2936
+ if (!("value" in descriptor)) {
2937
+ throw errorFactory(
2938
+ "Domain machine data cannot contain accessor properties."
2939
+ );
2940
+ }
2941
+ if (key === "length") continue;
2942
+ descriptor.value = cloneDomainMachineDataValue(
2943
+ descriptor.value,
2944
+ errorFactory,
2945
+ seen,
2946
+ traversal,
2947
+ depth + 1
2948
+ );
2949
+ Object.defineProperty(cloned2, key, descriptor);
2950
+ }
2951
+ return cloned2;
2952
+ }
2953
+ const prototype = Object.getPrototypeOf(source);
2954
+ if (prototype !== null && !isIntrinsicObjectPrototype(prototype)) {
2955
+ throw errorFactory(
2956
+ "Domain machine data cannot contain custom class instances."
2957
+ );
2958
+ }
2959
+ const tag = Object.prototype.toString.call(source);
2960
+ if (isBuiltInObject(source, tag) || ArrayBuffer.isView(source)) {
2961
+ throw errorFactory(
2962
+ `Domain machine data cannot contain ${tag.slice(8, -1)} object values.`
2963
+ );
2964
+ }
2965
+ const cloned = Object.create(prototype === null ? null : Object.prototype);
2966
+ seen.set(source, cloned);
2967
+ for (const key of readDomainMachineDataKeys(
2968
+ source,
2969
+ errorFactory,
2970
+ traversal
2971
+ )) {
2972
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
2973
+ if (!descriptor) continue;
2974
+ if (!("value" in descriptor)) {
2975
+ throw errorFactory(
2976
+ "Domain machine data cannot contain accessor properties."
2977
+ );
2978
+ }
2979
+ descriptor.value = cloneDomainMachineDataValue(
2980
+ descriptor.value,
2981
+ errorFactory,
2982
+ seen,
2983
+ traversal,
2984
+ depth + 1
2985
+ );
2986
+ Object.defineProperty(cloned, key, descriptor);
2987
+ }
2988
+ return cloned;
2989
+ }
2990
+ __name(cloneDomainMachineDataValue, "cloneDomainMachineDataValue");
2991
+ function readDomainMachineDataKeys(value, errorFactory, traversal) {
2992
+ const keys = Reflect.ownKeys(value);
2993
+ traversal.properties += keys.length;
2994
+ if (traversal.properties > DOMAIN_MACHINE_DATA_MAX_PROPERTIES) {
2995
+ throw errorFactory(
2996
+ `Domain machine data contains more than ${DOMAIN_MACHINE_DATA_MAX_PROPERTIES.toLocaleString("en-US")} own properties.`
2997
+ );
2998
+ }
2999
+ return keys;
3000
+ }
3001
+ __name(readDomainMachineDataKeys, "readDomainMachineDataKeys");
3002
+ function isRecord(value) {
3003
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3004
+ }
3005
+ __name(isRecord, "isRecord");
3006
+ function isPlainRecord(value) {
3007
+ if (!isRecord(value)) return false;
3008
+ const prototype = Object.getPrototypeOf(value);
3009
+ return prototype === null || isIntrinsicObjectPrototype(prototype);
3010
+ }
3011
+ __name(isPlainRecord, "isPlainRecord");
3012
+ function isIntrinsicArrayPrototype(prototype) {
3013
+ if (prototype === null || !Array.isArray(prototype)) return false;
3014
+ if (!isIntrinsicConstructorPrototype(prototype, "Array")) return false;
3015
+ const parentPrototype = Object.getPrototypeOf(prototype);
3016
+ return parentPrototype !== null && isIntrinsicObjectPrototype(parentPrototype);
3017
+ }
3018
+ __name(isIntrinsicArrayPrototype, "isIntrinsicArrayPrototype");
3019
+ function isIntrinsicObjectPrototype(prototype) {
3020
+ return Object.getPrototypeOf(prototype) === null && isIntrinsicConstructorPrototype(prototype, "Object");
3021
+ }
3022
+ __name(isIntrinsicObjectPrototype, "isIntrinsicObjectPrototype");
3023
+ function hasOwn(value, key) {
3024
+ return Object.hasOwn(value, key);
3025
+ }
3026
+ __name(hasOwn, "hasOwn");
3027
+
3028
+ // src/domain-state-machine/definition.ts
3029
+ var DOMAIN_MACHINE_DEFINITION_KEYS = /* @__PURE__ */ new Set([
3030
+ "initial",
3031
+ "initialContext",
3032
+ "validateSnapshot",
3033
+ "states"
3034
+ ]);
3035
+ var DOMAIN_MACHINE_STATE_NODE_KEYS = /* @__PURE__ */ new Set([
3036
+ "terminal",
3037
+ "validateContext",
3038
+ "on"
3039
+ ]);
3040
+ var DOMAIN_MACHINE_TRANSITION_KEYS = /* @__PURE__ */ new Set([
3041
+ "target",
3042
+ "guard",
3043
+ "reduce"
3044
+ ]);
3045
+ function copyDomainMachineDefinition(definition) {
3046
+ const states = readDomainMachineDefinitionProperty(
3047
+ definition,
3048
+ "states"
3049
+ );
3050
+ const copiedStates = /* @__PURE__ */ Object.create(null);
3051
+ for (const state of Object.keys(states)) {
3052
+ const node = readDomainMachineDefinitionProperty(
3053
+ states,
3054
+ state
3055
+ );
3056
+ const transitions = readOptionalDomainMachineDefinitionProperty(
3057
+ node,
3058
+ "on"
3059
+ );
3060
+ const copiedTransitions = /* @__PURE__ */ Object.create(null);
3061
+ for (const inputType of Object.keys(
3062
+ transitions ?? {}
3063
+ )) {
3064
+ const transition = readDomainMachineDefinitionProperty(
3065
+ transitions,
3066
+ inputType
3067
+ );
3068
+ if (transition) {
3069
+ const copiedTransition = Object.freeze({
3070
+ target: readDomainMachineDefinitionProperty(transition, "target"),
3071
+ guard: readOptionalDomainMachineDefinitionProperty(
3072
+ transition,
3073
+ "guard"
3074
+ ),
3075
+ reduce: readOptionalDomainMachineDefinitionProperty(
3076
+ transition,
3077
+ "reduce"
3078
+ )
3079
+ });
3080
+ Object.defineProperty(copiedTransitions, inputType, {
3081
+ value: copiedTransition,
3082
+ enumerable: true
3083
+ });
3084
+ }
3085
+ }
3086
+ Object.defineProperty(copiedStates, state, {
3087
+ value: Object.freeze({
3088
+ terminal: readOptionalDomainMachineDefinitionProperty(node, "terminal"),
3089
+ validateContext: readOptionalDomainMachineDefinitionProperty(
3090
+ node,
3091
+ "validateContext"
3092
+ ),
3093
+ on: Object.freeze(copiedTransitions)
3094
+ }),
3095
+ enumerable: true
3096
+ });
3097
+ }
3098
+ return Object.freeze({
3099
+ initial: readDomainMachineDefinitionProperty(
3100
+ definition,
3101
+ "initial"
3102
+ ),
3103
+ initialContext: readDomainMachineDefinitionProperty(
3104
+ definition,
3105
+ "initialContext"
3106
+ ),
3107
+ validateSnapshot: readOptionalDomainMachineDefinitionProperty(
3108
+ definition,
3109
+ "validateSnapshot"
3110
+ ),
3111
+ states: Object.freeze(copiedStates)
3112
+ });
3113
+ }
3114
+ __name(copyDomainMachineDefinition, "copyDomainMachineDefinition");
3115
+ var preparedDefinitions = /* @__PURE__ */ new WeakSet();
3116
+ function ensureStableDomainMachineDefinition(definition) {
3117
+ if (preparedDefinitions.has(definition)) return definition;
3118
+ validateDomainMachineDefinition(definition);
3119
+ return copyDomainMachineDefinition(definition);
3120
+ }
3121
+ __name(ensureStableDomainMachineDefinition, "ensureStableDomainMachineDefinition");
3122
+ function prepareDomainMachineDefinition(definition) {
3123
+ const stable = ensureStableDomainMachineDefinition(definition);
3124
+ preparedDefinitions.add(stable);
3125
+ return stable;
3126
+ }
3127
+ __name(prepareDomainMachineDefinition, "prepareDomainMachineDefinition");
3128
+ function getTransition(definition, state, input) {
3129
+ const transitions = definition.states[state].on;
3130
+ if (!transitions || !hasOwn(transitions, input.type)) return void 0;
3131
+ return transitions[input.type];
3132
+ }
3133
+ __name(getTransition, "getTransition");
3134
+ function validateDomainMachineDefinition(definition) {
3135
+ const candidate = definition;
3136
+ if (!isPlainRecord(candidate)) {
3137
+ throw new InvalidDomainMachineDefinitionError(
3138
+ "Domain machine definition must be a plain object."
3139
+ );
3140
+ }
3141
+ assertDomainMachineDefinitionDataProperties(
3142
+ candidate,
3143
+ DOMAIN_MACHINE_DEFINITION_KEYS
3144
+ );
3145
+ const initial = readDomainMachineDefinitionProperty(candidate, "initial");
3146
+ if (typeof initial !== "string") {
3147
+ throw new InvalidDomainMachineDefinitionError(
3148
+ "Domain machine initial state must be a string data property."
3149
+ );
3150
+ }
3151
+ if (typeof readDomainMachineDefinitionProperty(candidate, "initialContext") !== "function") {
3152
+ throw new InvalidDomainMachineDefinitionError(
3153
+ "Domain machine initialContext must be a function data property."
3154
+ );
3155
+ }
3156
+ const validateSnapshot = readOptionalDomainMachineDefinitionProperty(
3157
+ candidate,
3158
+ "validateSnapshot"
3159
+ );
3160
+ if (validateSnapshot !== void 0 && typeof validateSnapshot !== "function") {
3161
+ throw new InvalidDomainMachineDefinitionError(
3162
+ "Domain machine validateSnapshot must be a function data property."
3163
+ );
3164
+ }
3165
+ const statesCandidate = readDomainMachineDefinitionProperty(
3166
+ candidate,
3167
+ "states"
3168
+ );
3169
+ if (!isPlainRecord(statesCandidate)) {
3170
+ throw new InvalidDomainMachineDefinitionError(
3171
+ "Domain machine states must be a plain object data property."
3172
+ );
3173
+ }
3174
+ const states = statesCandidate;
3175
+ assertDomainMachineDefinitionEntryMap(states, "state");
3176
+ if (!hasOwn(states, initial)) {
3177
+ throw new InvalidDomainMachineDefinitionError(
3178
+ `Initial domain machine state "${initial}" is not defined.`
3179
+ );
3180
+ }
3181
+ for (const state of Object.keys(states)) {
3182
+ const node = readDomainMachineDefinitionProperty(states, state);
3183
+ if (!isPlainRecord(node)) {
3184
+ throw new InvalidDomainMachineDefinitionError(
3185
+ `Domain machine state "${state}" must be a plain object data property.`
3186
+ );
3187
+ }
3188
+ assertDomainMachineDefinitionDataProperties(
3189
+ node,
3190
+ DOMAIN_MACHINE_STATE_NODE_KEYS
3191
+ );
3192
+ const terminal = readOptionalDomainMachineDefinitionProperty(
3193
+ node,
3194
+ "terminal"
3195
+ );
3196
+ if (terminal !== void 0 && typeof terminal !== "boolean") {
3197
+ throw new InvalidDomainMachineDefinitionError(
3198
+ `Domain machine state "${state}" terminal flag must be a boolean.`
3199
+ );
3200
+ }
3201
+ const validateContext = readOptionalDomainMachineDefinitionProperty(node, "validateContext");
3202
+ if (validateContext !== void 0 && typeof validateContext !== "function") {
3203
+ throw new InvalidDomainMachineDefinitionError(
3204
+ `Domain machine state "${state}" validateContext must be a function.`
3205
+ );
3206
+ }
3207
+ const transitions = readOptionalDomainMachineDefinitionProperty(
3208
+ node,
3209
+ "on"
3210
+ );
3211
+ if (transitions !== void 0 && !isPlainRecord(transitions)) {
3212
+ throw new InvalidDomainMachineDefinitionError(
3213
+ `Domain machine state "${state}" transitions must be a plain object.`
3214
+ );
3215
+ }
3216
+ if (isPlainRecord(transitions)) {
3217
+ assertDomainMachineDefinitionEntryMap(transitions, "input");
3218
+ }
3219
+ const inputTypes = Object.keys(transitions ?? {});
3220
+ if (terminal === true && inputTypes.length > 0) {
3221
+ throw new InvalidDomainMachineDefinitionError(
3222
+ `Terminal domain machine state "${state}" cannot declare transitions.`
3223
+ );
3224
+ }
3225
+ for (const inputType of inputTypes) {
3226
+ const transition = readDomainMachineDefinitionProperty(
3227
+ transitions,
3228
+ inputType
3229
+ );
3230
+ if (!isPlainRecord(transition)) {
3231
+ throw new InvalidDomainMachineDefinitionError(
3232
+ `Domain transition from "${state}" on "${inputType}" must be a plain object.`
3233
+ );
3234
+ }
3235
+ assertDomainMachineDefinitionDataProperties(
3236
+ transition,
3237
+ DOMAIN_MACHINE_TRANSITION_KEYS
3238
+ );
3239
+ const target = readDomainMachineDefinitionProperty(
3240
+ transition,
3241
+ "target"
3242
+ );
3243
+ if (typeof target !== "string") {
3244
+ throw new InvalidDomainMachineDefinitionError(
3245
+ `Domain transition from "${state}" on "${inputType}" must target a string state.`
3246
+ );
3247
+ }
3248
+ if (!hasOwn(states, target)) {
3249
+ throw new InvalidDomainMachineDefinitionError(
3250
+ `Domain transition from "${state}" on "${inputType}" targets unknown state "${target}".`
3251
+ );
3252
+ }
3253
+ const guard = readOptionalDomainMachineDefinitionProperty(
3254
+ transition,
3255
+ "guard"
3256
+ );
3257
+ if (guard !== void 0 && typeof guard !== "function") {
3258
+ throw new InvalidDomainMachineDefinitionError(
3259
+ `Domain transition from "${state}" on "${inputType}" guard must be a function.`
3260
+ );
3261
+ }
3262
+ const reduce = readOptionalDomainMachineDefinitionProperty(
3263
+ transition,
3264
+ "reduce"
3265
+ );
3266
+ if (reduce !== void 0 && typeof reduce !== "function") {
3267
+ throw new InvalidDomainMachineDefinitionError(
3268
+ `Domain transition from "${state}" on "${inputType}" reduce must be a function.`
3269
+ );
3270
+ }
3271
+ }
3272
+ }
3273
+ }
3274
+ __name(validateDomainMachineDefinition, "validateDomainMachineDefinition");
3275
+ function assertDomainMachineDefinitionDataProperties(value, allowedKeys) {
3276
+ for (const key of Reflect.ownKeys(value)) {
3277
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
3278
+ if (descriptor !== void 0 && !("value" in descriptor)) {
3279
+ throw new InvalidDomainMachineDefinitionError(
3280
+ "Domain machine definition must contain data properties only."
3281
+ );
3282
+ }
3283
+ if (allowedKeys !== void 0 && !allowedKeys.has(key)) {
3284
+ throw new InvalidDomainMachineDefinitionError(
3285
+ `Domain machine definition contains unknown property "${String(key)}".`
3286
+ );
3287
+ }
3288
+ }
3289
+ }
3290
+ __name(assertDomainMachineDefinitionDataProperties, "assertDomainMachineDefinitionDataProperties");
3291
+ function assertDomainMachineDefinitionEntryMap(value, entryName) {
3292
+ assertDomainMachineDefinitionDataProperties(value);
3293
+ for (const key of Reflect.ownKeys(value)) {
3294
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
3295
+ if (typeof key !== "string" || descriptor?.enumerable !== true) {
3296
+ throw new InvalidDomainMachineDefinitionError(
3297
+ `Domain machine ${entryName} names must be enumerable string properties.`
3298
+ );
3299
+ }
3300
+ }
3301
+ }
3302
+ __name(assertDomainMachineDefinitionEntryMap, "assertDomainMachineDefinitionEntryMap");
3303
+ function readDomainMachineDefinitionProperty(value, key) {
3304
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
3305
+ if (descriptor === void 0 || !("value" in descriptor)) {
3306
+ throw new InvalidDomainMachineDefinitionError(
3307
+ "Domain machine definition must contain data properties only."
3308
+ );
3309
+ }
3310
+ return descriptor.value;
3311
+ }
3312
+ __name(readDomainMachineDefinitionProperty, "readDomainMachineDefinitionProperty");
3313
+ function readOptionalDomainMachineDefinitionProperty(value, key) {
3314
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
3315
+ if (descriptor === void 0) return void 0;
3316
+ if (!("value" in descriptor)) {
3317
+ throw new InvalidDomainMachineDefinitionError(
3318
+ "Domain machine definition must contain data properties only."
3319
+ );
3320
+ }
3321
+ return descriptor.value;
3322
+ }
3323
+ __name(readOptionalDomainMachineDefinitionProperty, "readOptionalDomainMachineDefinitionProperty");
3324
+
3325
+ // src/domain-state-machine/snapshot.ts
3326
+ var DOMAIN_TRANSITION_RESULT_KEYS = /* @__PURE__ */ new Set([
3327
+ "context",
3328
+ "outputs"
3329
+ ]);
3330
+ function prepareDomainMachineSnapshot(definition, snapshot) {
3331
+ validateDomainMachineSnapshot(definition, snapshot);
3332
+ const preparedSnapshot = createDomainMachineSnapshot(
3333
+ snapshot
3334
+ );
3335
+ validateDomainMachineSnapshotInvariant(definition, preparedSnapshot);
3336
+ return preparedSnapshot;
3337
+ }
3338
+ __name(prepareDomainMachineSnapshot, "prepareDomainMachineSnapshot");
3339
+ function createDomainMachineSnapshotFromPreparedContext(state, context) {
3340
+ return Object.freeze({ state, context });
3341
+ }
3342
+ __name(createDomainMachineSnapshotFromPreparedContext, "createDomainMachineSnapshotFromPreparedContext");
3343
+ function createDomainMachineSnapshot(snapshot) {
3344
+ return Object.freeze({
3345
+ state: readDomainMachineSnapshotState(snapshot),
3346
+ context: copyDomainMachineContext(
3347
+ readDomainMachineSnapshotContext(snapshot)
3348
+ )
3349
+ });
3350
+ }
3351
+ __name(createDomainMachineSnapshot, "createDomainMachineSnapshot");
3352
+ function validateDomainMachineSnapshot(definition, snapshot) {
3353
+ if (!isRecord(snapshot)) {
3354
+ throw new InvalidDomainMachineSnapshotError(
3355
+ "Domain machine snapshot must be an object."
3356
+ );
3357
+ }
3358
+ const state = readDomainMachineSnapshotState(snapshot);
3359
+ readDomainMachineSnapshotContext(snapshot);
3360
+ if (!hasOwn(definition.states, state)) {
3361
+ throw new InvalidDomainMachineSnapshotError(
3362
+ `Domain machine snapshot state "${state}" is not defined.`
3363
+ );
3364
+ }
3365
+ }
3366
+ __name(validateDomainMachineSnapshot, "validateDomainMachineSnapshot");
3367
+ function validateDomainMachineSnapshotInvariant(definition, snapshot) {
3368
+ const stateNode = definition.states[snapshot.state];
3369
+ if (stateNode.validateContext !== void 0) {
3370
+ const validContext = stateNode.validateContext({
3371
+ state: snapshot.state,
3372
+ context: snapshot.context
3373
+ });
3374
+ if (typeof validContext !== "boolean") {
3375
+ throw new InvalidDomainMachineDefinitionError(
3376
+ `Domain machine state "${snapshot.state}" validateContext must return a boolean.`
3377
+ );
3378
+ }
3379
+ if (!validContext) {
3380
+ throw new InvalidDomainMachineSnapshotError(
3381
+ `Domain machine snapshot violates the context invariant for state "${snapshot.state}".`
3382
+ );
3383
+ }
3384
+ }
3385
+ if (definition.validateSnapshot === void 0) return;
3386
+ const valid = definition.validateSnapshot(snapshot);
3387
+ if (typeof valid !== "boolean") {
3388
+ throw new InvalidDomainMachineDefinitionError(
3389
+ "Domain machine validateSnapshot must return a boolean."
3390
+ );
3391
+ }
3392
+ if (!valid) {
3393
+ throw new InvalidDomainMachineSnapshotError(
3394
+ `Domain machine snapshot violates invariants for state "${snapshot.state}".`
3395
+ );
3396
+ }
3397
+ }
3398
+ __name(validateDomainMachineSnapshotInvariant, "validateDomainMachineSnapshotInvariant");
3399
+ function readDomainMachineSnapshotState(snapshot) {
3400
+ const stateDescriptor = Object.getOwnPropertyDescriptor(snapshot, "state");
3401
+ if (stateDescriptor === void 0 || !("value" in stateDescriptor) || typeof stateDescriptor.value !== "string") {
3402
+ throw new InvalidDomainMachineSnapshotError(
3403
+ "Domain machine snapshot state must be a string data property."
3404
+ );
3405
+ }
3406
+ return stateDescriptor.value;
3407
+ }
3408
+ __name(readDomainMachineSnapshotState, "readDomainMachineSnapshotState");
3409
+ function readDomainMachineSnapshotContext(snapshot) {
3410
+ const contextDescriptor = Object.getOwnPropertyDescriptor(
3411
+ snapshot,
3412
+ "context"
3413
+ );
3414
+ if (contextDescriptor === void 0 || !("value" in contextDescriptor)) {
3415
+ throw new InvalidDomainMachineSnapshotError(
3416
+ "Domain machine snapshot context must be present as a data property."
3417
+ );
3418
+ }
3419
+ return contextDescriptor.value;
3420
+ }
3421
+ __name(readDomainMachineSnapshotContext, "readDomainMachineSnapshotContext");
3422
+ function validateDomainMachineInput(input) {
3423
+ if (!isDomainMachineInput(input)) {
3424
+ throw new InvalidDomainMachineInputError(
3425
+ "Domain machine input must be an object with a string type."
3426
+ );
3427
+ }
3428
+ }
3429
+ __name(validateDomainMachineInput, "validateDomainMachineInput");
3430
+ function isDomainMachineInput(input) {
3431
+ if (!isRecord(input)) return false;
3432
+ const typeDescriptor = Object.getOwnPropertyDescriptor(input, "type");
3433
+ return typeDescriptor !== void 0 && "value" in typeDescriptor && typeof typeDescriptor.value === "string";
3434
+ }
3435
+ __name(isDomainMachineInput, "isDomainMachineInput");
3436
+ function resolveDomainTransitionGuardResult(result) {
3437
+ if (typeof result === "boolean") return { allowed: result };
3438
+ if (result instanceof DomainError) {
3439
+ return { allowed: false, rejection: result };
3440
+ }
3441
+ throw new InvalidDomainTransitionGuardResultError(
3442
+ "Domain transition guard must return a boolean or DomainError."
3443
+ );
3444
+ }
3445
+ __name(resolveDomainTransitionGuardResult, "resolveDomainTransitionGuardResult");
3446
+ function validateDomainTransitionResult(result) {
3447
+ if (result === void 0) return;
3448
+ if (!isPlainRecord(result)) {
3449
+ throw new InvalidDomainTransitionResultError(
3450
+ "Domain transition result must be a plain object when returned."
3451
+ );
3452
+ }
3453
+ for (const key of Reflect.ownKeys(result)) {
3454
+ if (!DOMAIN_TRANSITION_RESULT_KEYS.has(key)) {
3455
+ throw new InvalidDomainTransitionResultError(
3456
+ `Domain transition result contains unknown property "${String(key)}".`
3457
+ );
3458
+ }
3459
+ const descriptor = Object.getOwnPropertyDescriptor(result, key);
3460
+ if (descriptor !== void 0 && !("value" in descriptor)) {
3461
+ throw new InvalidDomainTransitionResultError(
3462
+ "Domain transition result must contain data properties only."
3463
+ );
3464
+ }
3465
+ }
3466
+ const outputs = readDomainTransitionResultOutputs(result);
3467
+ if (outputs !== void 0 && !Array.isArray(outputs)) {
3468
+ throw new InvalidDomainTransitionResultError(
3469
+ "Domain transition result outputs must be an array when provided."
3470
+ );
3471
+ }
3472
+ }
3473
+ __name(validateDomainTransitionResult, "validateDomainTransitionResult");
3474
+ function readDomainTransitionResultContext(result) {
3475
+ if (result === void 0) return { hasContext: false };
3476
+ const contextDescriptor = Object.getOwnPropertyDescriptor(result, "context");
3477
+ if (contextDescriptor === void 0) return { hasContext: false };
3478
+ if (!("value" in contextDescriptor)) {
3479
+ throw new InvalidDomainTransitionResultError(
3480
+ "Domain transition result context must be a data property when provided."
3481
+ );
3482
+ }
3483
+ return { hasContext: true, context: contextDescriptor.value };
3484
+ }
3485
+ __name(readDomainTransitionResultContext, "readDomainTransitionResultContext");
3486
+ function readDomainTransitionResultOutputs(result) {
3487
+ if (result === void 0) return void 0;
3488
+ const outputsDescriptor = Object.getOwnPropertyDescriptor(result, "outputs");
3489
+ if (outputsDescriptor === void 0) return void 0;
3490
+ if (!("value" in outputsDescriptor)) {
3491
+ throw new InvalidDomainTransitionResultError(
3492
+ "Domain transition result outputs must be a data property when provided."
3493
+ );
3494
+ }
3495
+ return outputsDescriptor.value;
3496
+ }
3497
+ __name(readDomainTransitionResultOutputs, "readDomainTransitionResultOutputs");
3498
+
3499
+ // src/domain-state-machine/transition.ts
3500
+ function createInitialDomainMachineSnapshot(definition) {
3501
+ const stableDefinition = ensureStableDomainMachineDefinition(definition);
3502
+ return createInitialDomainMachineSnapshotFromPrepared(stableDefinition);
3503
+ }
3504
+ __name(createInitialDomainMachineSnapshot, "createInitialDomainMachineSnapshot");
3505
+ function createInitialDomainMachineSnapshotFromPrepared(definition) {
3506
+ const snapshot = createDomainMachineSnapshot({
3507
+ state: definition.initial,
3508
+ context: definition.initialContext()
3509
+ });
3510
+ validateDomainMachineSnapshotInvariant(definition, snapshot);
3511
+ return snapshot;
3512
+ }
3513
+ __name(createInitialDomainMachineSnapshotFromPrepared, "createInitialDomainMachineSnapshotFromPrepared");
3514
+ function canTransitionDomainState(definition, snapshot, input) {
3515
+ const stableDefinition = ensureStableDomainMachineDefinition(definition);
3516
+ const currentSnapshot = prepareDomainMachineSnapshot(
3517
+ stableDefinition,
3518
+ snapshot
3519
+ );
3520
+ return canTransitionPreparedDomainState(
3521
+ stableDefinition,
3522
+ currentSnapshot,
3523
+ input
3524
+ );
3525
+ }
3526
+ __name(canTransitionDomainState, "canTransitionDomainState");
3527
+ function canTransitionPreparedDomainState(definition, snapshot, input) {
3528
+ if (!isDomainMachineInput(input)) return false;
3529
+ const stateNode = definition.states[snapshot.state];
3530
+ if (stateNode.terminal === true) return false;
3531
+ const transition = getTransition(definition, snapshot.state, input);
3532
+ if (!transition) return false;
3533
+ const currentInput = copyDomainMachineInput(input);
3534
+ if (!transition.guard) return true;
3535
+ const guardResult = transition.guard({
3536
+ state: snapshot.state,
3537
+ context: snapshot.context,
3538
+ input: currentInput
3539
+ });
3540
+ return resolveDomainTransitionGuardResult(guardResult).allowed;
3541
+ }
3542
+ __name(canTransitionPreparedDomainState, "canTransitionPreparedDomainState");
3543
+ function transitionDomainState(definition, snapshot, input) {
3544
+ const stableDefinition = ensureStableDomainMachineDefinition(definition);
3545
+ const currentSnapshot = prepareDomainMachineSnapshot(
3546
+ stableDefinition,
3547
+ snapshot
3548
+ );
3549
+ return transitionPreparedDomainState(
3550
+ stableDefinition,
3551
+ currentSnapshot,
3552
+ input
3553
+ );
3554
+ }
3555
+ __name(transitionDomainState, "transitionDomainState");
3556
+ function transitionPreparedDomainState(definition, snapshot, input) {
3557
+ validateDomainMachineInput(input);
3558
+ const from = snapshot.state;
3559
+ const stateNode = definition.states[from];
3560
+ const transition = stateNode.terminal === true ? void 0 : getTransition(definition, from, input);
3561
+ if (!transition) {
3562
+ throw new InvalidDomainTransitionError(from, input.type);
3563
+ }
3564
+ const currentInput = copyDomainMachineInput(input);
3565
+ const guardResult = transition.guard === void 0 ? true : transition.guard({
3566
+ state: from,
3567
+ context: snapshot.context,
3568
+ input: currentInput
3569
+ });
3570
+ const guardDecision = resolveDomainTransitionGuardResult(guardResult);
3571
+ if (!guardDecision.allowed) {
3572
+ if (guardDecision.rejection !== void 0) {
3573
+ throw guardDecision.rejection;
3574
+ }
3575
+ throw new DomainTransitionGuardRejectedError(from, currentInput.type);
3576
+ }
3577
+ const result = transition.reduce?.({
3578
+ state: from,
3579
+ context: snapshot.context,
3580
+ input: currentInput
3581
+ });
3582
+ validateDomainTransitionResult(result);
3583
+ const contextResult = readDomainTransitionResultContext(result);
3584
+ const nextContext = contextResult.hasContext ? contextResult.context : snapshot.context;
3585
+ const nextSnapshot = nextContext === snapshot.context ? createDomainMachineSnapshotFromPreparedContext(
3586
+ transition.target,
3587
+ snapshot.context
3588
+ ) : createDomainMachineSnapshot({
3589
+ state: transition.target,
3590
+ context: nextContext
3591
+ });
3592
+ validateDomainMachineSnapshotInvariant(definition, nextSnapshot);
3593
+ return {
3594
+ from,
3595
+ to: transition.target,
3596
+ snapshot: nextSnapshot,
3597
+ outputs: copyDomainMachineOutputs(
3598
+ readDomainTransitionResultOutputs(result)
3599
+ )
3600
+ };
3601
+ }
3602
+ __name(transitionPreparedDomainState, "transitionPreparedDomainState");
3603
+
3604
+ // src/domain-state-machine/analyzer.ts
3605
+ function analyzeDomainMachineDefinition(definition) {
3606
+ validateDomainMachineDefinition(definition);
3607
+ const stableDefinition = copyDomainMachineDefinition(definition);
3608
+ const states = Object.keys(stableDefinition.states).sort(
3609
+ compareStrings
3610
+ );
3611
+ const outgoing = /* @__PURE__ */ new Map();
3612
+ const incoming = /* @__PURE__ */ new Map();
3613
+ const transitions = [];
3614
+ for (const state of states) {
3615
+ outgoing.set(state, []);
3616
+ incoming.set(state, []);
3617
+ }
3618
+ for (const state of states) {
3619
+ const stateTransitions = stableDefinition.states[state].on;
3620
+ const inputTypes = Object.keys(stateTransitions ?? {}).sort(compareStrings);
3621
+ for (const inputType of inputTypes) {
3622
+ const transition = stateTransitions?.[inputType];
3623
+ if (transition === void 0) continue;
3624
+ outgoing.get(state)?.push(transition.target);
3625
+ incoming.get(transition.target)?.push(state);
3626
+ transitions.push(
3627
+ Object.freeze({
3628
+ state,
3629
+ inputType,
3630
+ target: transition.target,
3631
+ guarded: transition.guard !== void 0
3632
+ })
3633
+ );
3634
+ }
3635
+ }
3636
+ const structurallyReachable = visitGraph(
3637
+ [stableDefinition.initial],
3638
+ outgoing
3639
+ );
3640
+ const terminalStates = states.filter(
3641
+ (state) => stableDefinition.states[state].terminal === true
3642
+ );
3643
+ const statesWithTerminalPath = visitGraph(terminalStates, incoming);
3644
+ const diagnostics = [];
3645
+ for (const state of states) {
3646
+ if (!structurallyReachable.has(state)) {
3647
+ diagnostics.push(Object.freeze({ code: "unreachable-state", state }));
3648
+ }
3649
+ }
3650
+ for (const state of states) {
3651
+ if (stableDefinition.states[state].terminal !== true && outgoing.get(state)?.length === 0) {
3652
+ diagnostics.push(Object.freeze({ code: "structural-dead-end", state }));
3653
+ }
3654
+ }
3655
+ for (const state of states) {
3656
+ if (!statesWithTerminalPath.has(state)) {
3657
+ diagnostics.push(Object.freeze({ code: "no-terminal-path", state }));
3658
+ }
3659
+ }
3660
+ return Object.freeze({
3661
+ diagnostics: Object.freeze(diagnostics),
3662
+ transitions: Object.freeze(transitions),
3663
+ structurallyReachableStates: Object.freeze(
3664
+ states.filter((state) => structurallyReachable.has(state))
3665
+ ),
3666
+ statesWithTerminalPath: Object.freeze(
3667
+ states.filter((state) => statesWithTerminalPath.has(state))
3668
+ )
3669
+ });
3670
+ }
3671
+ __name(analyzeDomainMachineDefinition, "analyzeDomainMachineDefinition");
3672
+ function visitGraph(startStates, edges) {
3673
+ const visited = /* @__PURE__ */ new Set();
3674
+ const pending = [...startStates];
3675
+ while (pending.length > 0) {
3676
+ const state = pending.pop();
3677
+ if (state === void 0 || visited.has(state)) continue;
3678
+ visited.add(state);
3679
+ for (const next of edges.get(state) ?? []) {
3680
+ if (!visited.has(next)) pending.push(next);
3681
+ }
3682
+ }
3683
+ return visited;
3684
+ }
3685
+ __name(visitGraph, "visitGraph");
3686
+ function compareStrings(left, right) {
3687
+ return left < right ? -1 : left > right ? 1 : 0;
3688
+ }
3689
+ __name(compareStrings, "compareStrings");
3690
+
3691
+ // src/domain-state-machine/domain-state-machine.ts
3692
+ var DomainStateMachine = class {
3693
+ static {
3694
+ __name(this, "DomainStateMachine");
3695
+ }
3696
+ definition;
3697
+ #snapshot;
3698
+ #evaluating = false;
3699
+ constructor(definition, ...snapshotInput) {
3700
+ this.definition = ensureStableDomainMachineDefinition(definition);
3701
+ const [suppliedSnapshot] = snapshotInput;
3702
+ if (suppliedSnapshot !== void 0) {
3703
+ validateDomainMachineSnapshot(this.definition, suppliedSnapshot);
3704
+ this.#snapshot = createDomainMachineSnapshot(
3705
+ suppliedSnapshot
3706
+ );
3707
+ validateDomainMachineSnapshotInvariant(this.definition, this.#snapshot);
3708
+ } else {
3709
+ this.#snapshot = createInitialDomainMachineSnapshotFromPrepared(
3710
+ this.definition
3711
+ );
3712
+ }
3713
+ }
3714
+ /** Returns a defensive, deeply frozen copy of the current snapshot. */
3715
+ get snapshot() {
3716
+ return createDomainMachineSnapshot(this.#snapshot);
3717
+ }
3718
+ /** Current named control state. */
3719
+ get state() {
3720
+ return this.#snapshot.state;
3721
+ }
3722
+ /** Current deeply readonly context. */
3723
+ get context() {
3724
+ return this.#snapshot.context;
3725
+ }
3726
+ /** Whether the current state permanently forbids outgoing transitions. */
3727
+ isTerminal() {
3728
+ return this.definition.states[this.state].terminal === true;
3729
+ }
3730
+ /** Checks a transition without changing the current snapshot. */
3731
+ can(input) {
3732
+ return this.evaluate(
3733
+ () => canTransitionPreparedDomainState(this.definition, this.#snapshot, input)
3734
+ );
3735
+ }
3736
+ /** Applies an input and advances the current snapshot on success. */
3737
+ dispatch(input) {
3738
+ return this.evaluate(() => {
3739
+ const result = transitionPreparedDomainState(
3740
+ this.definition,
3741
+ this.#snapshot,
3742
+ input
3743
+ );
3744
+ this.#snapshot = result.snapshot;
3745
+ return result;
3746
+ });
3747
+ }
3748
+ evaluate(operation) {
3749
+ if (this.#evaluating) {
3750
+ throw new ReentrantDomainStateMachineEvaluationError();
3751
+ }
3752
+ this.#evaluating = true;
3753
+ try {
3754
+ return operation();
3755
+ } finally {
3756
+ this.#evaluating = false;
3757
+ }
3758
+ }
3759
+ };
3760
+
3761
+ // src/events/event-bus.ts
3762
+ var EventBusImpl = class {
3763
+ static {
3764
+ __name(this, "EventBusImpl");
3765
+ }
3766
+ handlers = /* @__PURE__ */ new Map();
3767
+ subscribe(eventType, handler) {
3768
+ const type = eventType;
3769
+ if (!this.handlers.has(type)) {
3770
+ this.handlers.set(type, []);
3771
+ }
3772
+ const handlersForType = this.handlers.get(type);
3773
+ const casted = handler;
3774
+ handlersForType.push(casted);
3775
+ let removed = false;
3776
+ return () => {
3777
+ if (removed) return;
3778
+ const idx = handlersForType.indexOf(casted);
3779
+ if (idx !== -1) {
3780
+ handlersForType.splice(idx, 1);
3781
+ removed = true;
3782
+ }
3783
+ if (handlersForType.length === 0) {
3784
+ this.handlers.delete(type);
3785
+ }
3786
+ };
3787
+ }
3788
+ once(eventType, options) {
3789
+ return new Promise((resolve, reject) => {
3790
+ if (options?.signal?.aborted) {
3791
+ reject(abortReason(options.signal, "EventBus.once aborted"));
3792
+ return;
3793
+ }
3794
+ let timer;
2241
3795
  let settled = false;
2242
3796
  let abortListener;
2243
3797
  const cleanup = /* @__PURE__ */ __name(() => {
@@ -2319,21 +3873,98 @@ var InMemoryOutbox = class {
2319
3873
  __name(this, "InMemoryOutbox");
2320
3874
  }
2321
3875
  pending = /* @__PURE__ */ new Map();
3876
+ dead = /* @__PURE__ */ new Map();
3877
+ maxDeliveryAttempts;
3878
+ constructor(options) {
3879
+ const max = options?.maxDeliveryAttempts ?? 5;
3880
+ if (!Number.isInteger(max) || max < 1) {
3881
+ throw new Error(
3882
+ `InMemoryOutbox: maxDeliveryAttempts must be an integer >= 1, got ${max}`
3883
+ );
3884
+ }
3885
+ this.maxDeliveryAttempts = max;
3886
+ }
2322
3887
  async add(events) {
2323
3888
  for (const event of events) {
3889
+ if (this.dead.has(event.eventId)) {
3890
+ this.dead.delete(event.eventId);
3891
+ this.pending.set(event.eventId, {
3892
+ dispatchId: event.eventId,
3893
+ event,
3894
+ attempts: 0
3895
+ });
3896
+ continue;
3897
+ }
3898
+ const existing = this.pending.get(event.eventId);
3899
+ if (existing) {
3900
+ existing.event = event;
3901
+ continue;
3902
+ }
2324
3903
  this.pending.set(event.eventId, {
2325
3904
  dispatchId: event.eventId,
2326
- event
3905
+ event,
3906
+ attempts: 0
2327
3907
  });
2328
3908
  }
2329
3909
  }
2330
3910
  async getPending(limit) {
2331
- const all = [...this.pending.values()];
3911
+ const all = [...this.pending.values()].map((record) => ({
3912
+ dispatchId: record.dispatchId,
3913
+ event: record.event,
3914
+ attempts: record.attempts
3915
+ }));
2332
3916
  if (typeof limit !== "number") return all;
2333
3917
  return all.slice(0, Math.max(0, limit));
2334
3918
  }
2335
3919
  async markDispatched(dispatchIds) {
2336
- for (const id of dispatchIds) this.pending.delete(id);
3920
+ for (const id of dispatchIds) {
3921
+ this.pending.delete(id);
3922
+ this.dead.delete(id);
3923
+ }
3924
+ }
3925
+ async markFailed(dispatchId, error) {
3926
+ const record = this.pending.get(dispatchId);
3927
+ if (!record) return;
3928
+ record.attempts += 1;
3929
+ record.lastError = error instanceof Error ? error.message : String(error ?? "unknown");
3930
+ if (record.attempts >= this.maxDeliveryAttempts) {
3931
+ this.pending.delete(dispatchId);
3932
+ this.dead.set(dispatchId, {
3933
+ dispatchId: record.dispatchId,
3934
+ event: record.event,
3935
+ attempts: record.attempts,
3936
+ lastError: record.lastError
3937
+ });
3938
+ }
3939
+ }
3940
+ async deadLetters() {
3941
+ return [...this.dead.values()].map((record) => ({ ...record }));
3942
+ }
3943
+ };
3944
+
3945
+ // src/repo/in-memory-event-store.ts
3946
+ var InMemoryEventStore = class {
3947
+ static {
3948
+ __name(this, "InMemoryEventStore");
3949
+ }
3950
+ streams = /* @__PURE__ */ new Map();
3951
+ async append(streamId, events, options) {
3952
+ if (events.length === 0) return;
3953
+ const stream = this.streams.get(streamId) ?? [];
3954
+ if (stream.length !== options.expectedVersion) {
3955
+ throw new ConcurrencyConflictError({
3956
+ aggregateType: events[0]?.aggregateType ?? "stream",
3957
+ aggregateId: streamId,
3958
+ expectedVersion: options.expectedVersion,
3959
+ actualVersion: stream.length
3960
+ });
3961
+ }
3962
+ this.streams.set(streamId, [...stream, ...events]);
3963
+ }
3964
+ async readStream(streamId, options) {
3965
+ const stream = this.streams.get(streamId) ?? [];
3966
+ const fromVersion = options?.fromVersion ?? 0;
3967
+ return stream.slice(Math.max(0, fromVersion));
2337
3968
  }
2338
3969
  };
2339
3970
  var DEFAULT_MAX_ATTEMPTS = 3;
@@ -2424,7 +4055,7 @@ var RetryingTransactionScope = class {
2424
4055
  maxDelayMs: this.maxDelayMs,
2425
4056
  random: this.random
2426
4057
  });
2427
- this.onRetry?.({ attempt, error, delayMs });
4058
+ reportToObserver(() => this.onRetry?.({ attempt, error, delayMs }));
2428
4059
  await sleep(delayMs, signal);
2429
4060
  }
2430
4061
  }
@@ -2438,6 +4069,6 @@ function voValidated(t, validate, message = "Validation failed") {
2438
4069
  }
2439
4070
  __name(voValidated, "voValidated");
2440
4071
 
2441
- export { AggregateDeletedError, AggregateNotFoundError, AggregateRoot, CommandBus, CommitError, ConcurrencyConflictError, DomainError, DuplicateAggregateError, Entity, EventBusImpl, EventHarvestError, EventSourcedAggregate, IdentityMap, InMemoryOutbox, InfrastructureError, MissingHandlerError, NestedUnitOfWorkError, QueryBus, RetryingTransactionScope, RollbackError, TransactionClosedError, UnenrolledChangesError, UnitOfWork, ValueObject, computeBackoffDelay, copyMetadata, createDomainEvent, deepEqual, deepEqualExcept, deepFreeze, deepOmit, entityIds, findEntityById, freezeShallow, hasEntityId, mergeMetadata, removeEntityById, replaceEntityById, resetClockFactory, resetEventIdFactory, sameEntity, sameVersion, setClockFactory, setEventIdFactory, updateEntityById, vo, voEquals, voEqualsExcept, voValidated, voWithValidation, withClockFactory, withCommit, withEventIdFactory };
4072
+ export { AggregateDeletedError, AggregateNotFoundError, AggregateRoot, CommandBus, CommitError, ConcurrencyConflictError, DomainError, DomainStateMachine, DomainTransitionGuardRejectedError, DuplicateAggregateError, Entity, EventBusImpl, EventHarvestError, EventSourcedAggregate, IdentityMap, InMemoryEventStore, InMemoryOutbox, InfrastructureError, InvalidDomainMachineContextError, InvalidDomainMachineDefinitionError, InvalidDomainMachineInputError, InvalidDomainMachineSnapshotError, InvalidDomainTransitionError, InvalidDomainTransitionGuardResultError, InvalidDomainTransitionResultError, MissingHandlerError, NestedUnitOfWorkError, QueryBus, ReentrantDomainStateMachineEvaluationError, RetryingTransactionScope, RollbackError, SnapshotSchemaMismatchError, TransactionClosedError, UnenrolledChangesError, UnitOfWork, UnregisteredHandlerError, UnreplayableAggregateError, ValueObject, analyzeDomainMachineDefinition, canTransitionDomainState, computeBackoffDelay, copyMetadata, createDomainEvent, createInitialDomainMachineSnapshot, deepEqual, deepEqualExcept, deepFreeze, deepOmit, entityIds, findEntityById, freezeShallow, hasEntityId, mergeMetadata, prepareDomainMachineDefinition, removeEntityById, replaceEntityById, resetClockFactory, resetEventIdFactory, sameEntity, sameVersion, setClockFactory, setEventIdFactory, transitionDomainState, updateEntityById, vo, voEquals, voEqualsExcept, voValidated, voWithValidation, withClockFactory, withCommit, withEventIdFactory };
2442
4073
  //# sourceMappingURL=index.js.map
2443
4074
  //# sourceMappingURL=index.js.map