@shirudo/ddd-kit 1.0.0-rc.1 → 1.0.0-rc.4

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
@@ -1,85 +1,30 @@
1
+ import { err, ok } from '@shirudo/result';
2
+ import { BaseError } from '@shirudo/base-error';
3
+
1
4
  var __defProp = Object.defineProperty;
2
5
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
6
 
4
- // src/aggregate/domain-event.ts
5
- function createDomainEvent(type, payload, options) {
6
- return {
7
- type,
8
- payload,
9
- occurredAt: options?.occurredAt ?? /* @__PURE__ */ new Date(),
10
- version: options?.version ?? 1,
11
- metadata: options?.metadata
12
- };
13
- }
14
- __name(createDomainEvent, "createDomainEvent");
15
- function createDomainEventWithMetadata(type, payload, metadata, options) {
16
- return createDomainEvent(type, payload, {
17
- ...options,
18
- metadata
19
- });
20
- }
21
- __name(createDomainEventWithMetadata, "createDomainEventWithMetadata");
22
- function copyMetadata(sourceEvent, additionalMetadata) {
23
- return {
24
- ...sourceEvent.metadata ?? {},
25
- ...additionalMetadata ?? {}
26
- };
27
- }
28
- __name(copyMetadata, "copyMetadata");
29
- function mergeMetadata(...metadataObjects) {
30
- return Object.assign({}, ...metadataObjects.filter(Boolean));
31
- }
32
- __name(mergeMetadata, "mergeMetadata");
33
-
34
- // src/aggregate/aggregate.ts
35
- function aggregate(state, version = 0) {
36
- return { state, version };
37
- }
38
- __name(aggregate, "aggregate");
39
- function bump(agg) {
40
- return { ...agg, version: agg.version + 1 };
41
- }
42
- __name(bump, "bump");
43
- function sameVersion(a, b) {
44
- return a.id === b.id && a.version === b.version;
45
- }
46
- __name(sameVersion, "sameVersion");
47
-
48
7
  // src/utils/array/is-built-in.ts
8
+ var BUILT_IN_TAGS = /* @__PURE__ */ new Set([
9
+ "[object Date]",
10
+ "[object RegExp]",
11
+ "[object Map]",
12
+ "[object Set]",
13
+ "[object WeakMap]",
14
+ "[object WeakSet]",
15
+ "[object Promise]",
16
+ "[object Error]",
17
+ "[object Boolean]",
18
+ "[object Number]",
19
+ "[object String]",
20
+ "[object ArrayBuffer]",
21
+ "[object SharedArrayBuffer]",
22
+ "[object DataView]"
23
+ ]);
49
24
  function isBuiltInObject(obj, tag) {
50
- if (tag.endsWith("Array]")) {
51
- return true;
52
- }
53
- if (ArrayBuffer.isView(obj)) {
54
- return true;
55
- }
56
- if (tag === "[object ArrayBuffer]" || tag === "[object SharedArrayBuffer]") {
57
- return true;
58
- }
59
- const objConstructor = obj.constructor;
60
- if (objConstructor && typeof objConstructor === "function") {
61
- const constructorName = objConstructor.name;
62
- if (constructorName && typeof globalThis !== "undefined" && constructorName in globalThis && globalThis[constructorName] === objConstructor) {
63
- const proto = Object.getPrototypeOf(obj);
64
- if (proto !== Object.prototype && proto !== null) {
65
- return true;
66
- }
67
- }
68
- }
69
- const knownBuiltInTags = /* @__PURE__ */ new Set([
70
- "[object Date]",
71
- "[object RegExp]",
72
- "[object Map]",
73
- "[object Set]",
74
- "[object WeakMap]",
75
- "[object WeakSet]",
76
- "[object Promise]",
77
- "[object Error]",
78
- "[object Boolean]",
79
- "[object Number]",
80
- "[object String]"
81
- ]);
82
- return knownBuiltInTags.has(tag);
25
+ if (tag.endsWith("Array]")) return true;
26
+ if (ArrayBuffer.isView(obj)) return true;
27
+ return BUILT_IN_TAGS.has(tag);
83
28
  }
84
29
  __name(isBuiltInObject, "isBuiltInObject");
85
30
 
@@ -103,11 +48,15 @@ function deepEqualInner(a, b, visited) {
103
48
  }
104
49
  const objA = a;
105
50
  const objB = b;
