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

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