@shirudo/ddd-kit 3.0.0-rc.11 → 3.0.0-rc.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,1254 +1,7 @@
1
- import { C as InvalidEventStreamPageError, Y as assertNoHostileOwnProtoKey } from "./kit-errors.js";
2
- import { err, ok } from "@shirudo/result";
1
+ import { C as InvalidEventStreamPageError, Y as assertNoOwnProtoKey } from "./kit-errors.js";
2
+ import { _ as isIntrinsicConstructorPrototype, c as applyCooperativeBrand, f as builtInTagWithoutInvokingAccessors, h as hasIntrinsicPrototypeChain, l as hasCooperativeBrand, n as deepFreeze, p as builtInTypeName, s as isMutatorShadow } from "./value-object.js";
3
3
 
4
- //#region src/internal/structural/is-built-in.ts
5
- /**
6
- * Set of `Object.prototype.toString.call(x)` tags that the library treats
7
- * as built-in atomic types. Members of this set are compared/cloned by
8
- * reference (or with type-specific logic) rather than walked structurally.
9
- *
10
- * Detection is tag-based, since `Object.prototype.toString` gives the same
11
- * answer across realms (an iframe's `Date` has the same tag as the main
12
- * window's `Date`), and then brand-verified via internal-slot probes,
13
- * because `Symbol.toStringTag` lets any plain object claim a built-in tag.
14
- * The previous strategy also checked `globalThis[name] === constructor`
15
- * and a `proto !== Object.prototype` heuristic; both broke for cross-realm
16
- * objects and the latter additionally misclassified ordinary user classes
17
- * as built-ins.
18
- */
19
- const BUILT_IN_TAGS = /* @__PURE__ */ new Set([
20
- "[object Date]",
21
- "[object RegExp]",
22
- "[object Map]",
23
- "[object Set]",
24
- "[object WeakMap]",
25
- "[object WeakSet]",
26
- "[object Promise]",
27
- "[object Error]",
28
- "[object Boolean]",
29
- "[object Number]",
30
- "[object String]",
31
- "[object BigInt]",
32
- "[object ArrayBuffer]",
33
- "[object SharedArrayBuffer]",
34
- "[object DataView]"
35
- ]);
36
- function intrinsicGetter(proto, prop) {
37
- const get = Object.getOwnPropertyDescriptor(proto, prop)?.get;
38
- if (!get) throw new Error(`missing intrinsic getter for ${prop}`);
39
- return get;
40
- }
41
- const dateGetTime = Date.prototype.getTime;
42
- const mapSizeGet = intrinsicGetter(Map.prototype, "size");
43
- const setSizeGet = intrinsicGetter(Set.prototype, "size");
44
- const weakMapHas = WeakMap.prototype.has;
45
- const weakSetHas = WeakSet.prototype.has;
46
- const dataViewByteLengthGet = intrinsicGetter(DataView.prototype, "byteLength");
47
- const arrayBufferByteLengthGet = intrinsicGetter(ArrayBuffer.prototype, "byteLength");
48
- const sharedArrayBufferByteLengthGet = typeof SharedArrayBuffer === "undefined" ? void 0 : intrinsicGetter(SharedArrayBuffer.prototype, "byteLength");
49
- const regExpSourceGet = intrinsicGetter(RegExp.prototype, "source");
50
- const booleanValueOf = Boolean.prototype.valueOf;
51
- const numberValueOf = Number.prototype.valueOf;
52
- const stringValueOf = String.prototype.valueOf;
53
- const bigIntValueOf = BigInt.prototype.valueOf;
54
- const functionToString = Function.prototype.toString;
55
- const PROBE_KEY = {};
56
- const intrinsicConstructorSources = new Map([
57
- "Object",
58
- "Array",
59
- "Date",
60
- "RegExp",
61
- "Map",
62
- "Set",
63
- "WeakMap",
64
- "WeakSet",
65
- "Promise",
66
- "Error",
67
- "EvalError",
68
- "RangeError",
69
- "ReferenceError",
70
- "SyntaxError",
71
- "TypeError",
72
- "URIError",
73
- "AggregateError",
74
- "Boolean",
75
- "Number",
76
- "String",
77
- "BigInt",
78
- "ArrayBuffer",
79
- "SharedArrayBuffer",
80
- "DataView",
81
- "Int8Array",
82
- "Uint8Array",
83
- "Uint8ClampedArray",
84
- "Int16Array",
85
- "Uint16Array",
86
- "Int32Array",
87
- "Uint32Array",
88
- "Float32Array",
89
- "Float64Array",
90
- "BigInt64Array",
91
- "BigUint64Array"
92
- ].flatMap((name) => {
93
- const intrinsic = Object.getOwnPropertyDescriptor(globalThis, name)?.value;
94
- return typeof intrinsic === "function" ? [[name, /* @__PURE__ */ new Set([functionToString.call(intrinsic), `function ${name}() { [native code] }`])]] : [];
95
- }));
96
- const intrinsicConstructorSourceSet = new Set([...intrinsicConstructorSources.values()].flatMap((sources) => [...sources]));
97
- const ERROR_INTRINSIC_NAMES = [
98
- "Error",
99
- "EvalError",
100
- "RangeError",
101
- "ReferenceError",
102
- "SyntaxError",
103
- "TypeError",
104
- "URIError",
105
- "AggregateError"
106
- ];
107
- function isIntrinsicConstructorPrototype(prototype, expectedName) {
108
- const constructorDescriptor = Object.getOwnPropertyDescriptor(prototype, "constructor");
109
- const candidateConstructor = constructorDescriptor?.value;
110
- if (constructorDescriptor === void 0 || !("value" in constructorDescriptor) || typeof candidateConstructor !== "function") return false;
111
- let candidateSource;
112
- try {
113
- candidateSource = functionToString.call(candidateConstructor);
114
- } catch {
115
- return false;
116
- }
117
- const expectedSources = expectedName === void 0 ? void 0 : intrinsicConstructorSources.get(expectedName);
118
- if (expectedSources !== void 0 && !expectedSources.has(candidateSource) || expectedSources === void 0 && !intrinsicConstructorSourceSet.has(candidateSource)) return false;
119
- const nameDescriptor = Object.getOwnPropertyDescriptor(candidateConstructor, "name");
120
- const candidateName = nameDescriptor !== void 0 && "value" in nameDescriptor && typeof nameDescriptor.value === "string" ? nameDescriptor.value : void 0;
121
- const intrinsicName = expectedName ?? candidateName;
122
- const intrinsicSources = intrinsicName === void 0 ? void 0 : intrinsicConstructorSources.get(intrinsicName);
123
- return candidateName === intrinsicName && intrinsicSources !== void 0 && intrinsicSources.has(candidateSource) && Object.getOwnPropertyDescriptor(candidateConstructor, "prototype")?.value === prototype;
124
- }
125
- /**
126
- * Accepts an intrinsic prototype, optionally behind transparent
127
- * `Symbol.toStringTag` override layers. A user-defined subclass has its own
128
- * non-native constructor and is therefore rejected before reaching the
129
- * intrinsic prototype.
130
- */
131
- function hasIntrinsicPrototypeChain(value, expectedName) {
132
- const visited = /* @__PURE__ */ new WeakSet();
133
- let prototype = Object.getPrototypeOf(value);
134
- while (prototype !== null && !visited.has(prototype)) {
135
- visited.add(prototype);
136
- if (Object.hasOwn(prototype, "constructor")) return isIntrinsicConstructorPrototype(prototype, expectedName);
137
- const ownKeys = Reflect.ownKeys(prototype);
138
- if (ownKeys.length !== 1 || ownKeys[0] !== Symbol.toStringTag) return false;
139
- prototype = Object.getPrototypeOf(prototype);
140
- }
141
- return false;
142
- }
143
- /**
144
- * Tags that `deepEqual` compares BY REFERENCE (its unhandled-built-in
145
- * fallback) and that `deepOmit` must therefore ALIAS rather than clone:
146
- * a clone would break `deepEqualExcept(x, x)` reflexivity. Single source
147
- * of truth so the two modules cannot drift: if `deepEqual` ever learns a
148
- * by-value comparison for one of these, remove it here and add a clone
149
- * case in `deepOmit`'s `cloneBuiltIn` in the same change.
150
- */
151
- const REFERENCE_COMPARED_TAGS = /* @__PURE__ */ new Set([
152
- "[object Error]",
153
- "[object ArrayBuffer]",
154
- "[object SharedArrayBuffer]",
155
- "[object Promise]",
156
- "[object WeakMap]",
157
- "[object WeakSet]"
158
- ]);
159
- /**
160
- * Intrinsic tags of OPAQUE exotics: objects whose internal state no
161
- * structural walk can observe (boxed Symbols, generator objects,
162
- * WeakRefs, FinalizationRegistry handles). `deepEqual` compares them by
163
- * identity and `deepOmit` passes them through by reference; treating
164
- * them as (empty) plain objects would make ALL such exotics equal to
165
- * each other. Deliberately a curated INTRINSIC list, not "every unknown
166
- * tag": a user class exposing its own `Symbol.toStringTag` (e.g.
167
- * "Money") keeps structural comparison, and a plain object spoofing one
168
- * of these intrinsic tags gets the identity semantics of the thing it
169
- * claims to be.
170
- */
171
- const OPAQUE_EXOTIC_TAGS = /* @__PURE__ */ new Set([
172
- "[object Symbol]",
173
- "[object Generator]",
174
- "[object AsyncGenerator]",
175
- "[object WeakRef]",
176
- "[object FinalizationRegistry]"
177
- ]);
178
- /** True when `tag` names an opaque intrinsic; see {@link OPAQUE_EXOTIC_TAGS}. */
179
- function isOpaqueExoticTag(tag) {
180
- return OPAQUE_EXOTIC_TAGS.has(tag);
181
- }
182
- function findPropertyDescriptor(value, key) {
183
- const visited = /* @__PURE__ */ new WeakSet();
184
- let current = value;
185
- while (current !== null && !visited.has(current)) {
186
- visited.add(current);
187
- const descriptor = Object.getOwnPropertyDescriptor(current, key);
188
- if (descriptor !== void 0) return descriptor;
189
- current = Object.getPrototypeOf(current);
190
- }
191
- }
192
- function builtInTagWithoutInvokingAccessors(value) {
193
- if (ArrayBuffer.isView(value)) return hasBrand(value, "[object DataView]") ? "[object DataView]" : "[object TypedArray]";
194
- const descriptor = findPropertyDescriptor(value, Symbol.toStringTag);
195
- if (descriptor !== void 0 && !("value" in descriptor)) return builtInTagFromBrand(value);
196
- const tag = Object.prototype.toString.call(value);
197
- if (BUILT_IN_TAGS.has(tag) && hasBrand(value, tag)) return tag;
198
- return descriptor === void 0 ? void 0 : builtInTagFromBrand(value);
199
- }
200
- function builtInTagFromBrand(value) {
201
- if (hasBrand(value, "[object Date]")) return "[object Date]";
202
- if (hasBrand(value, "[object RegExp]")) return "[object RegExp]";
203
- if (hasBrand(value, "[object Map]")) return "[object Map]";
204
- if (hasBrand(value, "[object Set]")) return "[object Set]";
205
- if (hasBrand(value, "[object WeakMap]")) return "[object WeakMap]";
206
- if (hasBrand(value, "[object WeakSet]")) return "[object WeakSet]";
207
- if (hasBrand(value, "[object DataView]")) return "[object DataView]";
208
- if (hasBrand(value, "[object ArrayBuffer]")) return "[object ArrayBuffer]";
209
- if (hasBrand(value, "[object SharedArrayBuffer]")) return "[object SharedArrayBuffer]";
210
- if (hasBrand(value, "[object Boolean]")) return "[object Boolean]";
211
- if (hasBrand(value, "[object Number]")) return "[object Number]";
212
- if (hasBrand(value, "[object String]")) return "[object String]";
213
- if (hasBrand(value, "[object BigInt]")) return "[object BigInt]";
214
- if (hasNativePrototype(value, "Promise")) return "[object Promise]";
215
- if (hasNativePrototype(value, "Error")) return "[object Error]";
216
- }
217
- function hasNativePrototype(value, expectedName) {
218
- const visited = /* @__PURE__ */ new WeakSet();
219
- let prototype = Object.getPrototypeOf(value);
220
- while (prototype !== null && !visited.has(prototype)) {
221
- visited.add(prototype);
222
- if (isIntrinsicConstructorPrototype(prototype, expectedName)) return true;
223
- prototype = Object.getPrototypeOf(prototype);
224
- }
225
- return false;
226
- }
227
- /**
228
- * Verifies that `obj` genuinely is the type its tag claims, via an
229
- * internal-slot probe. Promise and Error have no side-effect-free standard
230
- * probe, so their visible tags remain conservative; masked instances are
231
- * identified separately through their native prototype chain.
232
- */
233
- function hasBrand(obj, tag) {
234
- try {
235
- switch (tag) {
236
- case "[object Date]":
237
- dateGetTime.call(obj);
238
- return true;
239
- case "[object RegExp]":
240
- regExpSourceGet.call(obj);
241
- return true;
242
- case "[object Map]":
243
- mapSizeGet.call(obj);
244
- return true;
245
- case "[object Set]":
246
- setSizeGet.call(obj);
247
- return true;
248
- case "[object WeakMap]":
249
- weakMapHas.call(obj, PROBE_KEY);
250
- return true;
251
- case "[object WeakSet]":
252
- weakSetHas.call(obj, PROBE_KEY);
253
- return true;
254
- case "[object DataView]":
255
- dataViewByteLengthGet.call(obj);
256
- return true;
257
- case "[object ArrayBuffer]":
258
- arrayBufferByteLengthGet.call(obj);
259
- return true;
260
- case "[object SharedArrayBuffer]":
261
- if (!sharedArrayBufferByteLengthGet) return false;
262
- sharedArrayBufferByteLengthGet.call(obj);
263
- return true;
264
- case "[object Boolean]":
265
- booleanValueOf.call(obj);
266
- return true;
267
- case "[object Number]":
268
- numberValueOf.call(obj);
269
- return true;
270
- case "[object String]":
271
- stringValueOf.call(obj);
272
- return true;
273
- case "[object BigInt]":
274
- bigIntValueOf.call(obj);
275
- return true;
276
- case "[object Promise]": return hasNativePrototype(obj, "Promise");
277
- case "[object Error]": return ERROR_INTRINSIC_NAMES.some((name) => hasNativePrototype(obj, name));
278
- default: return false;
279
- }
280
- } catch {
281
- return false;
282
- }
283
- }
284
- /**
285
- * Returns `true` when `obj` is a built-in JavaScript type that should be
286
- * treated atomically (compared/cloned as a unit, not walked structurally).
287
- * Cross-realm safe, and brand-verified: a plain object spoofing a built-in
288
- * tag via `Symbol.toStringTag` returns `false` and is walked structurally
289
- * like any other plain object instead of crashing type-specific code.
290
- *
291
- * @param obj - The object to classify
292
- * @param tag - The result of `Object.prototype.toString.call(obj)`, passed
293
- * in so callers that already computed it don't pay twice
294
- */
295
- function isBuiltInObject(obj, tag) {
296
- if (ArrayBuffer.isView(obj)) return true;
297
- if (tag.endsWith("Array]")) return false;
298
- return BUILT_IN_TAGS.has(tag) && hasBrand(obj, tag);
299
- }
300
- /**
301
- * Brand-verified `WeakMap` check that holds across realms. `instanceof
302
- * WeakMap` binds to the constructor of one realm, so a WeakMap from a `vm`
303
- * context or an iframe reads as a foreign value. The internal-slot probe
304
- * answers the same in every realm, and a plain object cannot spoof it
305
- * through `Symbol.toStringTag`.
306
- */
307
- function isWeakMap(value) {
308
- return typeof value === "object" && value !== null && hasBrand(value, "[object WeakMap]");
309
- }
310
-
311
- //#endregion
312
- //#region src/internal/structural/deep-equal.ts
313
- const objProto = Object.prototype;
314
- const objToString = objProto.toString;
315
- const objHasOwn = objProto.hasOwnProperty;
316
- /**
317
- * SameValueZero: `===` plus NaN-equals-NaN (and `+0 === -0`, unlike
318
- * `Object.is`). The numeric semantics `deepEqual` documents for primitives,
319
- * applied consistently inside TypedArrays, Dates and Number wrappers.
320
- */
321
- function sameValueZero(a, b) {
322
- return a === b || Number.isNaN(a) && Number.isNaN(b);
323
- }
324
- /**
325
- * Performs a deep equality check between two values.
326
- *
327
- * This function compares values recursively, handling:
328
- * - Primitives (with special handling for NaN)
329
- * - Arrays (nested arrays supported)
330
- * - Objects (plain objects and class instances)
331
- * - TypedArrays (Uint8Array, Int32Array, etc.)
332
- * - DataView
333
- * - Maps and Sets
334
- * - Dates and RegExp
335
- * - Wrapper objects (Boolean, Number, String)
336
- * - Circular references (detected and handled)
337
- *
338
- * @param a - The first value to compare
339
- * @param b - The second value to compare
340
- * @returns `true` if the values are deeply equal, `false` otherwise
341
- *
342
- * @example
343
- * ```ts
344
- * deepEqual([1, 2, 3], [1, 2, 3]); // true
345
- * deepEqual({ a: 1, b: [2, 3] }, { a: 1, b: [2, 3] }); // true
346
- * deepEqual(NaN, NaN); // true
347
- * deepEqual([1, 2], [1, 2, 3]); // false
348
- * ```
349
- */
350
- function deepEqual(a, b) {
351
- return deepEqualInner(a, b, /* @__PURE__ */ new WeakMap());
352
- }
353
- /**
354
- * Internal recursive function for deep equality comparison.
355
- *
356
- * @internal
357
- */
358
- function deepEqualInner(a, b, visited) {
359
- if (a === b) return true;
360
- const typeA = typeof a;
361
- const typeB = typeof b;
362
- if (typeA !== "object" || a === null || typeB !== "object" || b === null) {
363
- if (typeA === "number" && typeB === "number") return Number.isNaN(a) && Number.isNaN(b);
364
- return false;
365
- }
366
- const objA = a;
367
- const objB = b;
368
- let cachedBs = visited.get(objA);
369
- if (cachedBs?.has(objB)) return true;
370
- if (!cachedBs) {
371
- cachedBs = /* @__PURE__ */ new WeakSet();
372
- visited.set(objA, cachedBs);
373
- }
374
- cachedBs.add(objB);
375
- if (ArrayBuffer.isView(objA) || ArrayBuffer.isView(objB)) {
376
- if (!ArrayBuffer.isView(objA) || !ArrayBuffer.isView(objB)) return false;
377
- const tagA = objToString.call(objA);
378
- if (tagA !== objToString.call(objB)) return false;
379
- if (tagA === "[object DataView]") {
380
- const viewA = objA;
381
- const viewB = objB;
382
- if (viewA.byteLength !== viewB.byteLength) return false;
383
- const len = viewA.byteLength;
384
- for (let i = 0; i < len; i++) if (viewA.getUint8(i) !== viewB.getUint8(i)) return false;
385
- return true;
386
- }
387
- const arrA = objA;
388
- const arrB = objB;
389
- const len = arrA.length;
390
- if (len !== arrB.length) return false;
391
- for (let i = 0; i < len; i++) if (!sameValueZero(arrA[i], arrB[i])) return false;
392
- return true;
393
- }
394
- if (Array.isArray(objA) || Array.isArray(objB)) {
395
- if (!Array.isArray(objA) || !Array.isArray(objB)) return false;
396
- if (objA.length !== objB.length) return false;
397
- const keysA = Reflect.ownKeys(objA).filter((key) => key !== "length");
398
- const keysB = Reflect.ownKeys(objB).filter((key) => key !== "length");
399
- if (keysA.length !== keysB.length) return false;
400
- const arrA = objA;
401
- const arrB = objB;
402
- for (const key of keysA) {
403
- if (!objHasOwn.call(objB, key)) return false;
404
- if (!deepEqualInner(arrA[key], arrB[key], visited)) return false;
405
- }
406
- return true;
407
- }
408
- const tagA = objToString.call(objA);
409
- const tagB = objToString.call(objB);
410
- if (tagA !== tagB) return false;
411
- const builtInA = isBuiltInObject(objA, tagA);
412
- if (builtInA !== isBuiltInObject(objB, tagB)) return false;
413
- if (!builtInA) {
414
- if (isOpaqueExoticTag(tagA)) return objA === objB;
415
- return comparePlainObjects(objA, objB, visited);
416
- }
417
- switch (tagA) {
418
- case "[object Map]": {
419
- const mapA = objA;
420
- const mapB = objB;
421
- if (mapA.size !== mapB.size) return false;
422
- for (const [key, valA] of mapA) {
423
- if (!mapB.has(key)) return false;
424
- if (!deepEqualInner(valA, mapB.get(key), visited)) return false;
425
- }
426
- return true;
427
- }
428
- case "[object Set]": {
429
- const setA = objA;
430
- const setB = objB;
431
- if (setA.size !== setB.size) return false;
432
- for (const value of setA) if (!setB.has(value)) return false;
433
- return true;
434
- }
435
- case "[object Date]": return sameValueZero(objA.getTime(), objB.getTime());
436
- case "[object RegExp]": {
437
- const regA = objA;
438
- const regB = objB;
439
- return regA.source === regB.source && regA.flags === regB.flags;
440
- }
441
- case "[object Boolean]":
442
- case "[object Number]":
443
- case "[object String]":
444
- case "[object BigInt]": return sameValueZero(objA.valueOf(), objB.valueOf());
445
- default: return objA === objB;
446
- }
447
- }
448
- /**
449
- * Plain / custom objects: compare own enumerable string keys + own symbol
450
- * keys and their values. Used both as the final fallback and for objects
451
- * whose built-in-looking tag failed brand verification.
452
- */
453
- function comparePlainObjects(objA, objB, visited) {
454
- const recA = objA;
455
- const recB = objB;
456
- const stringKeysA = Object.getOwnPropertyNames(objA);
457
- const stringKeysB = Object.getOwnPropertyNames(objB);
458
- if (stringKeysA.length !== stringKeysB.length) return false;
459
- const symbolKeysA = Object.getOwnPropertySymbols(objA);
460
- const symbolKeysB = Object.getOwnPropertySymbols(objB);
461
- if (symbolKeysA.length !== symbolKeysB.length) return false;
462
- const symbolKeysBSet = new Set(symbolKeysB);
463
- for (const key of stringKeysA) if (!objHasOwn.call(objB, key)) return false;
464
- for (const key of symbolKeysA) if (!symbolKeysBSet.has(key)) return false;
465
- for (const key of stringKeysA) if (!deepEqualInner(recA[key], recB[key], visited)) return false;
466
- for (const key of symbolKeysA) if (!deepEqualInner(recA[key], recB[key], visited)) return false;
467
- return true;
468
- }
469
-
470
- //#endregion
471
- //#region src/internal/structural/deep-omit.ts
472
- /**
473
- * Creates a deep copy of `value` with certain keys removed according to the
474
- * provided rules.
475
- *
476
- * Walks the object tree and skips keys that match `ignoreKeys` /
477
- * `ignoreKeyPredicate`. Built-in atomic types that `deepEqual` compares by
478
- * value (Date, RegExp, Map, Set, TypedArrays, DataView) are cloned by type
479
- * rather than walked, since their internal structure has no key filtering to
480
- * apply. Types that `deepEqual` compares by reference (Error, ArrayBuffer,
481
- * SharedArrayBuffer, Promise, WeakMap, WeakSet) are passed through by
482
- * reference, so `deepEqualExcept(x, x)` stays reflexive. Cycles are
483
- * preserved: a cycle `a → a` clones to `a' → a'`. Arrays retain sparse
484
- * holes and all non-ignored own properties, including symbol keys.
485
- *
486
- * **Shared references.** Without `ignoreKeyPredicate`, an object reached
487
- * via several paths dedupes to a single clone. With a predicate, each
488
- * path gets its own clone, because the predicate may decide differently per
489
- * path, so memoising the first path's result would be wrong. This is
490
- * inherently exponential for diamond-shaped sharing (a node reachable
491
- * via 2^n paths is cloned 2^n times); the walk aborts with a descriptive
492
- * error after {@link PATH_SENSITIVE_VISIT_BUDGET} node visits instead of
493
- * hanging the process.
494
- *
495
- * **Prototype-pollution safety.** `__proto__` and `constructor` keys
496
- * encountered as *own* properties of the input (typical of `JSON.parse`
497
- * output) are copied as inert data properties via `Object.defineProperty`
498
- * so the clone graph cannot bleed into `Object.prototype`.
499
- *
500
- * **Class instances.** When the input is a class instance, the clone is
501
- * built via `Object.create(proto)` so the prototype is preserved, but the
502
- * constructor is NOT re-invoked, so class invariants enforced by the
503
- * constructor are not re-checked. `deepOmit` is therefore best used for
504
- * comparison/serialisation (`voEqualsExcept`, `deepEqualExcept`), not as
505
- * a general-purpose clone for behaviour-carrying objects.
506
- *
507
- * @param value - The value to create a deep copy from
508
- * @param options - Options specifying which keys to ignore
509
- * @returns A deep copy of `value` with specified keys removed
510
- */
511
- function deepOmit(value, options) {
512
- const visited = /* @__PURE__ */ new WeakMap();
513
- return omitInternal(value, options, options.ignoreKeys ? new Set(options.ignoreKeys) : void 0, [], visited, options.ignoreKeyPredicate ? { visits: 0 } : void 0);
514
- }
515
- /**
516
- * Maximum object-node visits for a single path-sensitive `deepOmit` walk.
517
- * Per-path cloning expands exponentially on diamond-shaped sharing; past
518
- * this bound the walk throws instead of hanging the process. One million
519
- * visits covers any realistically tree-shaped input.
520
- */
521
- const PATH_SENSITIVE_VISIT_BUDGET = 1e6;
522
- function omitInternal(value, options, ignoreKeys, path, visited, budget) {
523
- if (value === null) return value;
524
- if (typeof value !== "object") return value;
525
- const obj = value;
526
- if (visited.has(obj)) return visited.get(obj);
527
- if (budget && ++budget.visits > PATH_SENSITIVE_VISIT_BUDGET) throw new Error(`deepOmit: exceeded ${PATH_SENSITIVE_VISIT_BUDGET} node visits. With ignoreKeyPredicate, objects reached via shared references are cloned once per path (the predicate may decide differently per path), which expands exponentially on diamond-shaped sharing. Restructure the input to a tree, or use ignoreKeys for path-independent filtering.`);
528
- if (Array.isArray(obj)) {
529
- const arr = obj;
530
- const clone = new Array(arr.length);
531
- const lengthDescriptor = Object.getOwnPropertyDescriptor(arr, "length");
532
- visited.set(obj, clone);
533
- for (const key of Reflect.ownKeys(arr)) {
534
- if (key === "length") continue;
535
- const segment = arrayPathSegment(key);
536
- if (typeof segment !== "number" && shouldIgnoreKey(key, path, ignoreKeys, options)) continue;
537
- const descriptor = Object.getOwnPropertyDescriptor(arr, key);
538
- if (descriptor === void 0) continue;
539
- path.push(segment);
540
- if ("value" in descriptor) descriptor.value = omitInternal(descriptor.value, options, ignoreKeys, path, visited, budget);
541
- Object.defineProperty(clone, key, descriptor);
542
- path.pop();
543
- }
544
- if (lengthDescriptor !== void 0) Object.defineProperty(clone, "length", lengthDescriptor);
545
- if (budget) visited.delete(obj);
546
- return clone;
547
- }
548
- const tag = Object.prototype.toString.call(obj);
549
- if (isBuiltInObject(obj, tag)) {
550
- const builtInClone = cloneBuiltIn(obj, tag);
551
- visited.set(obj, builtInClone);
552
- return builtInClone;
553
- }
554
- if (isOpaqueExoticTag(tag)) {
555
- visited.set(obj, obj);
556
- return obj;
557
- }
558
- const clone = Object.create(Object.getPrototypeOf(obj));
559
- visited.set(obj, clone);
560
- const stringKeys = Object.getOwnPropertyNames(obj);
561
- const symbolKeys = Object.getOwnPropertySymbols(obj);
562
- for (const key of [...stringKeys, ...symbolKeys]) {
563
- if (shouldIgnoreKey(key, path, ignoreKeys, options)) continue;
564
- path.push(key);
565
- assignOwn(clone, key, omitInternal(obj[key], options, ignoreKeys, path, visited, budget), Object.getOwnPropertyDescriptor(obj, key)?.enumerable ?? true);
566
- path.pop();
567
- }
568
- if (budget) visited.delete(obj);
569
- return clone;
570
- }
571
- function arrayPathSegment(key) {
572
- if (typeof key === "symbol") return key;
573
- const index = Number(key);
574
- return Number.isInteger(index) && index >= 0 && index < 4294967295 && String(index) === key ? index : key;
575
- }
576
- /**
577
- * Assigns `value` as an OWN data property on `target` without going through
578
- * any inherited setter; critically, it never invokes the `__proto__` setter
579
- * even when `key === "__proto__"`. Required to defeat prototype-pollution
580
- * payloads that ship `__proto__` as a parsed-JSON own key.
581
- */
582
- function assignOwn(target, key, value, enumerable = true) {
583
- Object.defineProperty(target, key, {
584
- value,
585
- writable: true,
586
- enumerable,
587
- configurable: true
588
- });
589
- }
590
- /**
591
- * Clones a built-in atomic type by case. Falls back to `structuredClone`
592
- * for anything not explicitly enumerated (e.g. DataView, TypedArrays,
593
- * Boolean/Number/String wrappers, all of which `deepEqual` compares by
594
- * value). Types that `deepEqual` compares BY REFERENCE (the shared
595
- * {@link REFERENCE_COMPARED_TAGS} set) are passed through by reference
596
- * instead; cloning them would make `deepEqualExcept(x, x)` false.
597
- * Promise/WeakMap/WeakSet additionally cannot be cloned at all
598
- * (`structuredClone` rejects them).
599
- */
600
- function cloneBuiltIn(obj, tag) {
601
- if (REFERENCE_COMPARED_TAGS.has(tag)) return obj;
602
- switch (tag) {
603
- case "[object Date]": return new Date(obj.getTime());
604
- case "[object RegExp]": {
605
- const re = obj;
606
- const copy = new RegExp(re.source, re.flags);
607
- copy.lastIndex = re.lastIndex;
608
- return copy;
609
- }
610
- case "[object Map]": return new Map(obj);
611
- case "[object Set]": return new Set(obj);
612
- default: return structuredClone(obj);
613
- }
614
- }
615
- function shouldIgnoreKey(key, path, ignoreKeys, options) {
616
- if (ignoreKeys?.has(key)) return true;
617
- if (options.ignoreKeyPredicate?.(key, path.slice())) return true;
618
- return false;
619
- }
620
-
621
- //#endregion
622
- //#region src/internal/structural/deep-equal-except.ts
623
- /**
624
- * Performs a deep equality comparison between two values after omitting specified keys.
625
- *
626
- * This function first removes the specified keys from both values using `deepOmit`,
627
- * then performs a deep equality check using `deepEqual`.
628
- *
629
- * @param a - The first value to compare
630
- * @param b - The second value to compare
631
- * @param options - Options specifying which keys to omit before comparison
632
- * @returns `true` if the values are deeply equal after omitting specified keys, `false` otherwise
633
- *
634
- * @example
635
- * ```ts
636
- * const obj1 = { id: 1, name: "Alice", updatedAt: "2024-01-01" };
637
- * const obj2 = { id: 2, name: "Alice", updatedAt: "2024-01-02" };
638
- *
639
- * deepEqualExcept(obj1, obj2, { ignoreKeys: ["id", "updatedAt"] }); // true
640
- * ```
641
- */
642
- function deepEqualExcept(a, b, options) {
643
- const prunedA = deepOmit(a, options);
644
- const prunedB = deepOmit(b, options);
645
- return deepEqual(prunedA, prunedB);
646
- }
647
-
648
- //#endregion
649
- //#region src/domain/value-object/value-object.ts
650
- /**
651
- * `Object.freeze` does not protect internal slots: a frozen Date still
652
- * accepts `setTime`, a frozen Map still accepts `set`. To make the
653
- * "deeply immutable" guarantee real, the mutator methods are shadowed
654
- * with own throwing functions BEFORE the freeze. The shadows are
655
- * non-enumerable, so they are invisible to `Object.keys`/spread (deep
656
- * equality is unaffected) and `structuredClone` drops them (a `vo()`
657
- * round-trip never sees them).
658
- */
659
- const DATE_MUTATORS = [
660
- "setTime",
661
- "setMilliseconds",
662
- "setUTCMilliseconds",
663
- "setSeconds",
664
- "setUTCSeconds",
665
- "setMinutes",
666
- "setUTCMinutes",
667
- "setHours",
668
- "setUTCHours",
669
- "setDate",
670
- "setUTCDate",
671
- "setMonth",
672
- "setUTCMonth",
673
- "setFullYear",
674
- "setUTCFullYear",
675
- "setYear"
676
- ];
677
- const mutationThrowers = /* @__PURE__ */ new Map();
678
- const mapEntries$1 = Map.prototype.entries;
679
- const setValues$1 = Set.prototype.values;
680
- function mutationThrower(typeName, method) {
681
- const key = `${typeName}.${method}`;
682
- let thrower = mutationThrowers.get(key);
683
- if (!thrower) {
684
- thrower = function throwFrozenMutation() {
685
- throw new TypeError(`Cannot call ${method}() on a ${typeName} inside a deeply frozen value`);
686
- };
687
- mutationThrowers.set(key, thrower);
688
- }
689
- return thrower;
690
- }
691
- const shadowDescriptor = {
692
- value: void 0,
693
- writable: false,
694
- enumerable: false,
695
- configurable: false
696
- };
697
- /** Returns whether the shadows were installed. */
698
- function shadowMutators(obj, typeName, methods) {
699
- if (!Object.isExtensible(obj)) return false;
700
- for (const method of methods) {
701
- shadowDescriptor.value = mutationThrower(typeName, method);
702
- Object.defineProperty(obj, method, shadowDescriptor);
703
- }
704
- return true;
705
- }
706
- const VALUE_OBJECT_CLASS = Symbol.for("@shirudo/ddd-kit/value-object-class/v1");
707
- function recordValueObjectClass(instance, valueObjectClass) {
708
- Object.defineProperty(instance, VALUE_OBJECT_CLASS, {
709
- value: valueObjectClass,
710
- enumerable: false,
711
- writable: false,
712
- configurable: false
713
- });
714
- }
715
- function isValueObjectInstance(value) {
716
- if (value === null || typeof value !== "object") return false;
717
- const record = Reflect.getOwnPropertyDescriptor(value, VALUE_OBJECT_CLASS);
718
- if (record === void 0 || typeof record.value !== "function" || record.enumerable !== false || record.writable !== false || record.configurable !== false) return false;
719
- const props = Reflect.getOwnPropertyDescriptor(value, "props");
720
- return props !== void 0 && isSealedProps(props.value);
721
- }
722
- function isSealedProps(props) {
723
- if (typeof props !== "object" || props === null) return false;
724
- return Object.isFrozen(props) || builtInTagWithoutInvokingAccessors(props) === "[object RegExp]";
725
- }
726
- function looksLikeValueObject(instance) {
727
- if (Object.hasOwn(instance, VALUE_OBJECT_CLASS)) return true;
728
- const props = Reflect.getOwnPropertyDescriptor(instance, "props");
729
- return props !== void 0 && isSealedProps(props.value);
730
- }
731
- function openValueObjectKeys(instance) {
732
- return Reflect.ownKeys(instance).filter((key) => key !== "props" && key !== VALUE_OBJECT_CLASS);
733
- }
734
- function rejectValueObjectAsInput(input, entry) {
735
- if (isValueObjectInstance(input)) throw new TypeError(`${entry} does not accept a value object as its input: nest the value object under a key, or pass its props`);
736
- }
737
- /**
738
- * Deep freezes an object and all its nested properties recursively, then
739
- * returns it. Iterates both string-keyed and symbol-keyed own properties
740
- * so the freeze symmetry matches `deepEqual` (which also considers symbol
741
- * keys). Handles circular references by tracking visited objects.
742
- *
743
- * Note: `deepFreeze` mutates its argument in place; it sets `[[Frozen]]`
744
- * on the object you pass in. Callers that need to avoid touching the
745
- * input (e.g. `vo()`) should deep-clone first.
746
- *
747
- * Date/Map/Set keep internal-slot mutability under `Object.freeze`
748
- * (`setTime`, `set`, `add`, … still work on frozen instances), so their
749
- * mutator methods are shadowed with throwing own properties and Map/Set
750
- * contents are frozen recursively. The shadows are non-enumerable:
751
- * invisible to `Object.keys`, spread, `deepEqual`, and `structuredClone`.
752
- *
753
- * The shadowing is deny-by-enumeration: only the mutators known at
754
- * release time are blocked. If the runtime grows a NEW mutator (e.g. the
755
- * stage-3 `Map.prototype.getOrInsert` upsert proposal), it is not blocked
756
- * until the list is updated. Treat the mutator blocking as a guard rail,
757
- * not a security boundary.
758
- *
759
- * Two kinds of built-in pass through unfrozen. ArrayBuffer views
760
- * (TypedArrays, DataView): the spec forbids freezing a view with elements,
761
- * and a freeze cannot protect the underlying buffer, so their contents stay
762
- * mutable. RegExp: pattern and flags live in immutable internal slots, and
763
- * the only own data property, `lastIndex`, is scan state that every global
764
- * or sticky match writes. A frozen RegExp protects nothing and throws on
765
- * the first such match, so the RegExp keeps matching instead. A view or a
766
- * RegExp passes through whole: an expando property on it stays open, and
767
- * the walk does not enter its subtree.
768
- */
769
- const DEEP_FROZEN = /* @__PURE__ */ new WeakSet();
770
- const KIT_SHADOWED = /* @__PURE__ */ new WeakSet();
771
- function deepFreeze(obj) {
772
- const walk = {
773
- cyclic: false,
774
- sealed: [],
775
- done: /* @__PURE__ */ new Map(),
776
- inProgress: /* @__PURE__ */ new Set()
777
- };
778
- freezeDeep(obj, walk);
779
- if (!walk.cyclic) for (const object of walk.sealed) DEEP_FROZEN.add(object);
780
- return obj;
781
- }
782
- /** Freezes `obj` and its subtree; returns whether the subtree is sealed. */
783
- function freezeDeep(obj, walk) {
784
- if (obj === null || typeof obj !== "object") return true;
785
- if (DEEP_FROZEN.has(obj)) return true;
786
- if (ArrayBuffer.isView(obj)) return true;
787
- const finished = walk.done.get(obj);
788
- if (finished !== void 0) return finished;
789
- if (walk.inProgress.has(obj)) {
790
- walk.cyclic = true;
791
- return true;
792
- }
793
- const builtInTag = builtInTagWithoutInvokingAccessors(obj);
794
- if (builtInTag === "[object RegExp]") return true;
795
- walk.inProgress.add(obj);
796
- let sealed = true;
797
- if (builtInTag === "[object Date]" || builtInTag === "[object Map]" || builtInTag === "[object Set]") {
798
- let shadowed = KIT_SHADOWED.has(obj);
799
- if (builtInTag === "[object Date]") shadowed = shadowMutators(obj, "Date", DATE_MUTATORS) || shadowed;
800
- else if (builtInTag === "[object Map]") {
801
- for (const [key, value] of obj) {
802
- if (!freezeDeep(key, walk)) sealed = false;
803
- if (!freezeDeep(value, walk)) sealed = false;
804
- }
805
- shadowed = shadowMutators(obj, "Map", [
806
- "set",
807
- "delete",
808
- "clear"
809
- ]) || shadowed;
810
- } else if (builtInTag === "[object Set]") {
811
- for (const member of obj) if (!freezeDeep(member, walk)) sealed = false;
812
- shadowed = shadowMutators(obj, "Set", [
813
- "add",
814
- "delete",
815
- "clear"
816
- ]) || shadowed;
817
- }
818
- if (shadowed) KIT_SHADOWED.add(obj);
819
- else sealed = false;
820
- }
821
- const keys = Reflect.ownKeys(obj);
822
- for (const key of keys) {
823
- const value = obj[key];
824
- if (value !== null && typeof value === "object") {
825
- if (!freezeDeep(value, walk)) sealed = false;
826
- }
827
- }
828
- Object.freeze(obj);
829
- walk.inProgress.delete(obj);
830
- walk.done.set(obj, sealed);
831
- if (sealed) walk.sealed.push(obj);
832
- return sealed;
833
- }
834
- /**
835
- * Deep clone used by `vo()` and the `ValueObject` constructor.
836
- *
837
- * Plain objects, arrays, and Map values are walked manually so
838
- * that symbol-keyed properties survive (which `structuredClone` silently
839
- * drops; they would otherwise be invisible to `voEquals`, whose
840
- * `deepEqual` DOES consider symbol keys) and shared references / cycles
841
- * keep their identity across Map boundaries. Function values throw,
842
- * preserving `vo()`'s documented data-not-behaviour gate. Built-ins without
843
- * immutable value semantics throw a descriptive `TypeError`. A kit
844
- * `ValueObject` instance is admitted by reference (see
845
- * `VALUE_OBJECT_CLASS`). Every other custom class instance and every
846
- * subclass of a built-in is rejected because cloning it without invoking
847
- * its constructor can silently lose private or non-enumerable state. Map
848
- * keys and Set members must be primitive because their equality is
849
- * identity-based and object identity cannot survive defensive cloning.
850
- * Accessor properties are rejected without invoking them. Admitted atomic
851
- * built-ins (Date, RegExp and primitive wrappers) delegate to
852
- * `structuredClone`, brand-verified so a `Symbol.toStringTag` spoofer is
853
- * walked as the plain object it is. `__proto__` own keys are copied as
854
- * inert data properties.
855
- */
856
- function cloneForVo(value, visited) {
857
- if (typeof value === "function") throw new TypeError("vo() does not accept function values: Value Objects are data, not behaviour");
858
- if (value === null || typeof value !== "object") return value;
859
- const obj = value;
860
- if (ArrayBuffer.isView(obj)) throwUnsupportedValueSemantics(builtInTagWithoutInvokingAccessors(obj) ?? "[object ArrayBuffer view]");
861
- if (visited.has(obj)) return visited.get(obj);
862
- if (Array.isArray(obj)) {
863
- if (!hasIntrinsicPrototypeChain(obj, "Array")) throwUnsupportedClassInstance(obj);
864
- const clone = new Array(obj.length);
865
- visited.set(obj, clone);
866
- for (const key of Reflect.ownKeys(obj)) {
867
- if (key === "length") continue;
868
- const descriptor = Object.getOwnPropertyDescriptor(obj, key);
869
- if (descriptor === void 0) continue;
870
- if (!("value" in descriptor)) throwUnsupportedAccessorProperty();
871
- if (typeof key === "string" && !descriptor.enumerable) continue;
872
- descriptor.value = cloneForVo(descriptor.value, visited);
873
- Object.defineProperty(clone, key, descriptor);
874
- }
875
- return clone;
876
- }
877
- const tag = builtInTagWithoutInvokingAccessors(obj);
878
- if (tag !== void 0) {
879
- if (!hasIntrinsicPrototypeChain(obj)) throwUnsupportedClassInstance(obj);
880
- if (tag === "[object Map]") {
881
- const clone = /* @__PURE__ */ new Map();
882
- visited.set(obj, clone);
883
- for (const [key, entry] of mapEntries$1.call(obj)) {
884
- if (!isPrimitiveValue(key)) throw new TypeError("vo() Map keys must be primitive values to preserve value equality");
885
- clone.set(key, cloneForVo(entry, visited));
886
- }
887
- return clone;
888
- }
889
- if (tag === "[object Set]") {
890
- const clone = /* @__PURE__ */ new Set();
891
- visited.set(obj, clone);
892
- for (const member of setValues$1.call(obj)) {
893
- if (!isPrimitiveValue(member)) throw new TypeError("vo() Set members must be primitive values to preserve value equality");
894
- clone.add(member);
895
- }
896
- return clone;
897
- }
898
- if (tag === "[object Promise]" || tag === "[object WeakMap]" || tag === "[object WeakSet]") throw new TypeError(`vo() cannot clone a ${tag.slice(8, -1)}: Value Objects are plain data`);
899
- if (tag === "[object Error]" || tag === "[object ArrayBuffer]" || tag === "[object SharedArrayBuffer]") throwUnsupportedValueSemantics(tag);
900
- if (tag === "[object RegExp]") {
901
- const regExp = obj;
902
- if (regExp.global || regExp.sticky) throw new TypeError("vo() cannot accept a global or sticky RegExp: its lastIndex is mutable scan state, not an immutable value");
903
- }
904
- const builtInClone = structuredClone(obj);
905
- visited.set(obj, builtInClone);
906
- return builtInClone;
907
- }
908
- const prototype = Object.getPrototypeOf(obj);
909
- if (prototype !== null && (!isIntrinsicConstructorPrototype(prototype, "Object") || Object.getPrototypeOf(prototype) !== null)) {
910
- if (isValueObjectInstance(obj)) {
911
- const openKeys = openValueObjectKeys(obj);
912
- if (openKeys.length > 0) throwOpenValueObjectFields(openKeys);
913
- return obj;
914
- }
915
- throwUnsupportedClassInstance(obj);
916
- }
917
- const clone = Object.create(prototype === null ? null : Object.prototype);
918
- visited.set(obj, clone);
919
- for (const key of Reflect.ownKeys(obj)) {
920
- const descriptor = Object.getOwnPropertyDescriptor(obj, key);
921
- if (descriptor === void 0) continue;
922
- if (!("value" in descriptor)) throwUnsupportedAccessorProperty();
923
- if (typeof key === "string" && !descriptor.enumerable) continue;
924
- Object.defineProperty(clone, key, {
925
- value: cloneForVo(descriptor.value, visited),
926
- writable: true,
927
- enumerable: descriptor.enumerable,
928
- configurable: true
929
- });
930
- }
931
- return clone;
932
- }
933
- function throwUnsupportedClassInstance(instance) {
934
- const valueObjectHint = looksLikeValueObject(instance) ? ". A value object is recognized only when a copy of this kit version built it and its props are frozen" : "";
935
- throw new TypeError(`vo() cannot clone custom class instances: Value Objects are plain data${valueObjectHint}`);
936
- }
937
- function throwOpenValueObjectFields(keys) {
938
- throw new TypeError(`vo() cannot nest a value object with own fields outside props (${keys.map(String).join(", ")}): keep the state of a value object in props`);
939
- }
940
- function throwUnsupportedAccessorProperty() {
941
- throw new TypeError("vo() cannot clone accessor properties: Value Objects are plain data");
942
- }
943
- function throwUnsupportedValueSemantics(tag) {
944
- const name = tag.startsWith("[object ") ? tag.slice(8, -1) : tag;
945
- throw new TypeError(`vo() cannot accept ${name} values: Value Objects require immutable value semantics`);
946
- }
947
- function isPrimitiveValue(value) {
948
- return value === null || typeof value !== "object" && typeof value !== "function";
949
- }
950
- /**
951
- * Creates a deeply immutable value object from the given data.
952
- *
953
- * The input is first deep-cloned, then the clone is frozen, so calling
954
- * `vo(input)` never freezes the caller's own object graph as a
955
- * side-effect. Mutating the input afterwards does not bleed into the VO.
956
- * Symbol-keyed properties are preserved (matching `voEquals`). A kit
957
- * `ValueObject` instance nested in the input is kept by reference and
958
- * frozen in place; it must keep all of its state in `props`. A value
959
- * object as the input itself is rejected.
960
- * Function values and every other custom class instance are rejected
961
- * (Value Objects are plain data, not behaviour-bearing object graphs). Inputs must be trusted and
962
- * Proxy-free: ECMAScript provides no portable way to identify a transparent
963
- * Proxy without potentially executing its traps, so `vo()` is not a sandbox
964
- * for hostile in-process objects. Built-ins that cannot provide immutable,
965
- * value-based semantics are rejected instead of weakening the VO contract.
966
- *
967
- * @example
968
- * ```typescript
969
- * const nested = { lat: 52.5, lng: 13.4 };
970
- * const address = vo({ street: "Main St", coordinates: nested });
971
- * address.coordinates.lat = 99; // ❌ Cannot assign to read-only property
972
- * nested.lat = 0; // ✅ caller's input still mutable
973
- * ```
974
- */
975
- function vo(t) {
976
- rejectValueObjectAsInput(t, "vo()");
977
- return deepFreeze(cloneForVo(t, /* @__PURE__ */ new WeakMap()));
978
- }
979
- /**
980
- * Compares two value objects for equality based on their values.
981
- * Uses deep equality comparison that handles:
982
- * - Nested objects and arrays
983
- * - Primitives (including NaN)
984
- * - Dates, Maps, Sets, RegExp
985
- * - Symbol keys
986
- * - Circular references
987
- *
988
- * @param a - First value object
989
- * @param b - Second value object
990
- * @returns true if both objects have the same values, false otherwise
991
- *
992
- * @example
993
- * ```typescript
994
- * const money1 = vo({ amount: 100, currency: "USD" });
995
- * const money2 = vo({ amount: 100, currency: "USD" });
996
- * voEquals(money1, money2); // true
997
- *
998
- * const address1 = vo({
999
- * street: "Main St",
1000
- * coordinates: { lat: 52.5, lng: 13.4 }
1001
- * });
1002
- * const address2 = vo({
1003
- * street: "Main St",
1004
- * coordinates: { lat: 52.5, lng: 13.4 }
1005
- * });
1006
- * voEquals(address1, address2); // true
1007
- * ```
1008
- */
1009
- function voEquals(a, b) {
1010
- return deepEqual(a, b);
1011
- }
1012
- /**
1013
- * Compares two value objects for equality while ignoring specified keys.
1014
- * Useful for comparing value objects that contain metadata or optional fields
1015
- * that should not affect equality comparison.
1016
- *
1017
- * The walk enters a nested `ValueObject` instance like any other object,
1018
- * so inside it the path continues with `props`; `ignoreKeys: ["props"]`
1019
- * empties every nested value object. The key under which the kit records
1020
- * the class of the instance is never ignored.
1021
- *
1022
- * @param a - First value object
1023
- * @param b - Second value object
1024
- * @param options - Options specifying which keys to ignore during comparison
1025
- * @returns true if both objects have the same values (after ignoring specified keys), false otherwise
1026
- *
1027
- * @example
1028
- * ```typescript
1029
- * // Value object with metadata
1030
- * const address1 = vo({
1031
- * street: "Main St",
1032
- * city: "Berlin",
1033
- * metadata: { createdAt: "2024-01-01", updatedAt: "2024-01-02" }
1034
- * });
1035
- *
1036
- * const address2 = vo({
1037
- * street: "Main St",
1038
- * city: "Berlin",
1039
- * metadata: { createdAt: "2024-01-01", updatedAt: "2024-01-03" }
1040
- * });
1041
- *
1042
- * // Compare ignoring metadata timestamps
1043
- * voEqualsExcept(address1, address2, {
1044
- * ignoreKeys: ["updatedAt"],
1045
- * ignoreKeyPredicate: (key, path) => path.includes("metadata")
1046
- * }); // true
1047
- *
1048
- * // Compare ignoring all metadata
1049
- * voEqualsExcept(address1, address2, {
1050
- * ignoreKeyPredicate: (key, path) => path.includes("metadata")
1051
- * }); // true
1052
- * ```
1053
- */
1054
- function voEqualsExcept(a, b, options) {
1055
- return deepEqualExcept(a, b, keepValueObjectClass(options));
1056
- }
1057
- function keepValueObjectClass(options) {
1058
- const { ignoreKeys, ignoreKeyPredicate } = options;
1059
- return {
1060
- ...options,
1061
- ignoreKeys: ignoreKeys?.filter((key) => key !== VALUE_OBJECT_CLASS),
1062
- ignoreKeyPredicate: ignoreKeyPredicate && ((key, path) => key !== VALUE_OBJECT_CLASS && ignoreKeyPredicate(key, path))
1063
- };
1064
- }
1065
- /**
1066
- * Creates a value object with optional validation.
1067
- * Returns a Result type instead of throwing an error.
1068
- *
1069
- * Note: the Result covers VALIDATION failures only. Non-data values and
1070
- * built-ins without immutable value semantics still throw a
1071
- * `TypeError` from `vo()`; they cannot occur in parsed JSON and signal
1072
- * a programming error, not a validation failure.
1073
- *
1074
- * @param t - The data to convert into a value object
1075
- * @param validate - Validation function that returns true if valid
1076
- * @param errorMessage - Optional custom error message if validation fails
1077
- * @returns Result containing the value object if valid, or an error message if validation fails
1078
- *
1079
- * @example
1080
- * ```typescript
1081
- * const result = voWithValidation(
1082
- * { amount: 100, currency: "USD" },
1083
- * (m) => m.amount >= 0 && m.currency.length === 3,
1084
- * "Invalid money: amount must be non-negative and currency must be 3 characters"
1085
- * );
1086
- *
1087
- * if (result.ok) {
1088
- * console.log(result.value); // Use the value object
1089
- * } else {
1090
- * console.error(result.error); // Handle validation error
1091
- * }
1092
- * ```
1093
- */
1094
- function voWithValidation(t, validate, errorMessage) {
1095
- if (!validate(t)) return err(errorMessage ?? `Validation failed for value object: ${describeValue(t)}`);
1096
- return ok(vo(t));
1097
- }
1098
- /**
1099
- * Best-effort rendering of a value for the default validation-failure
1100
- * message. `JSON.stringify` throws for cyclic and BigInt-bearing values, and
1101
- * the error path of a Result-returning function must never throw itself.
1102
- */
1103
- function describeValue(value) {
1104
- try {
1105
- const json = JSON.stringify(value);
1106
- if (json !== void 0) return json;
1107
- } catch {}
1108
- return String(value);
1109
- }
1110
- /**
1111
- * Abstract base class for creating Value Objects.
1112
- * Value Objects are immutable and defined by their properties. A value
1113
- * object can hold other value objects in its props. Every instance
1114
- * records its class under an own symbol key, so `equals` compares a
1115
- * nested value object by class and props and does not call its `equals`
1116
- * method.
1117
- *
1118
- * @template T - The shape of the value object's properties
1119
- */
1120
- var ValueObject = class {
1121
- props;
1122
- /**
1123
- * Creates a new ValueObject.
1124
- * The plain-data properties are deep-cloned and then deeply
1125
- * frozen, so the caller's own object graph is never frozen or mutated,
1126
- * and later mutation of the input does not bleed into the value object.
1127
- *
1128
- * @param props - The properties of the value object
1129
- * @example
1130
- * ```ts
1131
- * class Money extends ValueObject<{ amount: number; currency: string }> {
1132
- * constructor(props: { amount: number; currency: string }) {
1133
- * super(props);
1134
- * }
1135
- *
1136
- * protected validate(props: { amount: number; currency: string }): void {
1137
- * if (props.amount < 0) throw new Error("Amount cannot be negative");
1138
- * }
1139
- * }
1140
- * ```
1141
- */
1142
- constructor(props) {
1143
- rejectValueObjectAsInput(props, "new ValueObject()");
1144
- this.validate(props);
1145
- this.props = deepFreeze(cloneForVo(props, /* @__PURE__ */ new WeakMap()));
1146
- recordValueObjectClass(this, this.constructor);
1147
- }
1148
- /**
1149
- * Optional validation hook that can be overridden by subclasses.
1150
- * Should throw an error if validation fails.
1151
- *
1152
- * @param props - The properties to validate
1153
- * @throws Error if validation fails
1154
- */
1155
- validate(props) {}
1156
- /**
1157
- * Checks if this value object is equal to another.
1158
- * Uses deep equality comparison on the properties and checks for constructor equality.
1159
- *
1160
- * @param other - The other value object to compare
1161
- * @returns true if the properties are deeply equal and constructors match
1162
- */
1163
- equals(other) {
1164
- if (other === null || other === void 0) return false;
1165
- if (this.constructor !== other.constructor) return false;
1166
- return deepEqual(this.props, other.props);
1167
- }
1168
- /**
1169
- * Creates a clone of the value object with optional property overrides.
1170
- *
1171
- * @param props - Optional properties to override
1172
- * @returns A new instance of the value object
1173
- */
1174
- clone(props) {
1175
- const Constructor = this.constructor;
1176
- const merged = {
1177
- ...this.props,
1178
- ...props || {}
1179
- };
1180
- for (const key of Reflect.ownKeys(this.props)) {
1181
- const descriptor = Object.getOwnPropertyDescriptor(this.props, key);
1182
- if (descriptor === void 0 || descriptor.enumerable) continue;
1183
- if (props && Object.hasOwn(props, key)) continue;
1184
- Object.defineProperty(merged, key, {
1185
- value: this.props[key],
1186
- writable: true,
1187
- enumerable: false,
1188
- configurable: true
1189
- });
1190
- }
1191
- return new Constructor(merged);
1192
- }
1193
- /**
1194
- * Serializes the value object to its raw properties for JSON operations.
1195
- *
1196
- * @returns The raw properties object
1197
- */
1198
- toJSON() {
1199
- return this.props;
1200
- }
1201
- };
1202
-
1203
- //#endregion
1204
- //#region src/internal/cooperative-brand.ts
1205
- /**
1206
- * A cooperative brand marks an object that a kit constructor produced, so
1207
- * a second loaded copy of the kit (duplicate npm dependency, dual CJS/ESM
1208
- * load, plugin bundle) recognizes it. A module-private WeakSet cannot do
1209
- * that: it is bound to one loaded copy. The brand is a `Symbol.for` key
1210
- * with the value `true`. The property is non-enumerable, so it never
1211
- * leaks into spreads, JSON, or equality, and non-writable and
1212
- * non-configurable, so nothing edits it after the stamp.
1213
- *
1214
- * The probe reads the brand as an OWN property with exactly these
1215
- * attributes and requires the carrier to be frozen. An object that
1216
- * inherits a branded object through its prototype can carry mutable own
1217
- * overrides; a branded object that is still open can change after the
1218
- * stamp. Neither is what the constructor produced. Every stamp site
1219
- * therefore freezes the carrier right after {@link stampCooperativeBrand}.
1220
- *
1221
- * The brand is forgeable BY DESIGN. It catches accidental hand-rolled
1222
- * literals; it is not a security boundary against code in the same
1223
- * process that fakes it on purpose.
1224
- *
1225
- * Internal utility (not exported from the package barrels).
1226
- */
1227
- function stampCooperativeBrand(target, brand) {
1228
- Object.defineProperty(target, brand, {
1229
- value: true,
1230
- enumerable: false,
1231
- writable: false,
1232
- configurable: false
1233
- });
1234
- }
1235
- /**
1236
- * Whether `value` carries `brand` as an own, non-enumerable, non-writable,
1237
- * non-configurable `true` and is frozen. A Proxy trap that throws reads
1238
- * as unbranded.
1239
- */
1240
- function hasCooperativeBrand(value, brand) {
1241
- if (value === null || typeof value !== "object" && typeof value !== "function") return false;
1242
- try {
1243
- const marker = Reflect.getOwnPropertyDescriptor(value, brand);
1244
- return marker?.value === true && marker.enumerable === false && marker.writable === false && marker.configurable === false && Object.isFrozen(value);
1245
- } catch {
1246
- return false;
1247
- }
1248
- }
1249
-
1250
- //#endregion
1251
- //#region src/internal/structural/detach-state.ts
4
+ //#region src/internal/structural/copy-plain-state.ts
1252
5
  /**
1253
6
  * Returns a copy of `state` that shares no object with the original.
1254
7
  * Throws a `TypeError` that names the path when the graph carries a value
@@ -1257,9 +10,11 @@ function hasCooperativeBrand(value, brand) {
1257
10
  * symbol-keyed, non-enumerable, or accessor property and an expando on a
1258
11
  * built-in are dropped. A function or a symbol value throws a raw
1259
12
  * `DataCloneError`. An Error, a Promise, a WeakMap, or a WeakSet cannot be
1260
- * detached at all. A SharedArrayBuffer and a view over one keep sharing
13
+ * copied at all. A SharedArrayBuffer and a view over one keep sharing
1261
14
  * their memory. A Proxy fails in the walk or in the clone; a failure in
1262
- * the clone is rethrown as a `TypeError` with the cause.
15
+ * the clone is rethrown as a `TypeError` with the cause. An error that a
16
+ * Proxy trap throws in the walk passes unchanged, so a coded error of the
17
+ * caller keeps its code.
1263
18
  *
1264
19
  * The walk reads the graph through property descriptors and walks a Map
1265
20
  * or a Set with the built-in iteration, so no accessor and no own iterator
@@ -1267,70 +22,52 @@ function hasCooperativeBrand(value, brand) {
1267
22
  * change the graph, and an accessor that a trap adds runs during the
1268
23
  * clone. A second walk checks the clone, so a value that a trap swapped in
1269
24
  * and the content of a built-in whose prototype was replaced are rejected
1270
- * too. The walks also count the data properties of records and arrays. A
1271
- * copy with another count throws, because a built-in whose prototype was
1272
- * replaced loses its own properties in the copy. `subject` names the state
1273
- * in the messages.
25
+ * too. A trap that changes the graph after the first walk can still hide a
26
+ * value that the copy drops: the walks catch accidents, not a hostile
27
+ * Proxy. The second walk also follows the steps of the first walk: the kind
28
+ * of each object and the key of each data property, in walk order. A copy
29
+ * that differs throws, because a built-in whose prototype was replaced is
30
+ * a built-in again in the copy and loses its own properties. `subject`
31
+ * names the state in the messages.
1274
32
  *
1275
33
  * Plain objects (from any realm), arrays, Dates, Maps, Sets, bigints, and
1276
34
  * typed arrays pass. A RegExp passes: pattern and flags survive the clone,
1277
35
  * and `lastIndex` restores as 0. The scan state of a global or sticky
1278
- * pattern is not domain data. A non-enumerable property on a built-in
1279
- * passes: it is the built-in's own machinery (`lastIndex`), not data. A
1280
- * non-enumerable symbol key passes anywhere: it is metadata by convention.
36
+ * pattern is not domain data. On a built-in, the mutator shadows of a deep
37
+ * freeze, the `lastIndex` of a RegExp, and the `length` of a boxed String
38
+ * pass: they are machinery, not data. Every other non-enumerable property
39
+ * throws. A non-enumerable symbol key passes anywhere: it is metadata by
40
+ * convention.
1281
41
  *
1282
- * The concrete entity uses it for a detached read DTO of a plain-data
42
+ * The concrete entity uses it for the plain state of a plain-data
1283
43
  * state. The snapshot model uses it for the captured DTO and the restored
1284
44
  * state. A state that carries a class-based child is mapped to plain data
1285
45
  * first, in the entity or in the model.
1286
46
  */