106
- const cached = visited.get(objA);
107
- if (cached !== void 0) {
108
- return cached === objB;
51
+ let cachedBs = visited.get(objA);
52
+ if (cachedBs?.has(objB)) {
53
+ return true;
54
+ }
55
+ if (!cachedBs) {
56
+ cachedBs = /* @__PURE__ */ new WeakSet();
57
+ visited.set(objA, cachedBs);
109
58
  }
110
- visited.set(objA, objB);
59
+ cachedBs.add(objB);
111
60
  if (ArrayBuffer.isView(objA) || ArrayBuffer.isView(objB)) {
112
61
  if (!ArrayBuffer.isView(objA) || !ArrayBuffer.isView(objB)) return false;
113
62
  const tagA2 = objToString.call(objA);
@@ -185,25 +134,28 @@ function deepEqualInner(a, b, visited) {
185
134
  if (isBuiltInObject(objA, tagA) && isBuiltInObject(objB, tagB)) {
186
135
  return objA === objB;
187
136
  }
137
+ const recA = objA;
138
+ const recB = objB;
188
139
  const stringKeysA = Object.keys(objA);
189
140
  const stringKeysB = Object.keys(objB);
141
+ if (stringKeysA.length !== stringKeysB.length) return false;
190
142
  const symbolKeysA = Object.getOwnPropertySymbols(objA);
191
143
  const symbolKeysB = Object.getOwnPropertySymbols(objB);
192
- if (stringKeysA.length !== stringKeysB.length) return false;
193
144
  if (symbolKeysA.length !== symbolKeysB.length) return false;
145
+ const symbolKeysBSet = new Set(symbolKeysB);
194
146
  for (const key of stringKeysA) {
195
147
  if (!objHasOwn.call(objB, key)) return false;
196
148
  }
197
149
  for (const key of symbolKeysA) {
198
- if (!Object.getOwnPropertySymbols(objB).includes(key)) return false;
150
+ if (!symbolKeysBSet.has(key)) return false;
199
151
  }
200
152
  for (const key of stringKeysA) {
201
- if (!deepEqualInner(objA[key], objB[key], visited)) {
153
+ if (!deepEqualInner(recA[key], recB[key], visited)) {
202
154
  return false;
203
155
  }
204
156
  }
205
157
  for (const key of symbolKeysA) {
206
- if (!deepEqualInner(objA[key], objB[key], visited)) {
158
+ if (!deepEqualInner(recA[key], recB[key], visited)) {
207
159
  return false;
208
160
  }
209
161
  }
@@ -213,6 +165,293 @@ function deepEqualInner(a, b, visited) {
213
165
  }
214
166
  __name(deepEqualInner, "deepEqualInner");
215
167
 
168
+ // src/utils/array/deep-omit.ts
169
+ function deepOmit(value, options) {
170
+ const visited = /* @__PURE__ */ new WeakMap();
171
+ const ignoreKeys = options.ignoreKeys ? new Set(options.ignoreKeys) : void 0;
172
+ return omitInternal(value, options, ignoreKeys, [], visited);
173
+ }
174
+ __name(deepOmit, "deepOmit");
175
+ function omitInternal(value, options, ignoreKeys, path, visited) {
176
+ if (value === null) return value;
177
+ if (typeof value !== "object") return value;
178
+ const obj = value;
179
+ if (visited.has(obj)) {
180
+ return visited.get(obj);
181
+ }
182
+ const tag = Object.prototype.toString.call(obj);
183
+ if (tag === "[object Array]") {
184
+ const arr = obj;
185
+ const clone2 = new Array(arr.length);
186
+ visited.set(obj, clone2);
187
+ for (let i = 0; i < arr.length; i++) {
188
+ path.push(i);
189
+ clone2[i] = omitInternal(arr[i], options, ignoreKeys, path, visited);
190
+ path.pop();
191
+ }
192
+ return clone2;
193
+ }
194
+ if (isBuiltInObject(obj, tag)) {
195
+ const builtInClone = cloneBuiltIn(obj, tag);
196
+ visited.set(obj, builtInClone);
197
+ return builtInClone;
198
+ }
199
+ const clone = Object.create(Object.getPrototypeOf(obj));
200
+ visited.set(obj, clone);
201
+ const stringKeys = Object.keys(obj);
202
+ const symbolKeys = Object.getOwnPropertySymbols(obj);
203
+ for (const key of stringKeys) {
204
+ if (shouldIgnoreKey(key, path, ignoreKeys, options)) continue;
205
+ path.push(key);
206
+ assignOwn(
207
+ clone,
208
+ key,
209
+ omitInternal(
210
+ obj[key],
211
+ options,
212
+ ignoreKeys,
213
+ path,
214
+ visited
215
+ )
216
+ );
217
+ path.pop();
218
+ }
219
+ for (const key of symbolKeys) {
220
+ if (shouldIgnoreKey(key, path, ignoreKeys, options)) continue;
221
+ path.push(key);
222
+ assignOwn(
223
+ clone,
224
+ key,
225
+ omitInternal(
226
+ obj[key],
227
+ options,
228
+ ignoreKeys,
229
+ path,
230
+ visited
231
+ )
232
+ );
233
+ path.pop();
234
+ }
235
+ return clone;
236
+ }
237
+ __name(omitInternal, "omitInternal");
238
+ function assignOwn(target, key, value) {
239
+ Object.defineProperty(target, key, {
240
+ value,
241
+ writable: true,
242
+ enumerable: true,
243
+ configurable: true
244
+ });
245
+ }
246
+ __name(assignOwn, "assignOwn");
247
+ function cloneBuiltIn(obj, tag) {
248
+ switch (tag) {
249
+ case "[object Date]":
250
+ return new Date(obj.getTime());
251
+ case "[object RegExp]": {
252
+ const re = obj;
253
+ const copy = new RegExp(re.source, re.flags);
254
+ copy.lastIndex = re.lastIndex;
255
+ return copy;
256
+ }
257
+ case "[object Map]": {
258
+ const m = obj;
259
+ return new Map(m);
260
+ }
261
+ case "[object Set]": {
262
+ const s = obj;
263
+ return new Set(s);
264
+ }
265
+ default:
266
+ return structuredClone(obj);
267
+ }
268
+ }
269
+ __name(cloneBuiltIn, "cloneBuiltIn");
270
+ function shouldIgnoreKey(key, path, ignoreKeys, options) {
271
+ if (ignoreKeys?.has(key)) return true;
272
+ if (options.ignoreKeyPredicate?.(key, path)) return true;
273
+ return false;
274
+ }
275
+ __name(shouldIgnoreKey, "shouldIgnoreKey");
276
+
277
+ // src/utils/array/deep-equal-except.ts
278
+ function deepEqualExcept(a, b, options) {
279
+ const prunedA = deepOmit(a, options);
280
+ const prunedB = deepOmit(b, options);
281
+ return deepEqual(prunedA, prunedB);
282
+ }
283
+ __name(deepEqualExcept, "deepEqualExcept");
284
+ function deepFreeze(obj, visited = /* @__PURE__ */ new WeakSet()) {
285
+ if (obj === null || typeof obj !== "object") {
286
+ return obj;
287
+ }
288
+ if (visited.has(obj)) {
289
+ return obj;
290
+ }
291
+ visited.add(obj);
292
+ const keys = Reflect.ownKeys(obj);
293
+ for (const key of keys) {
294
+ const value = obj[key];
295
+ if (value !== null && typeof value === "object") {
296
+ deepFreeze(value, visited);
297
+ }
298
+ }
299
+ return Object.freeze(obj);
300
+ }
301
+ __name(deepFreeze, "deepFreeze");
302
+ function vo(t) {
303
+ return deepFreeze(structuredClone(t));
304
+ }
305
+ __name(vo, "vo");
306
+ function voEquals(a, b) {
307
+ return deepEqual(a, b);
308
+ }
309
+ __name(voEquals, "voEquals");
310
+ function voEqualsExcept(a, b, options) {
311
+ return deepEqualExcept(a, b, options);
312
+ }
313
+ __name(voEqualsExcept, "voEqualsExcept");
314
+ function voWithValidation(t, validate, errorMessage) {
315
+ if (!validate(t)) {
316
+ return err(
317
+ errorMessage ?? `Validation failed for value object: ${JSON.stringify(t)}`
318
+ );
319
+ }
320
+ return ok(vo(t));
321
+ }
322
+ __name(voWithValidation, "voWithValidation");
323
+ var ValueObject = class {
324
+ static {
325
+ __name(this, "ValueObject");
326
+ }
327
+ props;
328
+ /**
329
+ * Creates a new ValueObject.
330
+ * The properties are deeply frozen to ensure immutability.
331
+ *
332
+ * @param props - The properties of the value object
333
+ * @example
334
+ * ```ts
335
+ * class Money extends ValueObject<{ amount: number; currency: string }> {
336
+ * constructor(props: { amount: number; currency: string }) {
337
+ * super(props);
338
+ * }
339
+ *
340
+ * protected validate(props: { amount: number; currency: string }): void {
341
+ * if (props.amount < 0) throw new Error("Amount cannot be negative");
342
+ * }
343
+ * }
344
+ * ```
345
+ */
346
+ constructor(props) {
347
+ this.validate(props);
348
+ this.props = deepFreeze({ ...props });
349
+ }
350
+ /**
351
+ * Optional validation hook that can be overridden by subclasses.
352
+ * Should throw an error if validation fails.
353
+ *
354
+ * @param props - The properties to validate
355
+ * @throws Error if validation fails
356
+ */
357
+ validate(props) {
358
+ }
359
+ /**
360
+ * Checks if this value object is equal to another.
361
+ * Uses deep equality comparison on the properties and checks for constructor equality.
362
+ *
363
+ * @param other - The other value object to compare
364
+ * @returns true if the properties are deeply equal and constructors match
365
+ */
366
+ equals(other) {
367
+ if (other === null || other === void 0) {
368
+ return false;
369
+ }
370
+ if (this.constructor !== other.constructor) {
371
+ return false;
372
+ }
373
+ return deepEqual(this.props, other.props);
374
+ }
375
+ /**
376
+ * Creates a clone of the value object with optional property overrides.
377
+ *
378
+ * @param props - Optional properties to override
379
+ * @returns A new instance of the value object
380
+ */
381
+ clone(props) {
382
+ const Constructor = this.constructor;
383
+ return new Constructor({ ...this.props, ...props || {} });
384
+ }
385
+ /**
386
+ * Serializes the value object to its raw properties for JSON operations.
387
+ *
388
+ * @returns The raw properties object
389
+ */
390
+ toJSON() {
391
+ return this.props;
392
+ }
393
+ };
394
+
395
+ // src/aggregate/domain-event.ts
396
+ var defaultEventIdFactory = /* @__PURE__ */ __name(() => crypto.randomUUID(), "defaultEventIdFactory");
397
+ var currentEventIdFactory = defaultEventIdFactory;
398
+ function setEventIdFactory(factory) {
399
+ currentEventIdFactory = factory;
400
+ }
401
+ __name(setEventIdFactory, "setEventIdFactory");
402
+ function resetEventIdFactory() {
403
+ currentEventIdFactory = defaultEventIdFactory;
404
+ }
405
+ __name(resetEventIdFactory, "resetEventIdFactory");
406
+ var defaultClockFactory = /* @__PURE__ */ __name(() => /* @__PURE__ */ new Date(), "defaultClockFactory");
407
+ var currentClockFactory = defaultClockFactory;
408
+ function setClockFactory(factory) {
409
+ currentClockFactory = factory;
410
+ }
411
+ __name(setClockFactory, "setClockFactory");
412
+ function resetClockFactory() {
413
+ currentClockFactory = defaultClockFactory;
414
+ }
415
+ __name(resetClockFactory, "resetClockFactory");
416
+ function createDomainEvent(type, payload, options) {
417
+ const event = {
418
+ eventId: options?.eventId ?? currentEventIdFactory(),
419
+ type,
420
+ aggregateId: options?.aggregateId,
421
+ aggregateType: options?.aggregateType,
422
+ payload,
423
+ occurredAt: options?.occurredAt ?? currentClockFactory(),
424
+ version: options?.version ?? 1,
425
+ metadata: options?.metadata
426
+ };
427
+ return deepFreeze(event);
428
+ }
429
+ __name(createDomainEvent, "createDomainEvent");
430
+ function createDomainEventWithMetadata(type, payload, metadata, options) {
431
+ return createDomainEvent(type, payload, {
432
+ ...options,
433
+ metadata
434
+ });
435
+ }
436
+ __name(createDomainEventWithMetadata, "createDomainEventWithMetadata");
437
+ function copyMetadata(sourceEvent, additionalMetadata) {
438
+ return {
439
+ ...sourceEvent.metadata ?? {},
440
+ ...additionalMetadata ?? {}
441
+ };
442
+ }
443
+ __name(copyMetadata, "copyMetadata");
444
+ function mergeMetadata(...metadataObjects) {
445
+ return Object.assign({}, ...metadataObjects.filter(Boolean));
446
+ }
447
+ __name(mergeMetadata, "mergeMetadata");
448
+
449
+ // src/aggregate/aggregate.ts
450
+ function sameVersion(a, b) {
451
+ return a.id === b.id && a.version === b.version;
452
+ }
453
+ __name(sameVersion, "sameVersion");
454
+
216
455
  // src/entity/entity.ts
217
456
  var Entity = class {
218
457
  static {
@@ -221,7 +460,15 @@ var Entity = class {
221
460
  id;
222
461
  /**
223
462
  * Returns the current state of the entity.
224
- * State is readonly from outside to enforce encapsulation.
463
+ *
464
+ * The state object is **shallowly frozen** — direct property writes
465
+ * (`entity.state.foo = …`) throw in strict mode, but writes to nested
466
+ * objects (`entity.state.address.zip = …`) bypass the freeze. For deep
467
+ * immutability either model nested data with `vo()` (which freezes
468
+ * deeply) or reach for a structural-sharing library like Immer at the
469
+ * App layer. The shallow contract is intentional: deep freezing on
470
+ * every state write is too expensive for hot paths, and DDD aggregates
471
+ * normally treat their own state as private (`Tell, Don't Ask`).
225
472
  */
226
473
  get state() {
227
474
  return this._state;
@@ -236,16 +483,29 @@ var Entity = class {
236
483
  throw new Error("Entity ID cannot be null or undefined");
237
484
  }
238
485
  this.id = id;
239
- this._state = initialState;
486
+ this._state = freezeShallow(initialState);
240
487
  this.validateState(this._state);
241
488
  }
242
489
  /**
243
- * Optional validation hook to ensure state invariants.
244
- * Called during construction and whenever helpful.
245
- * Override this method to implement validation logic.
490
+ * Optional validation hook to ensure state invariants. Called during
491
+ * construction (from `Entity`'s constructor) and again on every
492
+ * `setState()` call. Throw to reject invalid state.
493
+ *
494
+ * **⚠️ Must not read subclass instance fields via `this`.** The
495
+ * constructor calls `validateState(initialState)` BEFORE the subclass's
496
+ * field initializers run, so `this.someField` is `undefined` at that
497
+ * point — a classic TypeScript/JavaScript constructor-ordering footgun.
498
+ * The `state` argument is the single source of truth; treat the method
499
+ * as pure with respect to `this`.
500
+ *
501
+ * If your invariants genuinely depend on per-instance configuration
502
+ * that isn't part of the state, pass that configuration into the state
503
+ * itself (DDD-canonical: the aggregate's state contains everything it
504
+ * needs) or perform the additional check after construction in a
505
+ * dedicated factory method.
246
506
  *
247
507
  * @param state - The state to validate
248
- * @throws Error if validation fails
508
+ * @throws Error (or `DomainError` subclass) if validation fails
249
509
  */
250
510
  validateState(_state) {
251
511
  }
@@ -258,31 +518,38 @@ var Entity = class {
258
518
  */
259
519
  setState(newState) {
260
520
  this.validateState(newState);
261
- this._state = newState;
521
+ this._state = freezeShallow(newState);
262
522
  }
263
523
  };
524
+ function freezeShallow(value) {
525
+ if (value !== null && typeof value === "object") {
526
+ return Object.freeze(value);
527
+ }
528
+ return value;
529
+ }
530
+ __name(freezeShallow, "freezeShallow");
264
531
  function sameEntity(a, b) {
265
- return deepEqual(a.id, b.id);
532
+ return a.id === b.id;
266
533
  }
267
534
  __name(sameEntity, "sameEntity");
268
535
  function findEntityById(entities, id) {
269
- return entities.find((entity) => deepEqual(entity.id, id));
536
+ return entities.find((entity) => entity.id === id);
270
537
  }
271
538
  __name(findEntityById, "findEntityById");
272
539
  function hasEntityId(entities, id) {
273
- return entities.some((entity) => deepEqual(entity.id, id));
540
+ return entities.some((entity) => entity.id === id);
274
541
  }
275
542
  __name(hasEntityId, "hasEntityId");
276
543
  function removeEntityById(entities, id) {
277
- return entities.filter((entity) => !deepEqual(entity.id, id));
544
+ return entities.filter((entity) => entity.id !== id);
278
545
  }
279
546
  __name(removeEntityById, "removeEntityById");
280
547
  function updateEntityById(entities, id, updater) {
281
- return entities.map((entity) => deepEqual(entity.id, id) ? updater(entity) : entity);
548
+ return entities.map((entity) => entity.id === id ? updater(entity) : entity);
282
549
  }
283
550
  __name(updateEntityById, "updateEntityById");
284
551
  function replaceEntityById(entities, id, replacement) {
285
- return entities.map((entity) => deepEqual(entity.id, id) ? replacement : entity);
552
+ return entities.map((entity) => entity.id === id ? replacement : entity);
286
553
  }
287
554
  __name(replaceEntityById, "replaceEntityById");
288
555
  function entityIds(entities) {
@@ -310,7 +577,7 @@ var AggregateRoot = class extends Entity {
310
577
  * These events are side-effects of state changes.
311
578
  */
312
579
  get domainEvents() {
313
- return this._domainEvents;
580
+ return Object.freeze(this._domainEvents.slice());
314
581
  }
315
582
  /**
316
583
  * Clears the list of recorded domain events.
@@ -319,16 +586,105 @@ var AggregateRoot = class extends Entity {
319
586
  clearDomainEvents() {
320
587
  this._domainEvents = [];
321
588
  }
589
+ /**
590
+ * Post-save hook called by a `Repository.save()` implementation to push
591
+ * the persisted version back into the in-memory aggregate and clear the
592
+ * recorded domain events (they are now safely on the write side / in
593
+ * the outbox).
594
+ *
595
+ * Use this so `save()` can keep its `Promise<void>` return type: the
596
+ * caller holds the aggregate reference, which is up to date after this
597
+ * call.
598
+ */
599
+ markPersisted(version) {
600
+ this.setVersion(version);
601
+ this._domainEvents = [];
602
+ }
603
+ /**
604
+ * Mutates state and records the resulting domain events in the
605
+ * **canonical record-after-mutation order**. Use this instead of calling
606
+ * `setState` + `addDomainEvent` separately and you cannot trip the
607
+ * "event for a fact that never happened" footgun.
608
+ *
609
+ * Order of operations:
610
+ * 1. `setState(newState, true)` — runs `validateState` first.
611
+ * If it throws, the method propagates and **no event is recorded
612
+ * and no version is bumped**.
613
+ * 2. Each event in `events` is appended via `addDomainEvent`.
614
+ *
615
+ * `commit()` **always bumps the version**, regardless of the aggregate's
616
+ * `autoVersionBump` config. Recording a domain event implies "something
617
+ * happened that the outside world cares about", and optimistic-
618
+ * concurrency callers must see a fresh version every time. The config
619
+ * still governs the un-coupled `setState` path. If you need to mutate
620
+ * state without bumping (e.g. cosmetic caches), call `setState(newState,
621
+ * false)` and skip `commit` entirely.
622
+ *
623
+ * `events` accepts a single event or an array. Omit it (or pass `[]`)
624
+ * for state-only mutations.
625
+ *
626
+ * @example
627
+ * ```ts
628
+ * confirm(): void {
629
+ * if (this.state.status === "confirmed") {
630
+ * throw new OrderAlreadyConfirmedError(this.id);
631
+ * }
632
+ * this.commit(
633
+ * { ...this.state, status: "confirmed" },
634
+ * { type: "OrderConfirmed", orderId: this.id },
635
+ * );
636
+ * }
637
+ * ```
638
+ *
639
+ * `EventSourcedAggregate.apply()` enforces the same ordering
640
+ * structurally; `commit()` is the opt-in equivalent on `AggregateRoot`,
641
+ * where `setState` and `addDomainEvent` are otherwise decoupled and the
642
+ * ordering is convention-only.
643
+ *
644
+ * @param newState - The new state (validated by `validateState`)
645
+ * @param events - One event, an array of events, or none (default)
646
+ */
647
+ commit(newState, events = []) {
648
+ this.setState(newState, true);
649
+ const list = Array.isArray(events) ? events : [events];
650
+ for (const ev of list) {
651
+ this.addDomainEvent(ev);
652
+ }
653
+ }
322
654
  constructor(id, initialState, config) {
323
655
  super(id, initialState);
324
656
  this._config = config ?? {};
325
657
  this._autoVersionBump = this._config.autoVersionBump ?? false;
326
658
  }
327
659
  /**
328
- * Adds a domain event to the aggregate's list of changes.
329
- * Use this to record side-effects that should be published.
660
+ * Records a domain event for later publication.
661
+ *
662
+ * **Ordering: record AFTER state mutation.** Vernon (IDDD §8) is
663
+ * explicit: a domain event describes something that has just happened
664
+ * to the aggregate — its existence implies the state change already
665
+ * occurred. Concretely:
666
+ *
667
+ * ```ts
668
+ * confirm(): void {
669
+ * if (this.state.status === "confirmed") {
670
+ * throw new OrderAlreadyConfirmedError(this.id);
671
+ * }
672
+ * this.setState({ ...this.state, status: "confirmed" }, true);
673
+ * this.addDomainEvent({ type: "OrderConfirmed", orderId: this.id });
674
+ * // ↑ post-mutation. The event represents the committed fact.
675
+ * }
676
+ * ```
677
+ *
678
+ * Recording before mutation is a footgun: if a subsequent invariant
679
+ * check throws, the event has already been queued but the state never
680
+ * actually changed — consumers see an event for a fact that did not
681
+ * happen.
330
682
  *
331
- * @param event - The domain event to add
683
+ * `EventSourcedAggregate.apply()` enforces this ordering structurally;
684
+ * `AggregateRoot` leaves it as a convention because the state-mutation
685
+ * path (`setState`) is decoupled from event recording.
686
+ *
687
+ * @param event - The domain event to record
332
688
  */
333
689
  addDomainEvent(event) {
334
690
  this._domainEvents.push(event);
@@ -394,20 +750,65 @@ var AggregateRoot = class extends Entity {
394
750
  */
395
751
  restoreFromSnapshot(snapshot) {
396
752
  this.validateState(snapshot.state);
397
- this._state = snapshot.state;
753
+ this._state = freezeShallow(snapshot.state);
398
754
  this.setVersion(snapshot.version);
399
755
  }
400
756
  };
401
-
402
- // src/core/result/result.ts
403
- function ok(value) {
404
- return { ok: true, value };
405
- }
406
- __name(ok, "ok");
407
- function err(error) {
408
- return { ok: false, error };
409
- }
410
- __name(err, "err");
757
+ var DomainError = class extends BaseError {
758
+ static {
759
+ __name(this, "DomainError");
760
+ }
761
+ };
762
+ var InfrastructureError = class extends BaseError {
763
+ static {
764
+ __name(this, "InfrastructureError");
765
+ }
766
+ };
767
+ var MissingHandlerError = class extends BaseError {
768
+ constructor(eventType) {
769
+ super(`Missing handler for event type: ${eventType}`);
770
+ this.eventType = eventType;
771
+ }
772
+ static {
773
+ __name(this, "MissingHandlerError");
774
+ }
775
+ };
776
+ var AggregateNotFoundError = class extends InfrastructureError {
777
+ constructor(aggregateType, id) {
778
+ super(`Aggregate not found: ${aggregateType}(${id})`);
779
+ this.aggregateType = aggregateType;
780
+ this.id = id;
781
+ this.withUserMessage(
782
+ `The requested ${aggregateType.toLowerCase()} could not be found.`
783
+ );
784
+ }
785
+ static {
786
+ __name(this, "AggregateNotFoundError");
787
+ }
788
+ };
789
+ var ConcurrencyConflictError = class extends InfrastructureError {
790
+ constructor(aggregateType, aggregateId, expectedVersion, actualVersion) {
791
+ super(
792
+ `Concurrency conflict on ${aggregateType}(${aggregateId}): expected version ${expectedVersion}, actual ${actualVersion}`
793
+ );
794
+ this.aggregateType = aggregateType;
795
+ this.aggregateId = aggregateId;
796
+ this.expectedVersion = expectedVersion;
797
+ this.actualVersion = actualVersion;
798
+ this.withUserMessage(
799
+ "This resource was updated by another request. Please reload and try again."
800
+ );
801
+ }
802
+ static {
803
+ __name(this, "ConcurrencyConflictError");
804
+ }
805
+ /**
806
+ * Marks this error as retryable so `isRetryable(err)` returns
807
+ * true. The canonical OCC pattern is to reload the aggregate, re-apply
808
+ * the use case, and retry on this exception.
809
+ */
810
+ retryable = true;
811
+ };
411
812
 
412
813
  // src/aggregate/event-sourced-aggregate.ts
413
814
  var EventSourcedAggregate = class extends Entity {
@@ -426,73 +827,69 @@ var EventSourcedAggregate = class extends Entity {
426
827
  _pendingEvents = [];
427
828
  _autoVersionBump;
428
829
  get pendingEvents() {
429
- return this._pendingEvents;
830
+ return Object.freeze(this._pendingEvents.slice());
430
831
  }
431
832
  clearPendingEvents() {
432
833
  this._pendingEvents = [];
433
834
  }
835
+ /**
836
+ * Post-save hook called by a `Repository.save()` implementation to push
837
+ * the persisted version back into the in-memory aggregate and clear the
838
+ * pending events (they are now in the event store / outbox). Lets
839
+ * `save()` keep its `Promise<void>` return type.
840
+ */
841
+ markPersisted(version) {
842
+ this.setVersion(version);
843
+ this._pendingEvents = [];
844
+ }
434
845
  constructor(id, initialState, config) {
435
846
  super(id, initialState);
436
847
  this._autoVersionBump = config?.autoVersionBump ?? true;
437
848
  }
438
849
  // --- Event application ---
439
850
  /**
440
- * Validates an event before it is applied.
441
- * Override this method to add custom validation logic.
442
- * Return `ok(true)` if the event is valid, `err(message)` otherwise.
851
+ * Validates an event before it is applied. Default is no-op.
852
+ * Subclasses override to throw a concrete `DomainError` subclass when
853
+ * the event violates an invariant in the current state.
443
854
  */
444
855
  validateEvent(_event) {
445
- return ok(true);
446
856
  }
447
857
  /**
448
- * Applies an event to change the state and adds it to pending events.
449
- * Returns a Result type instead of throwing an error.
858
+ * Applies an event: validates, locates the handler, computes the next
859
+ * state, then commits state + pending event + version bump atomically.
860
+ *
861
+ * Throws `DomainError` (or a subclass) on validation failure.
862
+ * Throws `MissingHandlerError` if no handler is registered for `event.type`.
863
+ *
864
+ * State is not mutated if any step throws — the handler is invoked into
865
+ * a local and only assigned to `_state` once all checks pass.
866
+ *
867
+ * The method is generic in the event tag `K`, so concrete callers
868
+ * (`this.apply(orderCreated)`) narrow to the literal tag and the
869
+ * dispatched handler is typed as `Handler<TState, Extract<TEvent, { type: K }>>`
870
+ * — no `as` cast required at the call site.
450
871
  *
451
872
  * @param event - The domain event to apply
452
- * @param isNew - Whether the event is new (needs persisting) or from history replay
873
+ * @param isNew - Whether the event is new (needs persisting) or replayed from history
453
874
  */
454
875
  apply(event, isNew = true) {
455
- const validation = this.validateEvent(event);
456
- if (!validation.ok) {
457
- return err(
458
- `Event validation failed for ${event.type}: ${validation.error}`
459
- );
460
- }
461
- const handler = this.handlers[event.type];
462
- if (!handler) {
463
- return err(`Missing handler for event type: ${event.type}`);
464
- }
465
- this._state = handler(
466
- this._state,
467
- event
468
- );
469
- if (isNew) {
470
- this._pendingEvents.push(event);
471
- if (this._autoVersionBump) {
472
- this.setVersion(this._version + 1);
473
- }
474
- }
475
- return ok();
876
+ this.dispatchAndCommit(event, isNew);
476
877
  }
477
878
  /**
478
- * Applies an event to change the state and adds it to pending events.
479
- * Throws an error if validation fails or handler is missing.
879
+ * Internal dispatch path used by `apply()` and the replay methods
880
+ * (`loadFromHistory`, `restoreFromSnapshotWithEvents`). The replay loop
881
+ * iterates over `TEvent[]` and therefore cannot supply a narrowed `K`
882
+ * generic, so this helper accepts `TEvent` and the discriminator is
883
+ * resolved via the (statically-sound) `handlers` map.
480
884
  */
481
- applyUnsafe(event, isNew = true) {
482
- const validation = this.validateEvent(event);
483
- if (!validation.ok) {
484
- throw new Error(
485
- `Event validation failed for ${event.type}: ${validation.error}`
486
- );
487
- }
885
+ dispatchAndCommit(event, isNew) {
886
+ this.validateEvent(event);
488
887
  const handler = this.handlers[event.type];
489
888
  if (!handler) {
490
- throw new Error(`Missing handler for event type: ${event.type}`);
889
+ throw new MissingHandlerError(event.type);
491
890
  }
492
- this._state = handler(
493
- this._state,
494
- event
495
- );
891
+ const nextState = handler(this._state, event);
892
+ this._state = freezeShallow(nextState);
496
893
  if (isNew) {
497
894
  this._pendingEvents.push(event);
498
895
  if (this._autoVersionBump) {
@@ -509,17 +906,27 @@ var EventSourcedAggregate = class extends Entity {
509
906
  }
510
907
  // --- History & Snapshots ---
511
908
  /**
512
- * Reconstitutes the aggregate from an event history.
513
- * Sets the version to the number of events in the history.
909
+ * Reconstitutes the aggregate from an event history. Catches `DomainError`
910
+ * thrown during replay and returns it as an `Err` — this is the
911
+ * infrastructure boundary, where event-stream corruption is an expected
912
+ * recoverable failure. Unexpected (non-DomainError) throws propagate.
913
+ *
914
+ * Version advances additively: the aggregate's pre-existing version plus
915
+ * `history.length`. A fresh aggregate (v=0) loading 3 events ends at v=3;
916
+ * an aggregate already at v=1 (e.g. after a creation event) loading
917
+ * 2 events ends at v=3, not v=2.
514
918
  */
515
919
  loadFromHistory(history) {
920
+ const startVersion = this._version;
516
921
  for (const event of history) {
517
- const result = this.apply(event, false);
518
- if (!result.ok) {
519
- return result;
922
+ try {
923
+ this.dispatchAndCommit(event, false);
924
+ } catch (e) {
925
+ if (e instanceof DomainError) return err(e);
926
+ throw e;
520
927
  }
521
928
  }
522
- this.setVersion(history.length);
929
+ this.setVersion(startVersion + history.length);
523
930
  return ok();
524
931
  }
525
932
  hasPendingEvents() {
@@ -542,23 +949,34 @@ var EventSourcedAggregate = class extends Entity {
542
949
  };
543
950
  }
544
951
  /**
545
- * Restores the aggregate from a snapshot and applies events that occurred after.
952
+ * Restores the aggregate from a snapshot and applies events that occurred
953
+ * after. Same infrastructure-boundary semantics as `loadFromHistory`:
954
+ * catches `DomainError` and returns it as an `Err`; non-domain throws
955
+ * propagate.
956
+ *
957
+ * All-or-nothing: if any event mid-stream throws a `DomainError`, the
958
+ * aggregate is rolled back to its pre-call state + version. Partial
959
+ * restoration is never observable to the caller.
546
960
  */
547
961
  restoreFromSnapshotWithEvents(snapshot, eventsAfterSnapshot) {
548
- this._state = snapshot.state;
962
+ const previousState = this._state;
963
+ const previousVersion = this._version;
964
+ this._state = freezeShallow(snapshot.state);
549
965
  this.setVersion(snapshot.version);
550
966
  for (const event of eventsAfterSnapshot) {
551
- const result = this.apply(event, false);
552
- if (!result.ok) {
553
- return result;
967
+ try {
968
+ this.dispatchAndCommit(event, false);
969
+ } catch (e) {
970
+ this._state = previousState;
971
+ this.setVersion(previousVersion);
972
+ if (e instanceof DomainError) return err(e);
973
+ throw e;
554
974
  }
555
975
  }
556
976
  this.setVersion(snapshot.version + eventsAfterSnapshot.length);
557
977
  return ok();
558
978
  }
559
979
  };
560
-
561
- // src/app/command-bus.ts
562
980
  var CommandBus = class {
563
981
  static {
564
982
  __name(this, "CommandBus");
@@ -584,17 +1002,18 @@ var CommandBus = class {
584
1002
  };
585
1003
 
586
1004
  // src/app/handler.ts
587
- function withCommit(deps, fn) {
588
- return deps.uow.transactional(async () => {
589
- const { result, events } = await fn();
590
- await deps.outbox.add(events);
591
- if (deps.bus) await deps.bus.publish(events);
592
- return result;
1005
+ async function withCommit(deps, fn) {
1006
+ const { result, events } = await deps.scope.transactional(async () => {
1007
+ const fnResult = await fn();
1008
+ await deps.outbox.add(fnResult.events);
1009
+ return fnResult;
593
1010
  });
1011
+ if (deps.bus) {
1012
+ await deps.bus.publish(events);
1013
+ }
1014
+ return result;
594
1015
  }
595
1016
  __name(withCommit, "withCommit");
596
-
597
- // src/app/query-bus.ts
598
1017
  var QueryBus = class {
599
1018
  static {
600
1019
  __name(this, "QueryBus");
@@ -627,12 +1046,6 @@ var QueryBus = class {
627
1046
  }
628
1047
  };
629
1048
 
630
- // src/core/guard.ts
631
- function guard(cond, error) {
632
- return cond ? ok(true) : err(error);
633
- }
634
- __name(guard, "guard");
635
-
636
1049
  // src/events/event-bus.ts
637
1050
  var EventBusImpl = class {
638
1051
  static {
@@ -643,32 +1056,82 @@ var EventBusImpl = class {
643
1056
  subscribe(eventType, handler) {
644
1057
  const type = eventType;
645
1058
  if (!this.handlers.has(type)) {
646
- this.handlers.set(type, /* @__PURE__ */ new Set());
1059
+ this.handlers.set(type, []);
647
1060
  }
648
1061
  const handlersForType = this.handlers.get(type);
649
- handlersForType.add(handler);
1062
+ const casted = handler;
1063
+ handlersForType.push(casted);
1064
+ let removed = false;
650
1065
  return () => {
651
- handlersForType.delete(handler);
652
- if (handlersForType.size === 0) {
1066
+ if (removed) return;
1067
+ const idx = handlersForType.indexOf(casted);
1068
+ if (idx !== -1) {
1069
+ handlersForType.splice(idx, 1);
1070
+ removed = true;
1071
+ }
1072
+ if (handlersForType.length === 0) {
653
1073
  this.handlers.delete(type);
654
1074
  }
655
1075
  };
656
1076
  }
657
- once(eventType) {
658
- return new Promise((resolve) => {
659
- const unsubscribe = this.subscribe(eventType, (event) => {
1077
+ once(eventType, options) {
1078
+ return new Promise((resolve, reject) => {
1079
+ if (options?.signal?.aborted) {
1080
+ reject(options.signal.reason ?? new Error("EventBus.once aborted"));
1081
+ return;
1082
+ }
1083
+ let timer;
1084
+ let settled = false;
1085
+ let abortListener;
1086
+ const cleanup = /* @__PURE__ */ __name(() => {
1087
+ if (settled) return;
1088
+ settled = true;
660
1089
  unsubscribe();
1090
+ if (timer !== void 0) clearTimeout(timer);
1091
+ if (abortListener && options?.signal) {
1092
+ options.signal.removeEventListener("abort", abortListener);
1093
+ }
1094
+ }, "cleanup");
1095
+ const unsubscribe = this.subscribe(eventType, (event) => {
1096
+ cleanup();
661
1097
  resolve(event);
662
1098
  });
1099
+ if (options?.signal) {
1100
+ abortListener = /* @__PURE__ */ __name(() => {
1101
+ cleanup();
1102
+ reject(
1103
+ options.signal.reason ?? new Error("EventBus.once aborted")
1104
+ );
1105
+ }, "abortListener");
1106
+ options.signal.addEventListener("abort", abortListener);
1107
+ }
1108
+ if (typeof options?.timeoutMs === "number") {
1109
+ timer = setTimeout(() => {
1110
+ cleanup();
1111
+ reject(
1112
+ new Error(
1113
+ `EventBus.once timed out after ${options.timeoutMs}ms waiting for "${eventType}"`
1114
+ )
1115
+ );
1116
+ }, options.timeoutMs);
1117
+ }
663
1118
  });
664
1119
  }
1120
+ /**
1121
+ * See {@link EventBus.publish} for the full ordering / parallelism /
1122
+ * error-aggregation contract this implementation realises:
1123
+ * - events in input order, sequentially;
1124
+ * - handlers within one event in parallel via `Promise.allSettled`;
1125
+ * - errors collected and thrown after the batch (single Error, or
1126
+ * `AggregateError` for multiple failures).
1127
+ */
665
1128
  async publish(events) {
666
1129
  const errors = [];
667
1130
  for (const event of events) {
668
1131
  const handlersForType = this.handlers.get(event.type);
669
1132
  if (handlersForType) {
670
1133
  const results = await Promise.allSettled(
671
- Array.from(handlersForType).map((handler) => handler(event))
1134
+ handlersForType.slice().map((handler) => handler(event))
672
1135
  );
673
1136
  for (const result of results) {
674
1137
  if (result.status === "rejected") {
@@ -688,195 +1151,6 @@ var EventBusImpl = class {
688
1151
  }
689
1152
  };
690
1153
 
691
- // src/utils/array/deep-omit.ts
692
- function deepOmit(value, options) {
693
- const visited = /* @__PURE__ */ new WeakMap();
694
- return omitInternal(value, options, [], visited);
695
- }
696
- __name(deepOmit, "deepOmit");
697
- function omitInternal(value, options, path, visited) {
698
- if (value === null) return value;
699
- const type = typeof value;
700
- if (type !== "object") return value;
701
- const obj = value;
702
- const cached = visited.get(obj);
703
- if (cached !== void 0) {
704
- return cached;
705
- }
706
- const tag = Object.prototype.toString.call(obj);
707
- if (tag === "[object Array]") {
708
- const arr = obj;
709
- const clone2 = new Array(arr.length);
710
- visited.set(obj, clone2);
711
- for (let i = 0; i < arr.length; i++) {
712
- path.push(i);
713
- clone2[i] = omitInternal(arr[i], options, path, visited);
714
- path.pop();
715
- }
716
- return clone2;
717
- }
718
- if (isBuiltInObject(obj, tag)) {
719
- return value;
720
- }
721
- const clone = Object.create(Object.getPrototypeOf(obj));
722
- visited.set(obj, clone);
723
- const stringKeys = Object.keys(obj);
724
- const symbolKeys = Object.getOwnPropertySymbols(obj);
725
- const keys = [...stringKeys, ...symbolKeys];
726
- for (const key of keys) {
727
- if (shouldIgnoreKey(key, path, options)) continue;
728
- path.push(key);
729
- clone[key] = omitInternal(
730
- obj[key],
731
- options,
732
- path,
733
- visited
734
- );
735
- path.pop();
736
- }
737
- return clone;
738
- }
739
- __name(omitInternal, "omitInternal");
740
- function shouldIgnoreKey(key, path, options) {
741
- if (options.ignoreKeys?.includes(key)) {
742
- return true;
743
- }
744
- if (options.ignoreKeyPredicate?.(key, path)) {
745
- return true;
746
- }
747
- return false;
748
- }
749
- __name(shouldIgnoreKey, "shouldIgnoreKey");
750
-
751
- // src/utils/array/deep-equal-except.ts
752
- function deepEqualExcept(a, b, options) {
753
- const prunedA = deepOmit(a, options);
754
- const prunedB = deepOmit(b, options);
755
- return deepEqual(prunedA, prunedB);
756
- }
757
- __name(deepEqualExcept, "deepEqualExcept");
758
-
759
- // src/value-object/value-object.ts
760
- function deepFreeze(obj, visited = /* @__PURE__ */ new WeakSet()) {
761
- if (obj === null || typeof obj !== "object") {
762
- return obj;
763
- }
764
- if (visited.has(obj)) {
765
- return obj;
766
- }
767
- visited.add(obj);
768
- const propNames = Object.getOwnPropertyNames(obj);
769
- for (const name of propNames) {
770
- const value = obj[name];
771
- if (value && (typeof value === "object" || Array.isArray(value))) {
772
- deepFreeze(value, visited);
773
- }
774
- }
775
- return Object.freeze(obj);
776
- }
777
- __name(deepFreeze, "deepFreeze");
778
- function vo(t) {
779
- return deepFreeze({ ...t });
780
- }
781
- __name(vo, "vo");
782
- function voEquals(a, b) {
783
- return deepEqual(a, b);
784
- }
785
- __name(voEquals, "voEquals");
786
- function voEqualsExcept(a, b, options) {
787
- return deepEqualExcept(a, b, options);
788
- }
789
- __name(voEqualsExcept, "voEqualsExcept");
790
- function voWithValidation(t, validate, errorMessage) {
791
- if (!validate(t)) {
792
- return err(
793
- errorMessage ?? `Validation failed for value object: ${JSON.stringify(t)}`
794
- );
795
- }
796
- return ok(vo(t));
797
- }
798
- __name(voWithValidation, "voWithValidation");
799
- function voWithValidationUnsafe(t, validate, errorMessage) {
800
- if (!validate(t)) {
801
- throw new Error(
802
- errorMessage ?? `Validation failed for value object: ${JSON.stringify(t)}`
803
- );
804
- }
805
- return vo(t);
806
- }
807
- __name(voWithValidationUnsafe, "voWithValidationUnsafe");
808
- var ValueObject = class {
809
- static {
810
- __name(this, "ValueObject");
811
- }
812
- props;
813
- /**
814
- * Creates a new ValueObject.
815
- * The properties are deeply frozen to ensure immutability.
816
- *
817
- * @param props - The properties of the value object
818
- * @example
819
- * ```ts
820
- * class Money extends ValueObject<{ amount: number; currency: string }> {
821
- * constructor(props: { amount: number; currency: string }) {
822
- * super(props);
823
- * }
824
- *
825
- * protected validate(props: { amount: number; currency: string }): void {
826
- * if (props.amount < 0) throw new Error("Amount cannot be negative");
827
- * }
828
- * }
829
- * ```
830
- */
831
- constructor(props) {
832
- this.validate(props);
833
- this.props = deepFreeze({ ...props });
834
- }
835
- /**
836
- * Optional validation hook that can be overridden by subclasses.
837
- * Should throw an error if validation fails.
838
- *
839
- * @param props - The properties to validate
840
- * @throws Error if validation fails
841
- */
842
- validate(props) {
843
- }
844
- /**
845
- * Checks if this value object is equal to another.
846
- * Uses deep equality comparison on the properties and checks for constructor equality.
847
- *
848
- * @param other - The other value object to compare
849
- * @returns true if the properties are deeply equal and constructors match
850
- */
851
- equals(other) {
852
- if (other === null || other === void 0) {
853
- return false;
854
- }
855
- if (this.constructor !== other.constructor) {
856
- return false;
857
- }
858
- return deepEqual(this.props, other.props);
859
- }
860
- /**
861
- * Creates a clone of the value object with optional property overrides.
862
- *
863
- * @param props - Optional properties to override
864
- * @returns A new instance of the value object
865
- */
866
- clone(props) {
867
- const Constructor = this.constructor;
868
- return new Constructor({ ...this.props, ...props || {} });
869
- }
870
- /**
871
- * Serializes the value object to its raw properties for JSON operations.
872
- *
873
- * @returns The raw properties object
874
- */
875
- toJSON() {
876
- return this.props;
877
- }
878
- };
879
-
880
- export { AggregateRoot, CommandBus, Entity, EventBusImpl, EventSourcedAggregate, QueryBus, ValueObject, aggregate, bump, copyMetadata, createDomainEvent, createDomainEventWithMetadata, deepFreeze, entityIds, findEntityById, guard, hasEntityId, mergeMetadata, removeEntityById, replaceEntityById, sameEntity, sameVersion, updateEntityById, vo, voEquals, voEqualsExcept, voWithValidation, voWithValidationUnsafe, withCommit };
1154
+ export { AggregateNotFoundError, AggregateRoot, CommandBus, ConcurrencyConflictError, DomainError, Entity, EventBusImpl, EventSourcedAggregate, InfrastructureError, MissingHandlerError, QueryBus, ValueObject, copyMetadata, createDomainEvent, createDomainEventWithMetadata, deepEqual, deepEqualExcept, deepFreeze, deepOmit, entityIds, findEntityById, freezeShallow, hasEntityId, mergeMetadata, removeEntityById, replaceEntityById, resetClockFactory, resetEventIdFactory, sameEntity, sameVersion, setClockFactory, setEventIdFactory, updateEntityById, vo, voEquals, voEqualsExcept, voWithValidation, withCommit };
881
1155
  //# sourceMappingURL=index.js.map
882
1156
  //# sourceMappingURL=index.js.map