1287
- function detachState(state, subject = "detachState: state") {
1288
- return detach(state, subject, "clone");
47
+ function copyPlainState(state, subject = "copyPlainState: state") {
48
+ return copyState(state, subject, "clone");
1289
49
  }
1290
50
  /**
1291
- * {@link detachState} that also rejects what `deepFreeze` cannot make
51
+ * {@link copyPlainState} that also rejects what `deepFreeze` cannot make
1292
52
  * immutable: binary data (a typed array, a DataView, an ArrayBuffer, or a
1293
53
  * SharedArrayBuffer) and a RegExp. The walk rejects such a value at its
1294
54
  * tag, before it reads the indices of a typed array. Kit modules only.
1295
55
  */
1296
- function detachFreezableState(state, subject) {
1297
- return detach(state, subject, "freeze");
56
+ function copyFreezableState(state, subject) {
57
+ return copyState(state, subject, "freeze");
1298
58
  }
1299
- /**
1300
- * Throws the `TypeError` of {@link detachFreezableState} and copies
1301
- * nothing. A caller that has a clone already checks it with this, so the
1302
- * rule is the same. Kit modules only.
1303
- */
1304
- function assertFreezableState(state, subject) {
1305
- assertDetachable(state, "", {
1306
- seen: /* @__PURE__ */ new WeakSet(),
1307
- subject,
1308
- rule: "freeze",
1309
- dataProperties: 0
1310
- });
1311
- }
1312
- function detach(state, subject, rule) {
1313
- const original = {
1314
- seen: /* @__PURE__ */ new WeakSet(),
1315
- subject,
1316
- rule,
1317
- dataProperties: 0
1318
- };
1319
- assertDetachable(state, "", original);
59
+ function copyState(state, subject, rule) {
60
+ const recording = new StepRecording();
61
+ assertCopyable(state, "", newWalk(subject, rule, recording));
1320
62
  let copy;
1321
63
  try {
1322
64
  copy = structuredClone(state);
1323
65
  } catch (cause) {
1324
- throw new TypeError(`${subject} holds a Proxy or a host object that cannot be cloned; map it to plain data`, { cause });
66
+ throw new TypeError(`${subject} holds a Proxy or a host object that cannot be cloned, or a view over a detached or shrunk buffer; the cause names the value; map it to plain data`, { cause });
1325
67
  }
1326
- const copied = {
1327
- seen: /* @__PURE__ */ new WeakSet(),
1328
- subject,
1329
- rule,
1330
- dataProperties: 0
1331
- };
1332
- assertDetachable(copy, "", copied);
1333
- if (copied.dataProperties !== original.dataProperties) throw new TypeError(`${subject} changed in the copy: the state has ${original.dataProperties} data properties and the copy has ${copied.dataProperties}; a built-in whose prototype was replaced loses its own properties in the copy, and a Proxy trap can change the graph; map it to plain data`);
68
+ const comparison = new StepComparison(recording, subject);
69
+ assertCopyable(copy, "", newWalk(subject, rule, comparison));
70
+ comparison.assertComplete();
1334
71
  return copy;
1335
72
  }
1336
73
  const INDEX_KEY = /^(0|[1-9]\d*)$/;
@@ -1341,9 +78,77 @@ const BINARY_DATA_TAGS = /* @__PURE__ */ new Set([
1341
78
  "[object SharedArrayBuffer]"
1342
79
  ]);
1343
80
  const REGEXP_TAG = "[object RegExp]";
81
+ const STRING_TAG = "[object String]";
1344
82
  const mapEntries = Map.prototype.entries;
1345
83
  const setValues = Set.prototype.values;
1346
- function assertDetachable(value, path, walk) {
84
+ const RECORD_STEP = Symbol("record");
85
+ const ARRAY_STEP = Symbol("array");
86
+ const builtInSteps = /* @__PURE__ */ new Map();
87
+ function builtInStep(tag) {
88
+ let kind = builtInSteps.get(tag);
89
+ if (kind === void 0) {
90
+ kind = Symbol(tag);
91
+ builtInSteps.set(tag, kind);
92
+ }
93
+ return kind;
94
+ }
95
+ /**
96
+ * The steps of the walk over the original, in walk order.
97
+ */
98
+ var StepRecording = class {
99
+ steps = [];
100
+ take(next) {
101
+ this.steps.push(next);
102
+ }
103
+ };
104
+ /**
105
+ * The walk over the copy compares each of its steps with the step of the
106
+ * walk over the original at the same position. Structured clone keeps the
107
+ * order of the keys, of Map entries, and of Set members, and it keeps
108
+ * shared references, so the walk over an unchanged copy takes the same
109
+ * steps. A step that differs, or a step that is missing, means that the
110
+ * copy changed.
111
+ */
112
+ var StepComparison = class {
113
+ recording;
114
+ subject;
115
+ #position = 0;
116
+ constructor(recording, subject) {
117
+ this.recording = recording;
118
+ this.subject = subject;
119
+ }
120
+ take(next, path) {
121
+ const expected = this.recording.steps[this.#position];
122
+ if (expected !== next) throwChangedInCopy(this.subject, path, expected, next);
123
+ this.#position += 1;
124
+ }
125
+ assertComplete() {
126
+ if (this.#position < this.recording.steps.length) throwChangedInCopy(this.subject, "", this.recording.steps[this.#position], void 0);
127
+ }
128
+ };
129
+ function newWalk(subject, rule, trace) {
130
+ return {
131
+ seen: /* @__PURE__ */ new WeakSet(),
132
+ subject,
133
+ rule,
134
+ trace
135
+ };
136
+ }
137
+ function step(walk, next, path) {
138
+ walk.trace.take(next, path);
139
+ }
140
+ function throwChangedInCopy(subject, path, expected, found) {
141
+ throw new TypeError(`${subject}${path} changed in the copy: the state has ${describeStep(expected)} there, and the copy has ${describeStep(found)}; a built-in whose prototype was replaced is a built-in again in the copy and loses its own properties, and a Proxy trap can change the graph; map it to plain data`);
142
+ }
143
+ function describeStep(step) {
144
+ if (step === void 0) return "nothing";
145
+ if (typeof step === "string") return `the key "${step}"`;
146
+ if (step === RECORD_STEP) return "a record";
147
+ if (step === ARRAY_STEP) return "an array";
148
+ const name = builtInTypeName(step.description);
149
+ return /^[AEIOU]/.test(name) ? `an ${name}` : `a ${name}`;
150
+ }
151
+ function assertCopyable(value, path, walk) {
1347
152
  if (typeof value === "function") throw new TypeError(`${walk.subject}${path} is a function; map it to plain data`);
1348
153
  if (typeof value === "symbol") throw new TypeError(`${walk.subject}${path} is a symbol; map it to plain data`);
1349
154
  if (value === null || typeof value !== "object") return;
@@ -1352,45 +157,52 @@ function assertDetachable(value, path, walk) {
1352
157
  walk.seen.add(object);
1353
158
  if (Array.isArray(object)) {
1354
159
  if (!hasIntrinsicPrototypeChain(object, "Array")) throwClassInstance(object, path, walk.subject);
1355
- assertOwnPropertiesDetachable(object, path, walk, "array");
160
+ step(walk, ARRAY_STEP, path);
161
+ assertOwnPropertiesCopyable(object, path, walk, "array");
1356
162
  return;
1357
163
  }
1358
164
  const tag = builtInTagWithoutInvokingAccessors(object);
1359
165
  if (tag !== void 0) {
1360
166
  if (walk.rule === "freeze") assertFreezable(tag, path, walk.subject);
1361
167
  if (!hasIntrinsicPrototypeChain(object)) throwClassInstance(object, path, walk.subject);
168
+ step(walk, builtInStep(tag), path);
1362
169
  if (tag === "[object Map]") {
1363
170
  let index = 0;
1364
171
  for (const [key, entry] of mapEntries.call(object)) {
1365
- assertDetachable(key, `${path}<map key #${index}>`, walk);
1366
- assertDetachable(entry, `${path}<map value #${index}>`, walk);
172
+ assertCopyable(key, `${path}<map key #${index}>`, walk);
173
+ assertCopyable(entry, `${path}<map value #${index}>`, walk);
1367
174
  index++;
1368
175
  }
1369
176
  } else if (tag === "[object Set]") {
1370
177
  let index = 0;
1371
178
  for (const member of setValues.call(object)) {
1372
- assertDetachable(member, `${path}<set member #${index}>`, walk);
179
+ assertCopyable(member, `${path}<set member #${index}>`, walk);
1373
180
  index++;
1374
181
  }
1375
- } else if (tag === "[object Promise]" || tag === "[object WeakMap]" || tag === "[object WeakSet]") throw new TypeError(`${walk.subject}${path} is a ${tag.slice(8, -1)} and cannot be detached`);
182
+ } else if (tag === "[object Promise]" || tag === "[object WeakMap]" || tag === "[object WeakSet]") throw new TypeError(`${walk.subject}${path} is a ${builtInTypeName(tag)} and cannot be copied`);
1376
183
  else if (tag === "[object Error]") throw new TypeError(`${walk.subject}${path} is an Error; map it to plain data`);
1377
184
  else if (sharesMemory(object, tag)) throw new TypeError(`${walk.subject}${path} is backed by a SharedArrayBuffer and the copy would share its memory; map it to plain data`);
1378
- assertOwnPropertiesDetachable(object, path, walk, "built-in");
185
+ assertOwnPropertiesCopyable(object, path, walk, tag);
1379
186
  return;
1380
187
  }
1381
188
  const prototype = Object.getPrototypeOf(object);
1382
189
  if (!(prototype === null || isIntrinsicConstructorPrototype(prototype, "Object") && Object.getPrototypeOf(prototype) === null)) throwClassInstance(object, path, walk.subject);
1383
- assertOwnPropertiesDetachable(object, path, walk, "record");
190
+ step(walk, RECORD_STEP, path);
191
+ assertOwnPropertiesCopyable(object, path, walk, "record");
1384
192
  }
1385
193
  /**
1386
194
  * Audits the own properties the clone would copy or drop. The clone keeps
1387
195
  * the enumerable own keys of a record and of an array, expandos included,
1388
- * and drops every own key of another built-in. `length` on an array and
1389
- * the non-enumerable keys of a built-in (`lastIndex`) are its own
1390
- * machinery, not data. Index keys of a typed array or a boxed String are
1391
- * its content and pass as such.
196
+ * and drops every own key of another built-in. `length` on an array is
197
+ * its own machinery, not data, and so are the keys that
198
+ * {@link isBuiltInMachinery} names. The index keys of a built-in below its
199
+ * content length are its content and pass as such. Another index key of a
200
+ * built-in is an expando. `owner` is the kind of `object`: an array, a
201
+ * record, or the tag of a built-in.
1392
202
  */
1393
- function assertOwnPropertiesDetachable(object, path, walk, kind) {
203
+ function assertOwnPropertiesCopyable(object, path, walk, owner) {
204
+ const tag = owner === "array" || owner === "record" ? void 0 : owner;
205
+ const contentLength = tag === void 0 ? 0 : contentLengthOf(object, tag);
1394
206
  for (const key of Reflect.ownKeys(object)) {
1395
207
  const descriptor = Object.getOwnPropertyDescriptor(object, key);
1396
208
  if (descriptor === void 0) continue;
@@ -1398,20 +210,41 @@ function assertOwnPropertiesDetachable(object, path, walk, kind) {
1398
210
  if (!descriptor.enumerable) continue;
1399
211
  throw new TypeError(`${walk.subject}${path} has a symbol-keyed property; map it to plain data`);
1400
212
  }
1401
- if (kind === "array" && key === "length") continue;
213
+ if (owner === "array" && key === "length") continue;
1402
214
  const isIndex = INDEX_KEY.test(key);
215
+ const memberPath = isIndex ? `${path}[${key}]` : `${path}.${key}`;
1403
216
  if (!descriptor.enumerable) {
1404
- if (kind === "built-in") continue;
1405
- throw new TypeError(`${walk.subject}${path}.${key} is not enumerable and the clone would drop it; map it to plain data`);
217
+ if (tag !== void 0 && isBuiltInMachinery(tag, key, descriptor)) continue;
218
+ throw new TypeError(`${walk.subject}${memberPath} is not enumerable and the clone would drop it; map it to plain data`);
1406
219
  }
1407
- if (kind === "built-in" && isIndex) continue;
1408
- const memberPath = isIndex ? `${path}[${key}]` : `${path}.${key}`;
220
+ if (tag !== void 0 && isIndex && Number(key) < contentLength) continue;
1409
221
  if (!("value" in descriptor)) throw new TypeError(`${walk.subject}${memberPath} is an accessor property; map it to plain data`);
1410
- if (kind === "built-in") throw new TypeError(`${walk.subject}${memberPath} is an expando on a ${constructorName(Object.getPrototypeOf(object)) ?? "built-in"} and the clone would drop it; map it to plain data`);
1411
- walk.dataProperties += 1;
1412
- assertDetachable(descriptor.value, memberPath, walk);
222
+ if (tag !== void 0) throw new TypeError(`${walk.subject}${memberPath} is an expando on a ${constructorName(Object.getPrototypeOf(object)) ?? "built-in"} and the clone would drop it; map it to plain data`);
223
+ step(walk, key, memberPath);
224
+ assertCopyable(descriptor.value, memberPath, walk);
1413
225
  }
1414
226
  }
227
+ /**
228
+ * Whether an own non-enumerable property of a built-in is machinery, not
229
+ * data: a function that shadows a mutator of a frozen Date, Map, or Set,
230
+ * the scan position of a RegExp, or the length of a boxed String. The
231
+ * clone drops the shadow and rebuilds the other two from the internal
232
+ * slots.
233
+ */
234
+ function isBuiltInMachinery(tag, key, descriptor) {
235
+ return isMutatorShadow(tag, key, descriptor) || tag === REGEXP_TAG && key === "lastIndex" || tag === STRING_TAG && key === "length";
236
+ }
237
+ /**
238
+ * The number of index keys that hold the content of a built-in: the
239
+ * elements of a typed array and the characters of a boxed String.
240
+ */
241
+ function contentLengthOf(object, tag) {
242
+ if (tag === "[object TypedArray]") return typedArrayLength.call(object);
243
+ if (tag === STRING_TAG) return stringValueOf.call(object).length;
244
+ return 0;
245
+ }
246
+ const typedArrayLength = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Uint8Array.prototype), "length")?.get;
247
+ const stringValueOf = String.prototype.valueOf;
1415
248
  const typedArrayBuffer = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Uint8Array.prototype), "buffer")?.get;
1416
249
  const dataViewBuffer = Object.getOwnPropertyDescriptor(DataView.prototype, "buffer")?.get;
1417
250
  function sharesMemory(object, tag) {
@@ -1506,7 +339,7 @@ const defaultEventIdFactory = () => crypto.randomUUID();
1506
339
  * clock: () => new Date(),
1507
340
  * });
1508
341
  * order.confirm();
1509
- * recordPendingEvents(order, domainEvents);
342
+ * stampPendingEvents(order, domainEvents);
1510
343
  * ```
1511
344
  */
1512
345
  function createDomainEventFactory(options = {}) {
@@ -1517,7 +350,7 @@ function buildDomainEventFactory(options, createOperation) {
1517
350
  const eventIdFactory = options.eventIdFactory ?? defaultEventIdFactory;
1518
351
  const clock = options.clock ?? defaultClockFactory;
1519
352
  const { source } = options;
1520
- const create = ((type, payload, createOptions) => mintDomainEvent(type, payload, createOptions, eventIdFactory, clock, createOperation, source));
353
+ const create = ((type, payload, createOptions) => buildDomainEvent(type, payload, createOptions, eventIdFactory, clock, createOperation, source));
1521
354
  const createStamp = (stampOptions = {}) => {
1522
355
  const explicitOccurredAt = stampOptions.occurredAt === void 0 ? void 0 : copyValidEventDate(stampOptions.occurredAt);
1523
356
  if (stampOptions.eventId !== void 0) assertNonBlankEventField(stampOptions.eventId, "eventId", "EVENT_ID_INVALID");
@@ -1544,91 +377,46 @@ function buildDomainEventFactory(options, createOperation) {
1544
377
  * with {@link createDomainEventFactory} for custom policy.
1545
378
  */
1546
379
  const defaultDomainEventFactory = buildDomainEventFactory({}, "createDomainEvent");
1547
- /**
1548
- * Creates a domain event with default values.
1549
- * Sets occurredAt to current date and schemaVersion to 1 if not provided.
1550
- *
1551
- * **Input ownership.** The event is deeply frozen, and `payload` and
1552
- * `metadata` are deep-cloned first, so the caller's own objects are never
1553
- * frozen in place and later mutation of them does not bleed into the
1554
- * event (same contract as `vo()`). The clone follows the plain-data event
1555
- * contract via `structuredClone`: functions, Promise, and WeakMap/WeakSet
1556
- * values throw a `TypeError`; symbol-keyed properties are not carried
1557
- * over. The clone is then checked with the rule of the in-memory event
1558
- * store: an `Error`, a host object such as a `File`, a binary buffer, and
1559
- * shared memory throw a `TypeError`. A class instance or an accessor becomes
1560
- * plain data in the clone.
1561
- *
1562
- * **For aggregate-internal events, prefer `this.createEvent(...)` on
1563
- * `StateStoredAggregate` / `EventSourcedAggregate`.** That helper auto-injects
1564
- * `aggregateId` (from `this.id`) and `aggregateType` (from the
1565
- * aggregate's declared `aggregateType` property), which downstream
1566
- * consumers (outbox dispatchers, projection handlers, audit logs)
1567
- * route by. The commit boundary validates that both fields are present
1568
- * and throws if they are missing, so a direct `createDomainEvent(...)`
1569
- * call inside an aggregate that forgets the options is caught at
1570
- * runtime. Record pending decisions in the application
1571
- * shell before repository persistence or outbox harvest.
1572
- *
1573
- * Use `createDomainEvent(...)` directly for events that don't belong to
1574
- * an aggregate: system events, integration events, configuration events,
1575
- * test fixtures. For those, set `aggregateId` / `aggregateType` in
1576
- * `options` if downstream consumers expect routing metadata.
1577
- *
1578
- * @param type - The event type
1579
- * @param payload - The event payload
1580
- * @param options - Optional event configuration (including `aggregateId`
1581
- * and `aggregateType` for routing)
1582
- * @returns A domain event
1583
- *
1584
- * @example
1585
- * ```typescript
1586
- * const event = createDomainEvent("OrderCreated", { orderId: "123" });
1587
- * ```
1588
- */
1589
- const RECORDED_EVENTS = /* @__PURE__ */ new WeakSet();
1590
- const UNCOMMITTED_EVENTS = /* @__PURE__ */ new WeakSet();
380
+ const STAMPED_EVENTS = /* @__PURE__ */ new WeakSet();
381
+ const UNSTAMPED_EVENTS = /* @__PURE__ */ new WeakSet();
1591
382
  const FACTORY_OWNED_EVENT_STAMPS = /* @__PURE__ */ new WeakSet();
1592
- const RECORDED_BRAND = Symbol.for("@shirudo/ddd-kit.mintedEvent");
1593
- const UNCOMMITTED_BRAND = Symbol.for("@shirudo/ddd-kit.uncommittedEvent");
1594
- function stampRecordedBrand(event) {
1595
- stampCooperativeBrand(event, RECORDED_BRAND);
383
+ const STAMPED_BRAND = Symbol.for("@shirudo/ddd-kit.mintedEvent");
384
+ const UNSTAMPED_BRAND = Symbol.for("@shirudo/ddd-kit.uncommittedEvent");
385
+ function brandStampedEvent(event) {
386
+ applyCooperativeBrand(event, STAMPED_BRAND);
1596
387
  }
1597
- function stampUncommittedBrand(event) {
1598
- stampCooperativeBrand(event, UNCOMMITTED_BRAND);
388
+ function brandUnstampedEvent(event) {
389
+ applyCooperativeBrand(event, UNSTAMPED_BRAND);
1599
390
  }
1600
391
  function isFactoryOwnedDomainEventStamp(stamp) {
1601
392
  return FACTORY_OWNED_EVENT_STAMPS.has(stamp);
1602
393
  }
1603
394
  /**
1604
- * Whether `event` is a recorded domain event: it came out of
1605
- * {@link createDomainEvent}, {@link createDomainEventFromFacts}, or
1606
- * {@link recordDomainEvent}, so it is deeply frozen with defensively copied
1607
- * payload and metadata. An uncommitted decision carries the other brand; see
1608
- * {@link isUncommittedDomainEvent}. Two tiers: events of THIS
395
+ * Whether `event` is a stamped domain event: it came out of
396
+ * {@link createDomainEvent}, {@link createStampedDomainEvent}, or
397
+ * {@link stampDomainEvent}, so it is deeply frozen with defensively copied
398
+ * payload and metadata. An unstamped decision carries the other brand; see
399
+ * {@link isUnstampedDomainEvent}. Two tiers: events of THIS
1609
400
  * loaded copy of the kit are verified through the module-private
1610
- * WeakSet; events minted by ANOTHER copy (duplicate dependency, dual
401
+ * WeakSet; events built by ANOTHER copy (duplicate dependency, dual
1611
402
  * CJS/ESM load) are recognized through a cooperative `Symbol.for`
1612
403
  * brand that code in the same process can fake. The gate catches
1613
404
  * accidents, not adversaries. Module-internal export for the aggregate
1614
405
  * recording paths; not part of the package entries.
1615
406
  */
1616
- function isRecordedDomainEvent(event) {
1617
- return RECORDED_EVENTS.has(event) || hasCooperativeBrand(event, RECORDED_BRAND);
1618
- }
1619
- /** Whether a value was created by {@link createUncommittedDomainEvent}. */
1620
- function isUncommittedDomainEvent(event) {
1621
- return UNCOMMITTED_EVENTS.has(event) || hasCooperativeBrand(event, UNCOMMITTED_BRAND);
407
+ function isStampedDomainEvent(event) {
408
+ return STAMPED_EVENTS.has(event) || hasCooperativeBrand(event, STAMPED_BRAND);
1622
409
  }
1623
- function createUncommittedDomainEvent(type, payload, options) {
1624
- return mintUncommittedDomainEvent(type, payload, options, "createUncommittedDomainEvent");
410
+ /** Whether a value is a kit-built unstamped event. */
411
+ function isUnstampedDomainEvent(event) {
412
+ return UNSTAMPED_EVENTS.has(event) || hasCooperativeBrand(event, UNSTAMPED_BRAND);
1625
413
  }
1626
414
  /**
1627
- * {@link createUncommittedDomainEvent} whose errors name `operation`, the
415
+ * {@link createUnstampedDomainEvent} whose errors name `operation`, the
1628
416
  * function that the caller called. Module-internal export; not part of the
1629
417
  * package entries.
1630
418
  */
1631
- function mintUncommittedDomainEvent(type, payload, options, operation) {
419
+ function buildUnstampedDomainEvent(type, payload, options, operation) {
1632
420
  assertProducerOwnedEventFields(type, options);
1633
421
  const event = {
1634
422
  type,
@@ -1637,41 +425,41 @@ function mintUncommittedDomainEvent(type, payload, options, operation) {
1637
425
  payload: cloneOwnedEventData(payload, "payload", operation),
1638
426
  schemaVersion: options?.schemaVersion ?? 1
1639
427
  };
1640
- stampUncommittedBrand(event);
1641
- const uncommitted = deepFreeze(event);
1642
- UNCOMMITTED_EVENTS.add(uncommitted);
1643
- return uncommitted;
1644
- }
1645
- /** Brands and freezes a kit-derived copy of an uncommitted event. */
1646
- function adoptUncommittedDomainEvent(copy) {
1647
- stampUncommittedBrand(copy);
428
+ brandUnstampedEvent(event);
429
+ const unstamped = deepFreeze(event);
430
+ UNSTAMPED_EVENTS.add(unstamped);
431
+ return unstamped;
432
+ }
433
+ /** Brands and freezes a kit-derived copy of an unstamped event. */
434
+ function adoptUnstampedDomainEvent(copy) {
435
+ brandUnstampedEvent(copy);
1648
436
  Object.freeze(copy);
1649
- UNCOMMITTED_EVENTS.add(copy);
437
+ UNSTAMPED_EVENTS.add(copy);
1650
438
  return copy;
1651
439
  }
1652
440
  /**
1653
- * Attaches shell-owned recording data to an accepted aggregate decision.
441
+ * Attaches shell-owned stamp data to an accepted aggregate decision.
1654
442
  *
1655
443
  * The decision supplies the domain type, payload, source aggregate
1656
444
  * identity, and payload schema version. The stamp supplies only event
1657
- * identity, recording time, and trace metadata.
445
+ * identity, stamp time, and trace metadata.
1658
446
  */
1659
- function recordDomainEvent(event, stamp) {
1660
- if (!isUncommittedDomainEvent(event)) throw new TypeError("recordDomainEvent requires an event created by createUncommittedDomainEvent");
1661
- if (isFactoryOwnedDomainEventStamp(stamp)) return mintRecordedEvent(event, stamp.eventId, stamp.occurredAt, stamp.metadata);
447
+ function stampDomainEvent(event, stamp) {
448
+ if (!isUnstampedDomainEvent(event)) throw new TypeError("stampDomainEvent requires an unstamped event from an aggregate's createEvent");
449
+ if (isFactoryOwnedDomainEventStamp(stamp)) return buildStampedEvent(event, stamp.eventId, stamp.occurredAt, stamp.metadata);
1662
450
  assertNonBlankEventField(stamp.eventId, "eventId", "EVENT_ID_INVALID");
1663
451
  const occurredAt = deepFreeze(copyValidEventDate(stamp.occurredAt));
1664
- const metadata = ownedEventMetadata(stamp.metadata, "recordDomainEvent");
1665
- return mintRecordedEvent(event, stamp.eventId, occurredAt, metadata === void 0 ? void 0 : deepFreeze(metadata));
452
+ const metadata = ownedEventMetadata(stamp.metadata, "stampDomainEvent");
453
+ return buildStampedEvent(event, stamp.eventId, occurredAt, metadata === void 0 ? void 0 : deepFreeze(metadata));
1666
454
  }
1667
455
  /**
1668
- * Single mint tail for both stamp provenances. The stamp fields arrive
456
+ * Single build tail for both stamp provenances. The stamp fields arrive
1669
457
  * pre-validated, copied, and frozen (by `createStamp` for factory-owned
1670
- * stamps, by `recordDomainEvent` for caller-built stamps); the uncommitted
458
+ * stamps, by `stampDomainEvent` for caller-built stamps); the unstamped
1671
459
  * event's payload is already defensively cloned and deeply frozen by its
1672
460
  * constructor and is shared instead of paying a second deep copy per event.
1673
461
  */
1674
- function mintRecordedEvent(event, eventId, occurredAt, metadata) {
462
+ function buildStampedEvent(event, eventId, occurredAt, metadata) {
1675
463
  assertProducerOwnedEventFields(event.type, event);
1676
464
  const recorded = {
1677
465
  eventId,
@@ -1683,42 +471,42 @@ function mintRecordedEvent(event, eventId, occurredAt, metadata) {
1683
471
  schemaVersion: event.schemaVersion,
1684
472
  metadata
1685
473
  };
1686
- stampRecordedBrand(recorded);
474
+ brandStampedEvent(recorded);
1687
475
  Object.freeze(recorded);
1688
- RECORDED_EVENTS.add(recorded);
476
+ STAMPED_EVENTS.add(recorded);
1689
477
  return recorded;
1690
478
  }
1691
479
  /**
1692
- * Brands, freezes, and registers a kit-derived copy of a recorded event
480
+ * Brands, freezes, and registers a kit-derived copy of a stamped event
1693
481
  * (e.g. the copy `apply()` stamps with the aggregate identity) as recorded
1694
482
  * itself.
1695
483
  * The copy shares the already-frozen payload/metadata of its source,
1696
- * so the mint guarantee carries over. Stamping the cooperative brand
484
+ * so the kit-built guarantee carries over. Applying the cooperative brand
1697
485
  * before freezing keeps the copy recognizable by another loaded kit
1698
486
  * instance as well as by this instance's WeakSet. Module-internal
1699
487
  * export; not part of the package entries.
1700
488
  */
1701
- function adoptRecordedDomainEvent(copy) {
1702
- stampRecordedBrand(copy);
489
+ function adoptStampedDomainEvent(copy) {
490
+ brandStampedEvent(copy);
1703
491
  Object.freeze(copy);
1704
- RECORDED_EVENTS.add(copy);
492
+ STAMPED_EVENTS.add(copy);
1705
493
  return copy;
1706
494
  }
1707
495
  function createDomainEvent(type, payload, options) {
1708
496
  return defaultDomainEventFactory.create(type, payload, options);
1709
497
  }
1710
- function createDomainEventFromFacts(type, payload, options) {
498
+ function createStampedDomainEvent(type, payload, options) {
1711
499
  if (options?.eventId === void 0) missingExplicitEventId();
1712
500
  if (options.occurredAt === void 0) missingExplicitOccurredAt();
1713
- return mintDomainEvent(type, payload, options, missingExplicitEventId, missingExplicitOccurredAt, "createDomainEventFromFacts");
501
+ return buildDomainEvent(type, payload, options, missingExplicitEventId, missingExplicitOccurredAt, "createStampedDomainEvent");
1714
502
  }
1715
503
  function missingExplicitEventId() {
1716
- throw new DomainEventValidationError("EVENT_ID_REQUIRED", "eventId", "createDomainEventFromFacts requires an explicit eventId");
504
+ throw new DomainEventValidationError("EVENT_ID_REQUIRED", "eventId", "createStampedDomainEvent requires an explicit eventId");
1717
505
  }
1718
506
  function missingExplicitOccurredAt() {
1719
- throw new DomainEventValidationError("EVENT_OCCURRED_AT_REQUIRED", "occurredAt", "createDomainEventFromFacts requires an explicit occurredAt");
507
+ throw new DomainEventValidationError("EVENT_OCCURRED_AT_REQUIRED", "occurredAt", "createStampedDomainEvent requires an explicit occurredAt");
1720
508
  }
1721
- function mintDomainEvent(type, payload, options, eventIdFactory, clock, operation, factorySource) {
509
+ function buildDomainEvent(type, payload, options, eventIdFactory, clock, operation, factorySource) {
1722
510
  assertProducerOwnedEventFields(type, options);
1723
511
  const eventId = options?.eventId ?? eventIdFactory();
1724
512
  assertNonBlankEventField(eventId, "eventId", "EVENT_ID_INVALID");
@@ -1734,10 +522,10 @@ function mintDomainEvent(type, payload, options, eventIdFactory, clock, operatio
1734
522
  schemaVersion,
1735
523
  metadata: ownedEventMetadata(options?.metadata, operation, factorySource)
1736
524
  };
1737
- stampRecordedBrand(event);
1738
- const minted = deepFreeze(event);
1739
- RECORDED_EVENTS.add(minted);
1740
- return minted;
525
+ brandStampedEvent(event);
526
+ const built = deepFreeze(event);
527
+ STAMPED_EVENTS.add(built);
528
+ return built;
1741
529
  }
1742
530
  function assertProducerOwnedEventFields(type, options) {
1743
531
  assertNonBlankEventField(type, "type", "EVENT_TYPE_INVALID");
@@ -1758,11 +546,11 @@ function readEventClock(clock) {
1758
546
  }
1759
547
  /**
1760
548
  * Copies the caller's metadata and checks the copy. Metadata is a plain
1761
- * record or absent; a null from a JSON envelope would mint and then fail
549
+ * record or absent; a null from a JSON envelope would build and then fail
1762
550
  * every metadata read far from the producer. A factory origin fills
1763
551
  * `source` only after the check, and a call site that names its own source
1764
552
  * keeps it: an explicit fact about one event outranks the default of the
1765
- * factory that mints it.
553
+ * factory that builds it.
1766
554
  */
1767
555
  function ownedEventMetadata(metadata, operation, factorySource) {
1768
556
  if (metadata === void 0) return factorySource === void 0 ? void 0 : { source: factorySource };
@@ -1792,25 +580,12 @@ function isPlainRecordCopy(copy) {
1792
580
  /**
1793
581
  * Deep-clones caller-supplied event data (payload, metadata) before the
1794
582
  * event is frozen, so a constructor never freezes or aliases the caller's
1795
- * own object graph, and checks the clone with {@link assertFreezableState}.
1796
- * A primitive needs no copy, but the check rejects a symbol. A class
1797
- * instance or an accessor becomes plain data in the clone.
583
+ * own object graph. {@link copyFreezableState} checks the data and the
584
+ * clone; its JSDoc states the rule and its limits.
1798
585
  */
1799
586
  function cloneOwnedEventData(value, field, operation) {
1800
- if (typeof value === "function") throw new TypeError(`${operation}: ${field} must not be a function: domain events are plain data`);
1801
- if (value === null || typeof value !== "object") {
1802
- assertFreezableState(value, `${operation}: ${field}`);
1803
- return value;
1804
- }
1805
- let clone;
1806
- try {
1807
- clone = structuredClone(value);
1808
- } catch (cause) {
1809
- throw new TypeError(`${operation}: ${field} cannot be copied, because structuredClone failed (the cause names the value); domain events are plain data`, { cause });
1810
- }
1811
- assertNoHostileOwnProtoKey(clone, `Event ${field} of ${operation}`);
1812
- assertFreezableState(clone, `${operation}: ${field}`);
1813
- return clone;
587
+ if (value !== null && typeof value === "object") assertNoOwnProtoKey(value, `Event ${field} of ${operation}`);
588
+ return copyFreezableState(value, `${operation}: ${field}`);
1814
589
  }
1815
590
  /**
1816
591
  * Copies metadata from a source event to a new event.
@@ -1826,8 +601,8 @@ function cloneOwnedEventData(value, field, operation) {
1826
601
  * ```
1827
602
  */
1828
603
  function copyMetadata(sourceEvent, additionalMetadata) {
1829
- if (sourceEvent.metadata !== void 0) assertNoHostileOwnProtoKey(sourceEvent.metadata, "Event metadata");
1830
- if (additionalMetadata !== void 0) assertNoHostileOwnProtoKey(additionalMetadata, "Event metadata");
604
+ if (sourceEvent.metadata !== void 0) assertNoOwnProtoKey(sourceEvent.metadata, "Event metadata");
605
+ if (additionalMetadata !== void 0) assertNoOwnProtoKey(additionalMetadata, "Event metadata");
1831
606
  return {
1832
607
  ...sourceEvent.metadata ?? {},
1833
608
  ...additionalMetadata ?? {}
@@ -1850,7 +625,7 @@ function mergeMetadata(...metadataObjects) {
1850
625
  const merged = {};
1851
626
  for (const metadata of metadataObjects) {
1852
627
  if (!metadata) continue;
1853
- assertNoHostileOwnProtoKey(metadata, "Event metadata");
628
+ assertNoOwnProtoKey(metadata, "Event metadata");
1854
629
  for (const key of Reflect.ownKeys(metadata)) {
1855
630
  if (!Object.getOwnPropertyDescriptor(metadata, key)?.enumerable) continue;
1856
631
  Object.defineProperty(merged, key, {
@@ -1945,7 +720,7 @@ function assertNonNegativeSafeInteger(context, field, value) {
1945
720
  /** Default bound for delivery and post-commit operations. */
1946
721
  const DEFAULT_EXECUTION_TIMEOUT_MS = 3e4;
1947
722
  /**
1948
- * Owner signal of each child signal that {@link runBoundedExecution} minted.
723
+ * Owner signal of each child signal that {@link runBoundedExecution} created.
1949
724
  *
1950
725
  * One bounded operation often wraps another, and every hop derives a fresh
1951
726
  * signal. A consumer that follows a call chain by signal identity alone loses
@@ -2250,5 +1025,5 @@ function throwIfAborted(signal) {
2250
1025
  }
2251
1026
 
2252
1027
  //#endregion
2253
- export { SnapshotTimeValidationError as A, voWithValidation as B, defaultDomainEventFactory as C, mintUncommittedDomainEvent as D, mergeMetadata as E, ValueObject as F, isBuiltInObject as G, deepOmit as H, deepFreeze as I, isIntrinsicConstructorPrototype as K, vo as L, detachState as M, hasCooperativeBrand as N, recordDomainEvent as O, stampCooperativeBrand as P, voEquals as R, createUncommittedDomainEvent as S, isUncommittedDomainEvent as T, deepEqual as U, deepEqualExcept as V, findPropertyDescriptor as W, adoptUncommittedDomainEvent as _, isDispatchTrackingOutbox as a, createDomainEventFactory as b, runBoundedExecution as c, assertPositiveInteger as d, assertPositiveSafeInteger as f, adoptRecordedDomainEvent as g, waitRejectingOnAbort as h, assertPageWithinWindow as i, detachFreezableState as j, DomainEventValidationError as k, MAX_TIMER_DELAY_MS as l, abortReason as m, readStreamPages as n, DEFAULT_EXECUTION_TIMEOUT_MS as o, assertTimerDelay as p, isWeakMap as q, assertPageNotEmpty as r, ownerSignalOf as s, pinTargetVersion as t, assertNonNegativeSafeInteger as u, copyMetadata as v, isRecordedDomainEvent as w, createDomainEventFromFacts as x, createDomainEvent as y, voEqualsExcept as z };
1028
+ export { copyFreezableState as A, defaultDomainEventFactory as C, stampDomainEvent as D, mergeMetadata as E, DomainEventValidationError as O, createStampedDomainEvent as S, isUnstampedDomainEvent as T, adoptUnstampedDomainEvent as _, isDispatchTrackingOutbox as a, createDomainEvent as b, runBoundedExecution as c, assertPositiveInteger as d, assertPositiveSafeInteger as f, adoptStampedDomainEvent as g, waitRejectingOnAbort as h, assertPageWithinWindow as i, copyPlainState as j, SnapshotTimeValidationError as k, MAX_TIMER_DELAY_MS as l, abortReason as m, readStreamPages as n, DEFAULT_EXECUTION_TIMEOUT_MS as o, assertTimerDelay as p, assertPageNotEmpty as r, ownerSignalOf as s, pinTargetVersion as t, assertNonNegativeSafeInteger as u, buildUnstampedDomainEvent as v, isStampedDomainEvent as w, createDomainEventFactory as x, copyMetadata as y };
2254
1029
  //# sourceMappingURL=read-stream-pages.js.map