@shirudo/ddd-kit 3.0.0-rc.12 → 3.0.0-rc.14

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,1278 +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$1 = 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
- /** The type name in a tag: `"Map"` for `"[object Map]"`. */
201
- function builtInTypeName(tag) {
202
- return tag.slice(8, -1);
203
- }
204
- function builtInTagFromBrand(value) {
205
- if (hasBrand(value, "[object Date]")) return "[object Date]";
206
- if (hasBrand(value, "[object RegExp]")) return "[object RegExp]";
207
- if (hasBrand(value, "[object Map]")) return "[object Map]";
208
- if (hasBrand(value, "[object Set]")) return "[object Set]";
209
- if (hasBrand(value, "[object WeakMap]")) return "[object WeakMap]";
210
- if (hasBrand(value, "[object WeakSet]")) return "[object WeakSet]";
211
- if (hasBrand(value, "[object DataView]")) return "[object DataView]";
212
- if (hasBrand(value, "[object ArrayBuffer]")) return "[object ArrayBuffer]";
213
- if (hasBrand(value, "[object SharedArrayBuffer]")) return "[object SharedArrayBuffer]";
214
- if (hasBrand(value, "[object Boolean]")) return "[object Boolean]";
215
- if (hasBrand(value, "[object Number]")) return "[object Number]";
216
- if (hasBrand(value, "[object String]")) return "[object String]";
217
- if (hasBrand(value, "[object BigInt]")) return "[object BigInt]";
218
- if (hasNativePrototype(value, "Promise")) return "[object Promise]";
219
- if (hasNativePrototype(value, "Error")) return "[object Error]";
220
- }
221
- function hasNativePrototype(value, expectedName) {
222
- const visited = /* @__PURE__ */ new WeakSet();
223
- let prototype = Object.getPrototypeOf(value);
224
- while (prototype !== null && !visited.has(prototype)) {
225
- visited.add(prototype);
226
- if (isIntrinsicConstructorPrototype(prototype, expectedName)) return true;
227
- prototype = Object.getPrototypeOf(prototype);
228
- }
229
- return false;
230
- }
231
- /**
232
- * Verifies that `obj` genuinely is the type its tag claims, via an
233
- * internal-slot probe. Promise and Error have no side-effect-free standard
234
- * probe, so their visible tags remain conservative; masked instances are
235
- * identified separately through their native prototype chain.
236
- */
237
- function hasBrand(obj, tag) {
238
- try {
239
- switch (tag) {
240
- case "[object Date]":
241
- dateGetTime.call(obj);
242
- return true;
243
- case "[object RegExp]":
244
- regExpSourceGet.call(obj);
245
- return true;
246
- case "[object Map]":
247
- mapSizeGet.call(obj);
248
- return true;
249
- case "[object Set]":
250
- setSizeGet.call(obj);
251
- return true;
252
- case "[object WeakMap]":
253
- weakMapHas.call(obj, PROBE_KEY);
254
- return true;
255
- case "[object WeakSet]":
256
- weakSetHas.call(obj, PROBE_KEY);
257
- return true;
258
- case "[object DataView]":
259
- dataViewByteLengthGet.call(obj);
260
- return true;
261
- case "[object ArrayBuffer]":
262
- arrayBufferByteLengthGet.call(obj);
263
- return true;
264
- case "[object SharedArrayBuffer]":
265
- if (!sharedArrayBufferByteLengthGet) return false;
266
- sharedArrayBufferByteLengthGet.call(obj);
267
- return true;
268
- case "[object Boolean]":
269
- booleanValueOf.call(obj);
270
- return true;
271
- case "[object Number]":
272
- numberValueOf.call(obj);
273
- return true;
274
- case "[object String]":
275
- stringValueOf$1.call(obj);
276
- return true;
277
- case "[object BigInt]":
278
- bigIntValueOf.call(obj);
279
- return true;
280
- case "[object Promise]": return hasNativePrototype(obj, "Promise");
281
- case "[object Error]": return ERROR_INTRINSIC_NAMES.some((name) => hasNativePrototype(obj, name));
282
- default: return false;
283
- }
284
- } catch {
285
- return false;
286
- }
287
- }
288
- /**
289
- * Returns `true` when `obj` is a built-in JavaScript type that should be
290
- * treated atomically (compared/cloned as a unit, not walked structurally).
291
- * Cross-realm safe, and brand-verified: a plain object spoofing a built-in
292
- * tag via `Symbol.toStringTag` returns `false` and is walked structurally
293
- * like any other plain object instead of crashing type-specific code.
294
- *
295
- * @param obj - The object to classify
296
- * @param tag - The result of `Object.prototype.toString.call(obj)`, passed
297
- * in so callers that already computed it don't pay twice
298
- */
299
- function isBuiltInObject(obj, tag) {
300
- if (ArrayBuffer.isView(obj)) return true;
301
- if (tag.endsWith("Array]")) return false;
302
- return BUILT_IN_TAGS.has(tag) && hasBrand(obj, tag);
303
- }
304
- /**
305
- * Brand-verified `WeakMap` check that holds across realms. `instanceof
306
- * WeakMap` binds to the constructor of one realm, so a WeakMap from a `vm`
307
- * context or an iframe reads as a foreign value. The internal-slot probe
308
- * answers the same in every realm, and a plain object cannot spoof it
309
- * through `Symbol.toStringTag`.
310
- */
311
- function isWeakMap(value) {
312
- return typeof value === "object" && value !== null && hasBrand(value, "[object WeakMap]");
313
- }
314
-
315
- //#endregion
316
- //#region src/internal/structural/deep-equal.ts
317
- const objProto = Object.prototype;
318
- const objToString = objProto.toString;
319
- const objHasOwn = objProto.hasOwnProperty;
320
- /**
321
- * SameValueZero: `===` plus NaN-equals-NaN (and `+0 === -0`, unlike
322
- * `Object.is`). The numeric semantics `deepEqual` documents for primitives,
323
- * applied consistently inside TypedArrays, Dates and Number wrappers.
324
- */
325
- function sameValueZero(a, b) {
326
- return a === b || Number.isNaN(a) && Number.isNaN(b);
327
- }
328
- /**
329
- * Performs a deep equality check between two values.
330
- *
331
- * This function compares values recursively, handling:
332
- * - Primitives (with special handling for NaN)
333
- * - Arrays (nested arrays supported)
334
- * - Objects (plain objects and class instances)
335
- * - TypedArrays (Uint8Array, Int32Array, etc.)
336
- * - DataView
337
- * - Maps and Sets
338
- * - Dates and RegExp
339
- * - Wrapper objects (Boolean, Number, String)
340
- * - Circular references (detected and handled)
341
- *
342
- * @param a - The first value to compare
343
- * @param b - The second value to compare
344
- * @returns `true` if the values are deeply equal, `false` otherwise
345
- *
346
- * @example
347
- * ```ts
348
- * deepEqual([1, 2, 3], [1, 2, 3]); // true
349
- * deepEqual({ a: 1, b: [2, 3] }, { a: 1, b: [2, 3] }); // true
350
- * deepEqual(NaN, NaN); // true
351
- * deepEqual([1, 2], [1, 2, 3]); // false
352
- * ```
353
- */
354
- function deepEqual(a, b) {
355
- return deepEqualInner(a, b, /* @__PURE__ */ new WeakMap());
356
- }
357
- /**
358
- * Internal recursive function for deep equality comparison.
359
- *
360
- * @internal
361
- */
362
- function deepEqualInner(a, b, visited) {
363
- if (a === b) return true;
364
- const typeA = typeof a;
365
- const typeB = typeof b;
366
- if (typeA !== "object" || a === null || typeB !== "object" || b === null) {
367
- if (typeA === "number" && typeB === "number") return Number.isNaN(a) && Number.isNaN(b);
368
- return false;
369
- }
370
- const objA = a;
371
- const objB = b;
372
- let cachedBs = visited.get(objA);
373
- if (cachedBs?.has(objB)) return true;
374
- if (!cachedBs) {
375
- cachedBs = /* @__PURE__ */ new WeakSet();
376
- visited.set(objA, cachedBs);
377
- }
378
- cachedBs.add(objB);
379
- if (ArrayBuffer.isView(objA) || ArrayBuffer.isView(objB)) {
380
- if (!ArrayBuffer.isView(objA) || !ArrayBuffer.isView(objB)) return false;
381
- const tagA = objToString.call(objA);
382
- if (tagA !== objToString.call(objB)) return false;
383
- if (tagA === "[object DataView]") {
384
- const viewA = objA;
385
- const viewB = objB;
386
- if (viewA.byteLength !== viewB.byteLength) return false;
387
- const len = viewA.byteLength;
388
- for (let i = 0; i < len; i++) if (viewA.getUint8(i) !== viewB.getUint8(i)) return false;
389
- return true;
390
- }
391
- const arrA = objA;
392
- const arrB = objB;
393
- const len = arrA.length;
394
- if (len !== arrB.length) return false;
395
- for (let i = 0; i < len; i++) if (!sameValueZero(arrA[i], arrB[i])) return false;
396
- return true;
397
- }
398
- if (Array.isArray(objA) || Array.isArray(objB)) {
399
- if (!Array.isArray(objA) || !Array.isArray(objB)) return false;
400
- if (objA.length !== objB.length) return false;
401
- const keysA = Reflect.ownKeys(objA).filter((key) => key !== "length");
402
- const keysB = Reflect.ownKeys(objB).filter((key) => key !== "length");
403
- if (keysA.length !== keysB.length) return false;
404
- const arrA = objA;
405
- const arrB = objB;
406
- for (const key of keysA) {
407
- if (!objHasOwn.call(objB, key)) return false;
408
- if (!deepEqualInner(arrA[key], arrB[key], visited)) return false;
409
- }
410
- return true;
411
- }
412
- const tagA = objToString.call(objA);
413
- const tagB = objToString.call(objB);
414
- if (tagA !== tagB) return false;
415
- const builtInA = isBuiltInObject(objA, tagA);
416
- if (builtInA !== isBuiltInObject(objB, tagB)) return false;
417
- if (!builtInA) {
418
- if (isOpaqueExoticTag(tagA)) return objA === objB;
419
- return comparePlainObjects(objA, objB, visited);
420
- }
421
- switch (tagA) {
422
- case "[object Map]": {
423
- const mapA = objA;
424
- const mapB = objB;
425
- if (mapA.size !== mapB.size) return false;
426
- for (const [key, valA] of mapA) {
427
- if (!mapB.has(key)) return false;
428
- if (!deepEqualInner(valA, mapB.get(key), visited)) return false;
429
- }
430
- return true;
431
- }
432
- case "[object Set]": {
433
- const setA = objA;
434
- const setB = objB;
435
- if (setA.size !== setB.size) return false;
436
- for (const value of setA) if (!setB.has(value)) return false;
437
- return true;
438
- }
439
- case "[object Date]": return sameValueZero(objA.getTime(), objB.getTime());
440
- case "[object RegExp]": {
441
- const regA = objA;
442
- const regB = objB;
443
- return regA.source === regB.source && regA.flags === regB.flags;
444
- }
445
- case "[object Boolean]":
446
- case "[object Number]":
447
- case "[object String]":
448
- case "[object BigInt]": return sameValueZero(objA.valueOf(), objB.valueOf());
449
- default: return objA === objB;
450
- }
451
- }
452
- /**
453
- * Plain / custom objects: compare own enumerable string keys + own symbol
454
- * keys and their values. Used both as the final fallback and for objects
455
- * whose built-in-looking tag failed brand verification.
456
- */
457
- function comparePlainObjects(objA, objB, visited) {
458
- const recA = objA;
459
- const recB = objB;
460
- const stringKeysA = Object.getOwnPropertyNames(objA);
461
- const stringKeysB = Object.getOwnPropertyNames(objB);
462
- if (stringKeysA.length !== stringKeysB.length) return false;
463
- const symbolKeysA = Object.getOwnPropertySymbols(objA);
464
- const symbolKeysB = Object.getOwnPropertySymbols(objB);
465
- if (symbolKeysA.length !== symbolKeysB.length) return false;
466
- const symbolKeysBSet = new Set(symbolKeysB);
467
- for (const key of stringKeysA) if (!objHasOwn.call(objB, key)) return false;
468
- for (const key of symbolKeysA) if (!symbolKeysBSet.has(key)) return false;
469
- for (const key of stringKeysA) if (!deepEqualInner(recA[key], recB[key], visited)) return false;
470
- for (const key of symbolKeysA) if (!deepEqualInner(recA[key], recB[key], visited)) return false;
471
- return true;
472
- }
473
-
474
- //#endregion
475
- //#region src/internal/structural/deep-omit.ts
476
- /**
477
- * Creates a deep copy of `value` with certain keys removed according to the
478
- * provided rules.
479
- *
480
- * Walks the object tree and skips keys that match `ignoreKeys` /
481
- * `ignoreKeyPredicate`. Built-in atomic types that `deepEqual` compares by
482
- * value (Date, RegExp, Map, Set, TypedArrays, DataView) are cloned by type
483
- * rather than walked, since their internal structure has no key filtering to
484
- * apply. Types that `deepEqual` compares by reference (Error, ArrayBuffer,
485
- * SharedArrayBuffer, Promise, WeakMap, WeakSet) are passed through by
486
- * reference, so `deepEqualExcept(x, x)` stays reflexive. Cycles are
487
- * preserved: a cycle `a → a` clones to `a' → a'`. Arrays retain sparse
488
- * holes and all non-ignored own properties, including symbol keys.
489
- *
490
- * **Shared references.** Without `ignoreKeyPredicate`, an object reached
491
- * via several paths dedupes to a single clone. With a predicate, each
492
- * path gets its own clone, because the predicate may decide differently per
493
- * path, so memoising the first path's result would be wrong. This is
494
- * inherently exponential for diamond-shaped sharing (a node reachable
495
- * via 2^n paths is cloned 2^n times); the walk aborts with a descriptive
496
- * error after {@link PATH_SENSITIVE_VISIT_BUDGET} node visits instead of
497
- * hanging the process.
498
- *
499
- * **Prototype-pollution safety.** `__proto__` and `constructor` keys
500
- * encountered as *own* properties of the input (typical of `JSON.parse`
501
- * output) are copied as inert data properties via `Object.defineProperty`
502
- * so the clone graph cannot bleed into `Object.prototype`.
503
- *
504
- * **Class instances.** When the input is a class instance, the clone is
505
- * built via `Object.create(proto)` so the prototype is preserved, but the
506
- * constructor is NOT re-invoked, so class invariants enforced by the
507
- * constructor are not re-checked. `deepOmit` is therefore best used for
508
- * comparison/serialisation (`voEqualsExcept`, `deepEqualExcept`), not as
509
- * a general-purpose clone for behaviour-carrying objects.
510
- *
511
- * @param value - The value to create a deep copy from
512
- * @param options - Options specifying which keys to ignore
513
- * @returns A deep copy of `value` with specified keys removed
514
- */
515
- function deepOmit(value, options) {
516
- const visited = /* @__PURE__ */ new WeakMap();
517
- return omitInternal(value, options, options.ignoreKeys ? new Set(options.ignoreKeys) : void 0, [], visited, options.ignoreKeyPredicate ? { visits: 0 } : void 0);
518
- }
519
- /**
520
- * Maximum object-node visits for a single path-sensitive `deepOmit` walk.
521
- * Per-path cloning expands exponentially on diamond-shaped sharing; past
522
- * this bound the walk throws instead of hanging the process. One million
523
- * visits covers any realistically tree-shaped input.
524
- */
525
- const PATH_SENSITIVE_VISIT_BUDGET = 1e6;
526
- function omitInternal(value, options, ignoreKeys, path, visited, budget) {
527
- if (value === null) return value;
528
- if (typeof value !== "object") return value;
529
- const obj = value;
530
- if (visited.has(obj)) return visited.get(obj);
531
- 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.`);
532
- if (Array.isArray(obj)) {
533
- const arr = obj;
534
- const clone = new Array(arr.length);
535
- const lengthDescriptor = Object.getOwnPropertyDescriptor(arr, "length");
536
- visited.set(obj, clone);
537
- for (const key of Reflect.ownKeys(arr)) {
538
- if (key === "length") continue;
539
- const segment = arrayPathSegment(key);
540
- if (typeof segment !== "number" && shouldIgnoreKey(key, path, ignoreKeys, options)) continue;
541
- const descriptor = Object.getOwnPropertyDescriptor(arr, key);
542
- if (descriptor === void 0) continue;
543
- path.push(segment);
544
- if ("value" in descriptor) descriptor.value = omitInternal(descriptor.value, options, ignoreKeys, path, visited, budget);
545
- Object.defineProperty(clone, key, descriptor);
546
- path.pop();
547
- }
548
- if (lengthDescriptor !== void 0) Object.defineProperty(clone, "length", lengthDescriptor);
549
- if (budget) visited.delete(obj);
550
- return clone;
551
- }
552
- const tag = Object.prototype.toString.call(obj);
553
- if (isBuiltInObject(obj, tag)) {
554
- const builtInClone = cloneBuiltIn(obj, tag);
555
- visited.set(obj, builtInClone);
556
- return builtInClone;
557
- }
558
- if (isOpaqueExoticTag(tag)) {
559
- visited.set(obj, obj);
560
- return obj;
561
- }
562
- const clone = Object.create(Object.getPrototypeOf(obj));
563
- visited.set(obj, clone);
564
- const stringKeys = Object.getOwnPropertyNames(obj);
565
- const symbolKeys = Object.getOwnPropertySymbols(obj);
566
- for (const key of [...stringKeys, ...symbolKeys]) {
567
- if (shouldIgnoreKey(key, path, ignoreKeys, options)) continue;
568
- path.push(key);
569
- assignOwn(clone, key, omitInternal(obj[key], options, ignoreKeys, path, visited, budget), Object.getOwnPropertyDescriptor(obj, key)?.enumerable ?? true);
570
- path.pop();
571
- }
572
- if (budget) visited.delete(obj);
573
- return clone;
574
- }
575
- function arrayPathSegment(key) {
576
- if (typeof key === "symbol") return key;
577
- const index = Number(key);
578
- return Number.isInteger(index) && index >= 0 && index < 4294967295 && String(index) === key ? index : key;
579
- }
580
- /**
581
- * Assigns `value` as an OWN data property on `target` without going through
582
- * any inherited setter; critically, it never invokes the `__proto__` setter
583
- * even when `key === "__proto__"`. Required to defeat prototype-pollution
584
- * payloads that ship `__proto__` as a parsed-JSON own key.
585
- */
586
- function assignOwn(target, key, value, enumerable = true) {
587
- Object.defineProperty(target, key, {
588
- value,
589
- writable: true,
590
- enumerable,
591
- configurable: true
592
- });
593
- }
594
- /**
595
- * Clones a built-in atomic type by case. Falls back to `structuredClone`
596
- * for anything not explicitly enumerated (e.g. DataView, TypedArrays,
597
- * Boolean/Number/String wrappers, all of which `deepEqual` compares by
598
- * value). Types that `deepEqual` compares BY REFERENCE (the shared
599
- * {@link REFERENCE_COMPARED_TAGS} set) are passed through by reference
600
- * instead; cloning them would make `deepEqualExcept(x, x)` false.
601
- * Promise/WeakMap/WeakSet additionally cannot be cloned at all
602
- * (`structuredClone` rejects them).
603
- */
604
- function cloneBuiltIn(obj, tag) {
605
- if (REFERENCE_COMPARED_TAGS.has(tag)) return obj;
606
- switch (tag) {
607
- case "[object Date]": return new Date(obj.getTime());
608
- case "[object RegExp]": {
609
- const re = obj;
610
- const copy = new RegExp(re.source, re.flags);
611
- copy.lastIndex = re.lastIndex;
612
- return copy;
613
- }
614
- case "[object Map]": return new Map(obj);
615
- case "[object Set]": return new Set(obj);
616
- default: return structuredClone(obj);
617
- }
618
- }
619
- function shouldIgnoreKey(key, path, ignoreKeys, options) {
620
- if (ignoreKeys?.has(key)) return true;
621
- if (options.ignoreKeyPredicate?.(key, path.slice())) return true;
622
- return false;
623
- }
624
-
625
- //#endregion
626
- //#region src/internal/structural/deep-equal-except.ts
627
- /**
628
- * Performs a deep equality comparison between two values after omitting specified keys.
629
- *
630
- * This function first removes the specified keys from both values using `deepOmit`,
631
- * then performs a deep equality check using `deepEqual`.
632
- *
633
- * @param a - The first value to compare
634
- * @param b - The second value to compare
635
- * @param options - Options specifying which keys to omit before comparison
636
- * @returns `true` if the values are deeply equal after omitting specified keys, `false` otherwise
637
- *
638
- * @example
639
- * ```ts
640
- * const obj1 = { id: 1, name: "Alice", updatedAt: "2024-01-01" };
641
- * const obj2 = { id: 2, name: "Alice", updatedAt: "2024-01-02" };
642
- *
643
- * deepEqualExcept(obj1, obj2, { ignoreKeys: ["id", "updatedAt"] }); // true
644
- * ```
645
- */
646
- function deepEqualExcept(a, b, options) {
647
- const prunedA = deepOmit(a, options);
648
- const prunedB = deepOmit(b, options);
649
- return deepEqual(prunedA, prunedB);
650
- }
651
-
652
- //#endregion
653
- //#region src/internal/cooperative-brand.ts
654
- /**
655
- * A cooperative brand marks an object that a kit constructor produced, so
656
- * a second loaded copy of the kit (duplicate npm dependency, dual CJS/ESM
657
- * load, plugin bundle) recognizes it. A module-private WeakSet cannot do
658
- * that: it is bound to one loaded copy. The brand is a `Symbol.for` key
659
- * with the value `true`. The property is non-enumerable, so it never
660
- * leaks into spreads, JSON, or equality, and non-writable and
661
- * non-configurable, so nothing edits it after the stamp.
662
- *
663
- * The probe reads the brand as an OWN property with exactly these
664
- * attributes and requires the carrier to be frozen. An object that
665
- * inherits a branded object through its prototype can carry mutable own
666
- * overrides; a branded object that is still open can change after the
667
- * stamp. Neither is what the constructor produced. Every stamp site
668
- * therefore freezes the carrier right after {@link stampCooperativeBrand}.
669
- *
670
- * The brand is forgeable BY DESIGN. It catches accidental hand-rolled
671
- * literals; it is not a security boundary against code in the same
672
- * process that fakes it on purpose.
673
- *
674
- * Internal utility (not exported from the package barrels).
675
- */
676
- function stampCooperativeBrand(target, brand) {
677
- Object.defineProperty(target, brand, {
678
- value: true,
679
- enumerable: false,
680
- writable: false,
681
- configurable: false
682
- });
683
- }
684
- /**
685
- * Whether `value` carries `brand` as an own, non-enumerable, non-writable,
686
- * non-configurable `true` and is frozen. A Proxy trap that throws reads
687
- * as unbranded.
688
- */
689
- function hasCooperativeBrand(value, brand) {
690
- if (value === null || typeof value !== "object" && typeof value !== "function") return false;
691
- try {
692
- const marker = Reflect.getOwnPropertyDescriptor(value, brand);
693
- return marker?.value === true && marker.enumerable === false && marker.writable === false && marker.configurable === false && Object.isFrozen(value);
694
- } catch {
695
- return false;
696
- }
697
- }
698
-
699
- //#endregion
700
- //#region src/internal/structural/shadowed-mutators.ts
701
- /**
702
- * `Object.freeze` does not protect internal slots: a frozen Date still
703
- * accepts `setTime`, a frozen Map still accepts `set`. A deep freeze
704
- * therefore shadows these mutators with own throwing functions. The
705
- * shadows are non-enumerable, so they are invisible to `Object.keys` and
706
- * spread, and `structuredClone` drops them. A walk that checks a copy
707
- * accepts them as machinery, not data.
708
- */
709
- const mutatorsByTag = /* @__PURE__ */ new Map([
710
- ["[object Date]", [
711
- "setTime",
712
- "setMilliseconds",
713
- "setUTCMilliseconds",
714
- "setSeconds",
715
- "setUTCSeconds",
716
- "setMinutes",
717
- "setUTCMinutes",
718
- "setHours",
719
- "setUTCHours",
720
- "setDate",
721
- "setUTCDate",
722
- "setMonth",
723
- "setUTCMonth",
724
- "setFullYear",
725
- "setUTCFullYear",
726
- "setYear"
727
- ]],
728
- ["[object Map]", [
729
- "set",
730
- "delete",
731
- "clear"
732
- ]],
733
- ["[object Set]", [
734
- "add",
735
- "delete",
736
- "clear"
737
- ]]
738
- ]);
739
- const mutatorShadowBrand = Symbol.for("@shirudo/ddd-kit/mutator-shadow/v1");
740
- const shadows = /* @__PURE__ */ new Map();
741
- function shadowOf(typeName, method) {
742
- const key = `${typeName}.${method}`;
743
- let shadow = shadows.get(key);
744
- if (shadow === void 0) {
745
- shadow = function throwFrozenMutation() {
746
- throw new TypeError(`Cannot call ${method}() on a ${typeName} inside a deeply frozen value`);
747
- };
748
- stampCooperativeBrand(shadow, mutatorShadowBrand);
749
- Object.freeze(shadow);
750
- shadows.set(key, shadow);
751
- }
752
- return shadow;
753
- }
754
- const shadowDescriptor = {
755
- value: void 0,
756
- writable: false,
757
- enumerable: false,
758
- configurable: false
759
- };
760
- /**
761
- * Installs the mutator shadows on a Date, a Map, or a Set whose tag is
762
- * `tag`. Returns whether it installed them: a built-in of another kind has
763
- * no mutator shadows, and a non-extensible one cannot take them.
764
- */
765
- function installMutatorShadows(object, tag) {
766
- const mutators = mutatorsByTag.get(tag);
767
- if (mutators === void 0 || !Object.isExtensible(object)) return false;
768
- const typeName = builtInTypeName(tag);
769
- for (const method of mutators) {
770
- shadowDescriptor.value = shadowOf(typeName, method);
771
- Object.defineProperty(object, method, shadowDescriptor);
772
- }
773
- return true;
774
- }
775
- /**
776
- * Whether the own property `key` of a built-in whose tag is `tag` is a
777
- * mutator shadow: a locked, non-enumerable function with the cooperative
778
- * brand of a shadow, under a mutator key of that built-in. The brand
779
- * catches accidents, not a forgery.
780
- */
781
- function isMutatorShadow(tag, key, descriptor) {
782
- const shadow = descriptor.value;
783
- return (mutatorsByTag.get(tag)?.includes(key) ?? false) && typeof shadow === "function" && descriptor.enumerable === false && descriptor.writable === false && descriptor.configurable === false && hasCooperativeBrand(shadow, mutatorShadowBrand);
784
- }
785
-
786
- //#endregion
787
- //#region src/domain/value-object/value-object.ts
788
- const mapEntries$1 = Map.prototype.entries;
789
- const setValues$1 = Set.prototype.values;
790
- const VALUE_OBJECT_CLASS = Symbol.for("@shirudo/ddd-kit/value-object-class/v1");
791
- function recordValueObjectClass(instance, valueObjectClass) {
792
- Object.defineProperty(instance, VALUE_OBJECT_CLASS, {
793
- value: valueObjectClass,
794
- enumerable: false,
795
- writable: false,
796
- configurable: false
797
- });
798
- }
799
- function isValueObjectInstance(value) {
800
- if (value === null || typeof value !== "object") return false;
801
- const record = Reflect.getOwnPropertyDescriptor(value, VALUE_OBJECT_CLASS);
802
- if (record === void 0 || typeof record.value !== "function" || record.enumerable !== false || record.writable !== false || record.configurable !== false) return false;
803
- const props = Reflect.getOwnPropertyDescriptor(value, "props");
804
- return props !== void 0 && isSealedProps(props.value);
805
- }
806
- function isSealedProps(props) {
807
- if (typeof props !== "object" || props === null) return false;
808
- return Object.isFrozen(props) || builtInTagWithoutInvokingAccessors(props) === "[object RegExp]";
809
- }
810
- function looksLikeValueObject(instance) {
811
- if (Object.hasOwn(instance, VALUE_OBJECT_CLASS)) return true;
812
- const props = Reflect.getOwnPropertyDescriptor(instance, "props");
813
- return props !== void 0 && isSealedProps(props.value);
814
- }
815
- function openValueObjectKeys(instance) {
816
- return Reflect.ownKeys(instance).filter((key) => key !== "props" && key !== VALUE_OBJECT_CLASS);
817
- }
818
- function rejectValueObjectAsInput(input, entry) {
819
- 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`);
820
- }
821
- /**
822
- * Deep freezes an object and all its nested properties recursively, then
823
- * returns it. Iterates both string-keyed and symbol-keyed own properties
824
- * so the freeze symmetry matches `deepEqual` (which also considers symbol
825
- * keys). Handles circular references by tracking visited objects.
826
- *
827
- * Note: `deepFreeze` mutates its argument in place; it sets `[[Frozen]]`
828
- * on the object you pass in. Callers that need to avoid touching the
829
- * input (e.g. `vo()`) should deep-clone first.
830
- *
831
- * Date/Map/Set keep internal-slot mutability under `Object.freeze`
832
- * (`setTime`, `set`, `add`, … still work on frozen instances), so their
833
- * mutator methods are shadowed with throwing own properties and Map/Set
834
- * contents are frozen recursively. The shadows are non-enumerable:
835
- * invisible to `Object.keys`, spread, `deepEqual`, and `structuredClone`.
836
- *
837
- * The shadowing is deny-by-enumeration: only the mutators known at
838
- * release time are blocked. If the runtime grows a NEW mutator (e.g. the
839
- * stage-3 `Map.prototype.getOrInsert` upsert proposal), it is not blocked
840
- * until the list is updated. Treat the mutator blocking as a guard rail,
841
- * not a security boundary.
842
- *
843
- * Two kinds of built-in pass through unfrozen. ArrayBuffer views
844
- * (TypedArrays, DataView): the spec forbids freezing a view with elements,
845
- * and a freeze cannot protect the underlying buffer, so their contents stay
846
- * mutable. RegExp: pattern and flags live in immutable internal slots, and
847
- * the only own data property, `lastIndex`, is scan state that every global
848
- * or sticky match writes. A frozen RegExp protects nothing and throws on
849
- * the first such match, so the RegExp keeps matching instead. A view or a
850
- * RegExp passes through whole: an expando property on it stays open, and
851
- * the walk does not enter its subtree.
852
- */
853
- const DEEP_FROZEN = /* @__PURE__ */ new WeakSet();
854
- const KIT_SHADOWED = /* @__PURE__ */ new WeakSet();
855
- function deepFreeze(obj) {
856
- const walk = {
857
- cyclic: false,
858
- sealed: [],
859
- done: /* @__PURE__ */ new Map(),
860
- inProgress: /* @__PURE__ */ new Set()
861
- };
862
- freezeDeep(obj, walk);
863
- if (!walk.cyclic) for (const object of walk.sealed) DEEP_FROZEN.add(object);
864
- return obj;
865
- }
866
- /** Freezes `obj` and its subtree; returns whether the subtree is sealed. */
867
- function freezeDeep(obj, walk) {
868
- if (obj === null || typeof obj !== "object") return true;
869
- if (DEEP_FROZEN.has(obj)) return true;
870
- if (ArrayBuffer.isView(obj)) return true;
871
- const finished = walk.done.get(obj);
872
- if (finished !== void 0) return finished;
873
- if (walk.inProgress.has(obj)) {
874
- walk.cyclic = true;
875
- return true;
876
- }
877
- const builtInTag = builtInTagWithoutInvokingAccessors(obj);
878
- if (builtInTag === "[object RegExp]") return true;
879
- walk.inProgress.add(obj);
880
- let sealed = true;
881
- if (builtInTag === "[object Date]" || builtInTag === "[object Map]" || builtInTag === "[object Set]") {
882
- if (builtInTag === "[object Map]") for (const [key, value] of obj) {
883
- if (!freezeDeep(key, walk)) sealed = false;
884
- if (!freezeDeep(value, walk)) sealed = false;
885
- }
886
- else if (builtInTag === "[object Set]") {
887
- for (const member of obj) if (!freezeDeep(member, walk)) sealed = false;
888
- }
889
- if (installMutatorShadows(obj, builtInTag) || KIT_SHADOWED.has(obj)) KIT_SHADOWED.add(obj);
890
- else sealed = false;
891
- }
892
- const keys = Reflect.ownKeys(obj);
893
- for (const key of keys) {
894
- const value = obj[key];
895
- if (value !== null && typeof value === "object") {
896
- if (!freezeDeep(value, walk)) sealed = false;
897
- }
898
- }
899
- Object.freeze(obj);
900
- walk.inProgress.delete(obj);
901
- walk.done.set(obj, sealed);
902
- if (sealed) walk.sealed.push(obj);
903
- return sealed;
904
- }
905
- /**
906
- * Deep clone used by `vo()` and the `ValueObject` constructor.
907
- *
908
- * Plain objects, arrays, and Map values are walked manually so
909
- * that symbol-keyed properties survive (which `structuredClone` silently
910
- * drops; they would otherwise be invisible to `voEquals`, whose
911
- * `deepEqual` DOES consider symbol keys) and shared references / cycles
912
- * keep their identity across Map boundaries. Function values throw,
913
- * preserving `vo()`'s documented data-not-behaviour gate. Built-ins without
914
- * immutable value semantics throw a descriptive `TypeError`. A kit
915
- * `ValueObject` instance is admitted by reference (see
916
- * `VALUE_OBJECT_CLASS`). Every other custom class instance and every
917
- * subclass of a built-in is rejected because cloning it without invoking
918
- * its constructor can silently lose private or non-enumerable state. Map
919
- * keys and Set members must be primitive because their equality is
920
- * identity-based and object identity cannot survive defensive cloning.
921
- * Accessor properties are rejected without invoking them. Admitted atomic
922
- * built-ins (Date, RegExp and primitive wrappers) delegate to
923
- * `structuredClone`, brand-verified so a `Symbol.toStringTag` spoofer is
924
- * walked as the plain object it is. `__proto__` own keys are copied as
925
- * inert data properties.
926
- */
927
- function cloneForVo(value, visited) {
928
- if (typeof value === "function") throw new TypeError("vo() does not accept function values: Value Objects are data, not behaviour");
929
- if (value === null || typeof value !== "object") return value;
930
- const obj = value;
931
- if (ArrayBuffer.isView(obj)) throwUnsupportedValueSemantics(builtInTagWithoutInvokingAccessors(obj) ?? "[object ArrayBuffer view]");
932
- if (visited.has(obj)) return visited.get(obj);
933
- if (Array.isArray(obj)) {
934
- if (!hasIntrinsicPrototypeChain(obj, "Array")) throwUnsupportedClassInstance(obj);
935
- const clone = new Array(obj.length);
936
- visited.set(obj, clone);
937
- for (const key of Reflect.ownKeys(obj)) {
938
- if (key === "length") continue;
939
- const descriptor = Object.getOwnPropertyDescriptor(obj, key);
940
- if (descriptor === void 0) continue;
941
- if (!("value" in descriptor)) throwUnsupportedAccessorProperty();
942
- if (typeof key === "string" && !descriptor.enumerable) continue;
943
- descriptor.value = cloneForVo(descriptor.value, visited);
944
- Object.defineProperty(clone, key, descriptor);
945
- }
946
- return clone;
947
- }
948
- const tag = builtInTagWithoutInvokingAccessors(obj);
949
- if (tag !== void 0) {
950
- if (!hasIntrinsicPrototypeChain(obj)) throwUnsupportedClassInstance(obj);
951
- if (tag === "[object Map]") {
952
- const clone = /* @__PURE__ */ new Map();
953
- visited.set(obj, clone);
954
- for (const [key, entry] of mapEntries$1.call(obj)) {
955
- if (!isPrimitiveValue(key)) throw new TypeError("vo() Map keys must be primitive values to preserve value equality");
956
- clone.set(key, cloneForVo(entry, visited));
957
- }
958
- return clone;
959
- }
960
- if (tag === "[object Set]") {
961
- const clone = /* @__PURE__ */ new Set();
962
- visited.set(obj, clone);
963
- for (const member of setValues$1.call(obj)) {
964
- if (!isPrimitiveValue(member)) throw new TypeError("vo() Set members must be primitive values to preserve value equality");
965
- clone.add(member);
966
- }
967
- return clone;
968
- }
969
- 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`);
970
- if (tag === "[object Error]" || tag === "[object ArrayBuffer]" || tag === "[object SharedArrayBuffer]") throwUnsupportedValueSemantics(tag);
971
- if (tag === "[object RegExp]") {
972
- const regExp = obj;
973
- 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");
974
- }
975
- const builtInClone = structuredClone(obj);
976
- visited.set(obj, builtInClone);
977
- return builtInClone;
978
- }
979
- const prototype = Object.getPrototypeOf(obj);
980
- if (prototype !== null && (!isIntrinsicConstructorPrototype(prototype, "Object") || Object.getPrototypeOf(prototype) !== null)) {
981
- if (isValueObjectInstance(obj)) {
982
- const openKeys = openValueObjectKeys(obj);
983
- if (openKeys.length > 0) throwOpenValueObjectFields(openKeys);
984
- return obj;
985
- }
986
- throwUnsupportedClassInstance(obj);
987
- }
988
- const clone = Object.create(prototype === null ? null : Object.prototype);
989
- visited.set(obj, clone);
990
- for (const key of Reflect.ownKeys(obj)) {
991
- const descriptor = Object.getOwnPropertyDescriptor(obj, key);
992
- if (descriptor === void 0) continue;
993
- if (!("value" in descriptor)) throwUnsupportedAccessorProperty();
994
- if (typeof key === "string" && !descriptor.enumerable) continue;
995
- Object.defineProperty(clone, key, {
996
- value: cloneForVo(descriptor.value, visited),
997
- writable: true,
998
- enumerable: descriptor.enumerable,
999
- configurable: true
1000
- });
1001
- }
1002
- return clone;
1003
- }
1004
- function throwUnsupportedClassInstance(instance) {
1005
- const valueObjectHint = looksLikeValueObject(instance) ? ". A value object is recognized only when a copy of this kit version built it and its props are frozen" : "";
1006
- throw new TypeError(`vo() cannot clone custom class instances: Value Objects are plain data${valueObjectHint}`);
1007
- }
1008
- function throwOpenValueObjectFields(keys) {
1009
- 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`);
1010
- }
1011
- function throwUnsupportedAccessorProperty() {
1012
- throw new TypeError("vo() cannot clone accessor properties: Value Objects are plain data");
1013
- }
1014
- function throwUnsupportedValueSemantics(tag) {
1015
- const name = tag.startsWith("[object ") ? tag.slice(8, -1) : tag;
1016
- throw new TypeError(`vo() cannot accept ${name} values: Value Objects require immutable value semantics`);
1017
- }
1018
- function isPrimitiveValue(value) {
1019
- return value === null || typeof value !== "object" && typeof value !== "function";
1020
- }
1021
- /**
1022
- * Creates a deeply immutable value object from the given data.
1023
- *
1024
- * The input is first deep-cloned, then the clone is frozen, so calling
1025
- * `vo(input)` never freezes the caller's own object graph as a
1026
- * side-effect. Mutating the input afterwards does not bleed into the VO.
1027
- * Symbol-keyed properties are preserved (matching `voEquals`). A kit
1028
- * `ValueObject` instance nested in the input is kept by reference and
1029
- * frozen in place; it must keep all of its state in `props`. A value
1030
- * object as the input itself is rejected.
1031
- * Function values and every other custom class instance are rejected
1032
- * (Value Objects are plain data, not behaviour-bearing object graphs). Inputs must be trusted and
1033
- * Proxy-free: ECMAScript provides no portable way to identify a transparent
1034
- * Proxy without potentially executing its traps, so `vo()` is not a sandbox
1035
- * for hostile in-process objects. Built-ins that cannot provide immutable,
1036
- * value-based semantics are rejected instead of weakening the VO contract.
1037
- *
1038
- * @example
1039
- * ```typescript
1040
- * const nested = { lat: 52.5, lng: 13.4 };
1041
- * const address = vo({ street: "Main St", coordinates: nested });
1042
- * address.coordinates.lat = 99; // ❌ Cannot assign to read-only property
1043
- * nested.lat = 0; // ✅ caller's input still mutable
1044
- * ```
1045
- */
1046
- function vo(t) {
1047
- rejectValueObjectAsInput(t, "vo()");
1048
- return deepFreeze(cloneForVo(t, /* @__PURE__ */ new WeakMap()));
1049
- }
1050
- /**
1051
- * Compares two value objects for equality based on their values.
1052
- * Uses deep equality comparison that handles:
1053
- * - Nested objects and arrays
1054
- * - Primitives (including NaN)
1055
- * - Dates, Maps, Sets, RegExp
1056
- * - Symbol keys
1057
- * - Circular references
1058
- *
1059
- * @param a - First value object
1060
- * @param b - Second value object
1061
- * @returns true if both objects have the same values, false otherwise
1062
- *
1063
- * @example
1064
- * ```typescript
1065
- * const money1 = vo({ amount: 100, currency: "USD" });
1066
- * const money2 = vo({ amount: 100, currency: "USD" });
1067
- * voEquals(money1, money2); // true
1068
- *
1069
- * const address1 = vo({
1070
- * street: "Main St",
1071
- * coordinates: { lat: 52.5, lng: 13.4 }
1072
- * });
1073
- * const address2 = vo({
1074
- * street: "Main St",
1075
- * coordinates: { lat: 52.5, lng: 13.4 }
1076
- * });
1077
- * voEquals(address1, address2); // true
1078
- * ```
1079
- */
1080
- function voEquals(a, b) {
1081
- return deepEqual(a, b);
1082
- }
1083
- /**
1084
- * Compares two value objects for equality while ignoring specified keys.
1085
- * Useful for comparing value objects that contain metadata or optional fields
1086
- * that should not affect equality comparison.
1087
- *
1088
- * The walk enters a nested `ValueObject` instance like any other object,
1089
- * so inside it the path continues with `props`; `ignoreKeys: ["props"]`
1090
- * empties every nested value object. The key under which the kit records
1091
- * the class of the instance is never ignored.
1092
- *
1093
- * @param a - First value object
1094
- * @param b - Second value object
1095
- * @param options - Options specifying which keys to ignore during comparison
1096
- * @returns true if both objects have the same values (after ignoring specified keys), false otherwise
1097
- *
1098
- * @example
1099
- * ```typescript
1100
- * // Value object with metadata
1101
- * const address1 = vo({
1102
- * street: "Main St",
1103
- * city: "Berlin",
1104
- * metadata: { createdAt: "2024-01-01", updatedAt: "2024-01-02" }
1105
- * });
1106
- *
1107
- * const address2 = vo({
1108
- * street: "Main St",
1109
- * city: "Berlin",
1110
- * metadata: { createdAt: "2024-01-01", updatedAt: "2024-01-03" }
1111
- * });
1112
- *
1113
- * // Compare ignoring metadata timestamps
1114
- * voEqualsExcept(address1, address2, {
1115
- * ignoreKeys: ["updatedAt"],
1116
- * ignoreKeyPredicate: (key, path) => path.includes("metadata")
1117
- * }); // true
1118
- *
1119
- * // Compare ignoring all metadata
1120
- * voEqualsExcept(address1, address2, {
1121
- * ignoreKeyPredicate: (key, path) => path.includes("metadata")
1122
- * }); // true
1123
- * ```
1124
- */
1125
- function voEqualsExcept(a, b, options) {
1126
- return deepEqualExcept(a, b, keepValueObjectClass(options));
1127
- }
1128
- function keepValueObjectClass(options) {
1129
- const { ignoreKeys, ignoreKeyPredicate } = options;
1130
- return {
1131
- ...options,
1132
- ignoreKeys: ignoreKeys?.filter((key) => key !== VALUE_OBJECT_CLASS),
1133
- ignoreKeyPredicate: ignoreKeyPredicate && ((key, path) => key !== VALUE_OBJECT_CLASS && ignoreKeyPredicate(key, path))
1134
- };
1135
- }
1136
- /**
1137
- * Creates a value object with optional validation.
1138
- * Returns a Result type instead of throwing an error.
1139
- *
1140
- * Note: the Result covers VALIDATION failures only. Non-data values and
1141
- * built-ins without immutable value semantics still throw a
1142
- * `TypeError` from `vo()`; they cannot occur in parsed JSON and signal
1143
- * a programming error, not a validation failure.
1144
- *
1145
- * @param t - The data to convert into a value object
1146
- * @param validate - Validation function that returns true if valid
1147
- * @param errorMessage - Optional custom error message if validation fails
1148
- * @returns Result containing the value object if valid, or an error message if validation fails
1149
- *
1150
- * @example
1151
- * ```typescript
1152
- * const result = voWithValidation(
1153
- * { amount: 100, currency: "USD" },
1154
- * (m) => m.amount >= 0 && m.currency.length === 3,
1155
- * "Invalid money: amount must be non-negative and currency must be 3 characters"
1156
- * );
1157
- *
1158
- * if (result.ok) {
1159
- * console.log(result.value); // Use the value object
1160
- * } else {
1161
- * console.error(result.error); // Handle validation error
1162
- * }
1163
- * ```
1164
- */
1165
- function voWithValidation(t, validate, errorMessage) {
1166
- if (!validate(t)) return err(errorMessage ?? `Validation failed for value object: ${describeValue(t)}`);
1167
- return ok(vo(t));
1168
- }
1169
- /**
1170
- * Best-effort rendering of a value for the default validation-failure
1171
- * message. `JSON.stringify` throws for cyclic and BigInt-bearing values, and
1172
- * the error path of a Result-returning function must never throw itself.
1173
- */
1174
- function describeValue(value) {
1175
- try {
1176
- const json = JSON.stringify(value);
1177
- if (json !== void 0) return json;
1178
- } catch {}
1179
- return String(value);
1180
- }
1181
- /**
1182
- * Abstract base class for creating Value Objects.
1183
- * Value Objects are immutable and defined by their properties. A value
1184
- * object can hold other value objects in its props. Every instance
1185
- * records its class under an own symbol key, so `equals` compares a
1186
- * nested value object by class and props and does not call its `equals`
1187
- * method.
1188
- *
1189
- * @template T - The shape of the value object's properties
1190
- */
1191
- var ValueObject = class {
1192
- props;
1193
- /**
1194
- * Creates a new ValueObject.
1195
- * The plain-data properties are deep-cloned and then deeply
1196
- * frozen, so the caller's own object graph is never frozen or mutated,
1197
- * and later mutation of the input does not bleed into the value object.
1198
- *
1199
- * @param props - The properties of the value object
1200
- * @example
1201
- * ```ts
1202
- * class Money extends ValueObject<{ amount: number; currency: string }> {
1203
- * constructor(props: { amount: number; currency: string }) {
1204
- * super(props);
1205
- * }
1206
- *
1207
- * protected validate(props: { amount: number; currency: string }): void {
1208
- * if (props.amount < 0) throw new Error("Amount cannot be negative");
1209
- * }
1210
- * }
1211
- * ```
1212
- */
1213
- constructor(props) {
1214
- rejectValueObjectAsInput(props, "new ValueObject()");
1215
- this.validate(props);
1216
- this.props = deepFreeze(cloneForVo(props, /* @__PURE__ */ new WeakMap()));
1217
- recordValueObjectClass(this, this.constructor);
1218
- }
1219
- /**
1220
- * Optional validation hook that can be overridden by subclasses.
1221
- * Should throw an error if validation fails.
1222
- *
1223
- * @param props - The properties to validate
1224
- * @throws Error if validation fails
1225
- */
1226
- validate(props) {}
1227
- /**
1228
- * Checks if this value object is equal to another.
1229
- * Uses deep equality comparison on the properties and checks for constructor equality.
1230
- *
1231
- * @param other - The other value object to compare
1232
- * @returns true if the properties are deeply equal and constructors match
1233
- */
1234
- equals(other) {
1235
- if (other === null || other === void 0) return false;
1236
- if (this.constructor !== other.constructor) return false;
1237
- return deepEqual(this.props, other.props);
1238
- }
1239
- /**
1240
- * Creates a clone of the value object with optional property overrides.
1241
- *
1242
- * @param props - Optional properties to override
1243
- * @returns A new instance of the value object
1244
- */
1245
- clone(props) {
1246
- const Constructor = this.constructor;
1247
- const merged = {
1248
- ...this.props,
1249
- ...props || {}
1250
- };
1251
- for (const key of Reflect.ownKeys(this.props)) {
1252
- const descriptor = Object.getOwnPropertyDescriptor(this.props, key);
1253
- if (descriptor === void 0 || descriptor.enumerable) continue;
1254
- if (props && Object.hasOwn(props, key)) continue;
1255
- Object.defineProperty(merged, key, {
1256
- value: this.props[key],
1257
- writable: true,
1258
- enumerable: false,
1259
- configurable: true
1260
- });
1261
- }
1262
- return new Constructor(merged);
1263
- }
1264
- /**
1265
- * Serializes the value object to its raw properties for JSON operations.
1266
- *
1267
- * @returns The raw properties object
1268
- */
1269
- toJSON() {
1270
- return this.props;
1271
- }
1272
- };
1273
-
1274
- //#endregion
1275
- //#region src/internal/structural/detach-state.ts
4
+ //#region src/internal/structural/copy-plain-state.ts
1276
5
  /**
1277
6
  * Returns a copy of `state` that shares no object with the original.
1278
7
  * Throws a `TypeError` that names the path when the graph carries a value
@@ -1281,7 +10,7 @@ var ValueObject = class {
1281
10
  * symbol-keyed, non-enumerable, or accessor property and an expando on a
1282
11
  * built-in are dropped. A function or a symbol value throws a raw
1283
12
  * `DataCloneError`. An Error, a Promise, a WeakMap, or a WeakSet cannot be
1284
- * 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
1285
14
  * their memory. A Proxy fails in the walk or in the clone; a failure in
1286
15
  * the clone is rethrown as a `TypeError` with the cause. An error that a
1287
16
  * Proxy trap throws in the walk passes unchanged, so a coded error of the
@@ -1310,26 +39,26 @@ var ValueObject = class {
1310
39
  * throws. A non-enumerable symbol key passes anywhere: it is metadata by
1311
40
  * convention.
1312
41
  *
1313
- * 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
1314
43
  * state. The snapshot model uses it for the captured DTO and the restored
1315
44
  * state. A state that carries a class-based child is mapped to plain data
1316
45
  * first, in the entity or in the model.
1317
46
  */
1318
- function detachState(state, subject = "detachState: state") {
1319
- return detach(state, subject, "clone");
47
+ function copyPlainState(state, subject = "copyPlainState: state") {
48
+ return copyState(state, subject, "clone");
1320
49
  }
1321
50
  /**
1322
- * {@link detachState} that also rejects what `deepFreeze` cannot make
51
+ * {@link copyPlainState} that also rejects what `deepFreeze` cannot make
1323
52
  * immutable: binary data (a typed array, a DataView, an ArrayBuffer, or a
1324
53
  * SharedArrayBuffer) and a RegExp. The walk rejects such a value at its
1325
54
  * tag, before it reads the indices of a typed array. Kit modules only.
1326
55
  */
1327
- function detachFreezableState(state, subject) {
1328
- return detach(state, subject, "freeze");
56
+ function copyFreezableState(state, subject) {
57
+ return copyState(state, subject, "freeze");
1329
58
  }
1330
- function detach(state, subject, rule) {
59
+ function copyState(state, subject, rule) {
1331
60
  const recording = new StepRecording();
1332
- assertDetachable(state, "", newWalk(subject, rule, recording));
61
+ assertCopyable(state, "", newWalk(subject, rule, recording));
1333
62
  let copy;
1334
63
  try {
1335
64
  copy = structuredClone(state);
@@ -1337,7 +66,7 @@ function detach(state, subject, rule) {
1337
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 });
1338
67
  }
1339
68
  const comparison = new StepComparison(recording, subject);
1340
- assertDetachable(copy, "", newWalk(subject, rule, comparison));
69
+ assertCopyable(copy, "", newWalk(subject, rule, comparison));
1341
70
  comparison.assertComplete();
1342
71
  return copy;
1343
72
  }
@@ -1419,7 +148,7 @@ function describeStep(step) {
1419
148
  const name = builtInTypeName(step.description);
1420
149
  return /^[AEIOU]/.test(name) ? `an ${name}` : `a ${name}`;
1421
150
  }
1422
- function assertDetachable(value, path, walk) {
151
+ function assertCopyable(value, path, walk) {
1423
152
  if (typeof value === "function") throw new TypeError(`${walk.subject}${path} is a function; map it to plain data`);
1424
153
  if (typeof value === "symbol") throw new TypeError(`${walk.subject}${path} is a symbol; map it to plain data`);
1425
154
  if (value === null || typeof value !== "object") return;
@@ -1429,7 +158,7 @@ function assertDetachable(value, path, walk) {
1429
158
  if (Array.isArray(object)) {
1430
159
  if (!hasIntrinsicPrototypeChain(object, "Array")) throwClassInstance(object, path, walk.subject);
1431
160
  step(walk, ARRAY_STEP, path);
1432
- assertOwnPropertiesDetachable(object, path, walk, "array");
161
+ assertOwnPropertiesCopyable(object, path, walk, "array");
1433
162
  return;
1434
163
  }
1435
164
  const tag = builtInTagWithoutInvokingAccessors(object);
@@ -1440,26 +169,26 @@ function assertDetachable(value, path, walk) {
1440
169
  if (tag === "[object Map]") {
1441
170
  let index = 0;
1442
171
  for (const [key, entry] of mapEntries.call(object)) {
1443
- assertDetachable(key, `${path}<map key #${index}>`, walk);
1444
- assertDetachable(entry, `${path}<map value #${index}>`, walk);
172
+ assertCopyable(key, `${path}<map key #${index}>`, walk);
173
+ assertCopyable(entry, `${path}<map value #${index}>`, walk);
1445
174
  index++;
1446
175
  }
1447
176
  } else if (tag === "[object Set]") {
1448
177
  let index = 0;
1449
178
  for (const member of setValues.call(object)) {
1450
- assertDetachable(member, `${path}<set member #${index}>`, walk);
179
+ assertCopyable(member, `${path}<set member #${index}>`, walk);
1451
180
  index++;
1452
181
  }
1453
- } else if (tag === "[object Promise]" || tag === "[object WeakMap]" || tag === "[object WeakSet]") throw new TypeError(`${walk.subject}${path} is a ${builtInTypeName(tag)} 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`);
1454
183
  else if (tag === "[object Error]") throw new TypeError(`${walk.subject}${path} is an Error; map it to plain data`);
1455
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`);
1456
- assertOwnPropertiesDetachable(object, path, walk, tag);
185
+ assertOwnPropertiesCopyable(object, path, walk, tag);
1457
186
  return;
1458
187
  }
1459
188
  const prototype = Object.getPrototypeOf(object);
1460
189
  if (!(prototype === null || isIntrinsicConstructorPrototype(prototype, "Object") && Object.getPrototypeOf(prototype) === null)) throwClassInstance(object, path, walk.subject);
1461
190
  step(walk, RECORD_STEP, path);
1462
- assertOwnPropertiesDetachable(object, path, walk, "record");
191
+ assertOwnPropertiesCopyable(object, path, walk, "record");
1463
192
  }
1464
193
  /**
1465
194
  * Audits the own properties the clone would copy or drop. The clone keeps
@@ -1471,7 +200,7 @@ function assertDetachable(value, path, walk) {
1471
200
  * built-in is an expando. `owner` is the kind of `object`: an array, a
1472
201
  * record, or the tag of a built-in.
1473
202
  */
1474
- function assertOwnPropertiesDetachable(object, path, walk, owner) {
203
+ function assertOwnPropertiesCopyable(object, path, walk, owner) {
1475
204
  const tag = owner === "array" || owner === "record" ? void 0 : owner;
1476
205
  const contentLength = tag === void 0 ? 0 : contentLengthOf(object, tag);
1477
206
  for (const key of Reflect.ownKeys(object)) {
@@ -1492,7 +221,7 @@ function assertOwnPropertiesDetachable(object, path, walk, owner) {
1492
221
  if (!("value" in descriptor)) throw new TypeError(`${walk.subject}${memberPath} is an accessor property; map it to plain data`);
1493
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`);
1494
223
  step(walk, key, memberPath);
1495
- assertDetachable(descriptor.value, memberPath, walk);
224
+ assertCopyable(descriptor.value, memberPath, walk);
1496
225
  }
1497
226
  }
1498
227
  /**
@@ -1547,6 +276,55 @@ function constructorName(prototype) {
1547
276
  return typeof name === "string" && name !== "" ? name : void 0;
1548
277
  }
1549
278
 
279
+ //#endregion
280
+ //#region src/internal/uuid-v7.ts
281
+ const COUNTER_LIMIT = 2 ** 42;
282
+ let lastMilliseconds = Number.NEGATIVE_INFINITY;
283
+ let counter = 0;
284
+ /**
285
+ * RFC 9562 UUID version 7: the Unix time in milliseconds in the first 48 bits,
286
+ * then random bits from Web Crypto. A B-tree index on such ids stays
287
+ * clustered.
288
+ *
289
+ * The ids of one process sort in creation order. Ids from several processes
290
+ * interleave within one millisecond and depend on their clocks. Within one
291
+ * millisecond of one process, a 42-bit counter increases by one per id: the
292
+ * 12 bits of `rand_a` and the first 30 bits of `rand_b`. It starts at a random
293
+ * value with its top bit clear, so it does not run out in practice, also not
294
+ * on a runtime that stops the clock during a request. If it runs out, the time
295
+ * field moves one millisecond ahead. A clock that goes back does not move the
296
+ * time field back. This is method 1 of RFC 9562, section 6.2.
297
+ */
298
+ function uuidV7() {
299
+ const bytes = /* @__PURE__ */ new Uint8Array(16);
300
+ globalThis.crypto.getRandomValues(bytes);
301
+ const view = new DataView(bytes.buffer);
302
+ const seed = (view.getUint16(0) & 511) * 2 ** 32 + view.getUint32(2);
303
+ const now = Date.now();
304
+ if (now > lastMilliseconds) {
305
+ lastMilliseconds = now;
306
+ counter = seed;
307
+ } else if (counter + 1 < COUNTER_LIMIT) counter += 1;
308
+ else {
309
+ lastMilliseconds += 1;
310
+ counter = seed;
311
+ }
312
+ let time = lastMilliseconds;
313
+ for (let index = 5; index >= 0; index -= 1) {
314
+ bytes[index] = time % 256;
315
+ time = Math.floor(time / 256);
316
+ }
317
+ const counterHigh = Math.floor(counter / 2 ** 24);
318
+ const counterLow = counter % 2 ** 24;
319
+ view.setUint16(6, 28672 | counterHigh >>> 6);
320
+ view.setUint8(8, 128 | counterHigh & 63);
321
+ view.setUint8(9, counterLow >>> 16);
322
+ view.setUint16(10, counterLow & 65535);
323
+ let hex = "";
324
+ for (const byte of bytes) hex += byte.toString(16).padStart(2, "0");
325
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
326
+ }
327
+
1550
328
  //#endregion
1551
329
  //#region src/domain/event/clock.ts
1552
330
  /** Immutable library default captured by the default domain-event factory. */
@@ -1591,14 +369,14 @@ var SnapshotTimeValidationError = class extends TypeError {
1591
369
 
1592
370
  //#endregion
1593
371
  //#region src/domain/event/domain-event.ts
1594
- const defaultEventIdFactory = () => crypto.randomUUID();
372
+ const defaultEventIdFactory = uuidV7;
1595
373
  /**
1596
374
  * Creates an immutable, instance-bound domain-event factory.
1597
375
  *
1598
376
  * The supplied functions are read once and captured by value. The returned
1599
377
  * object is frozen, so another request, test, or library cannot replace its
1600
378
  * policy. Its {@link DomainEventFactory.createStamp} method is the
1601
- * application-shell bridge that records an accepted aggregate decision.
379
+ * application-shell bridge that stamps an accepted aggregate decision.
1602
380
  * Passing the factory through `AggregateConfig`
1603
381
  * enables the explicitly named convenience methods, whose defaults read time
1604
382
  * and randomness.
@@ -1609,8 +387,7 @@ const defaultEventIdFactory = () => crypto.randomUUID();
1609
387
  * eventIdFactory: () => uuidv7(),
1610
388
  * clock: () => new Date(),
1611
389
  * });
1612
- * order.confirm();
1613
- * recordPendingEvents(order, domainEvents);
390
+ * const uow = new UnitOfWork({ ...deps, eventStampFactory: domainEvents });
1614
391
  * ```
1615
392
  */
1616
393
  function createDomainEventFactory(options = {}) {
@@ -1621,7 +398,7 @@ function buildDomainEventFactory(options, createOperation) {
1621
398
  const eventIdFactory = options.eventIdFactory ?? defaultEventIdFactory;
1622
399
  const clock = options.clock ?? defaultClockFactory;
1623
400
  const { source } = options;
1624
- const create = ((type, payload, createOptions) => mintDomainEvent(type, payload, createOptions, eventIdFactory, clock, createOperation, source));
401
+ const create = ((type, payload, createOptions) => buildDomainEvent(type, payload, createOptions, eventIdFactory, clock, createOperation, source));
1625
402
  const createStamp = (stampOptions = {}) => {
1626
403
  const explicitOccurredAt = stampOptions.occurredAt === void 0 ? void 0 : copyValidEventDate(stampOptions.occurredAt);
1627
404
  if (stampOptions.eventId !== void 0) assertNonBlankEventField(stampOptions.eventId, "eventId", "EVENT_ID_INVALID");
@@ -1648,49 +425,46 @@ function buildDomainEventFactory(options, createOperation) {
1648
425
  * with {@link createDomainEventFactory} for custom policy.
1649
426
  */
1650
427
  const defaultDomainEventFactory = buildDomainEventFactory({}, "createDomainEvent");
1651
- const RECORDED_EVENTS = /* @__PURE__ */ new WeakSet();
1652
- const UNCOMMITTED_EVENTS = /* @__PURE__ */ new WeakSet();
428
+ const STAMPED_EVENTS = /* @__PURE__ */ new WeakSet();
429
+ const UNSTAMPED_EVENTS = /* @__PURE__ */ new WeakSet();
1653
430
  const FACTORY_OWNED_EVENT_STAMPS = /* @__PURE__ */ new WeakSet();
1654
- const RECORDED_BRAND = Symbol.for("@shirudo/ddd-kit.mintedEvent");
1655
- const UNCOMMITTED_BRAND = Symbol.for("@shirudo/ddd-kit.uncommittedEvent");
1656
- function stampRecordedBrand(event) {
1657
- stampCooperativeBrand(event, RECORDED_BRAND);
431
+ const STAMPED_BRAND = Symbol.for("@shirudo/ddd-kit.mintedEvent");
432
+ const UNSTAMPED_BRAND = Symbol.for("@shirudo/ddd-kit.uncommittedEvent");
433
+ function brandStampedEvent(event) {
434
+ applyCooperativeBrand(event, STAMPED_BRAND);
1658
435
  }
1659
- function stampUncommittedBrand(event) {
1660
- stampCooperativeBrand(event, UNCOMMITTED_BRAND);
436
+ function brandUnstampedEvent(event) {
437
+ applyCooperativeBrand(event, UNSTAMPED_BRAND);
1661
438
  }
1662
439
  function isFactoryOwnedDomainEventStamp(stamp) {
1663
440
  return FACTORY_OWNED_EVENT_STAMPS.has(stamp);
1664
441
  }
1665
442
  /**
1666
- * Whether `event` is a recorded domain event: it came out of
1667
- * {@link createDomainEvent}, {@link createDomainEventFromFacts}, or
1668
- * {@link recordDomainEvent}, so it is deeply frozen with defensively copied
1669
- * payload and metadata. An uncommitted decision carries the other brand; see
1670
- * {@link isUncommittedDomainEvent}. Two tiers: events of THIS
443
+ * Whether `event` is a stamped domain event: it came out of
444
+ * {@link createDomainEvent}, {@link createStampedDomainEvent}, or
445
+ * {@link stampDomainEvent}, so it is deeply frozen with defensively copied
446
+ * payload and metadata. An unstamped decision carries the other brand; see
447
+ * {@link isUnstampedDomainEvent}. Two tiers: events of THIS
1671
448
  * loaded copy of the kit are verified through the module-private
1672
- * WeakSet; events minted by ANOTHER copy (duplicate dependency, dual
449
+ * WeakSet; events built by ANOTHER copy (duplicate dependency, dual
1673
450
  * CJS/ESM load) are recognized through a cooperative `Symbol.for`
1674
451
  * brand that code in the same process can fake. The gate catches
1675
452
  * accidents, not adversaries. Module-internal export for the aggregate
1676
453
  * recording paths; not part of the package entries.
1677
454
  */
1678
- function isRecordedDomainEvent(event) {
1679
- return RECORDED_EVENTS.has(event) || hasCooperativeBrand(event, RECORDED_BRAND);
1680
- }
1681
- /** Whether a value was created by {@link createUncommittedDomainEvent}. */
1682
- function isUncommittedDomainEvent(event) {
1683
- return UNCOMMITTED_EVENTS.has(event) || hasCooperativeBrand(event, UNCOMMITTED_BRAND);
455
+ function isStampedDomainEvent(event) {
456
+ return STAMPED_EVENTS.has(event) || hasCooperativeBrand(event, STAMPED_BRAND);
1684
457
  }
1685
- function createUncommittedDomainEvent(type, payload, options) {
1686
- return mintUncommittedDomainEvent(type, payload, options, "createUncommittedDomainEvent");
458
+ /** Whether a value is a kit-built unstamped event. */
459
+ function isUnstampedDomainEvent(event) {
460
+ return UNSTAMPED_EVENTS.has(event) || hasCooperativeBrand(event, UNSTAMPED_BRAND);
1687
461
  }
1688
462
  /**
1689
- * {@link createUncommittedDomainEvent} whose errors name `operation`, the
463
+ * {@link createUnstampedDomainEvent} whose errors name `operation`, the
1690
464
  * function that the caller called. Module-internal export; not part of the
1691
465
  * package entries.
1692
466
  */
1693
- function mintUncommittedDomainEvent(type, payload, options, operation) {
467
+ function buildUnstampedDomainEvent(type, payload, options, operation) {
1694
468
  assertProducerOwnedEventFields(type, options);
1695
469
  const event = {
1696
470
  type,
@@ -1699,41 +473,41 @@ function mintUncommittedDomainEvent(type, payload, options, operation) {
1699
473
  payload: cloneOwnedEventData(payload, "payload", operation),
1700
474
  schemaVersion: options?.schemaVersion ?? 1
1701
475
  };
1702
- stampUncommittedBrand(event);
1703
- const uncommitted = deepFreeze(event);
1704
- UNCOMMITTED_EVENTS.add(uncommitted);
1705
- return uncommitted;
1706
- }
1707
- /** Brands and freezes a kit-derived copy of an uncommitted event. */
1708
- function adoptUncommittedDomainEvent(copy) {
1709
- stampUncommittedBrand(copy);
476
+ brandUnstampedEvent(event);
477
+ const unstamped = deepFreeze(event);
478
+ UNSTAMPED_EVENTS.add(unstamped);
479
+ return unstamped;
480
+ }
481
+ /** Brands and freezes a kit-derived copy of an unstamped event. */
482
+ function adoptUnstampedDomainEvent(copy) {
483
+ brandUnstampedEvent(copy);
1710
484
  Object.freeze(copy);
1711
- UNCOMMITTED_EVENTS.add(copy);
485
+ UNSTAMPED_EVENTS.add(copy);
1712
486
  return copy;
1713
487
  }
1714
488
  /**
1715
- * Attaches shell-owned recording data to an accepted aggregate decision.
489
+ * Attaches shell-owned stamp data to an accepted aggregate decision.
1716
490
  *
1717
491
  * The decision supplies the domain type, payload, source aggregate
1718
492
  * identity, and payload schema version. The stamp supplies only event
1719
- * identity, recording time, and trace metadata.
493
+ * identity, stamp time, and trace metadata.
1720
494
  */
1721
- function recordDomainEvent(event, stamp) {
1722
- if (!isUncommittedDomainEvent(event)) throw new TypeError("recordDomainEvent requires an event created by createUncommittedDomainEvent");
1723
- if (isFactoryOwnedDomainEventStamp(stamp)) return mintRecordedEvent(event, stamp.eventId, stamp.occurredAt, stamp.metadata);
495
+ function stampDomainEvent(event, stamp) {
496
+ if (!isUnstampedDomainEvent(event)) throw new TypeError("stampDomainEvent requires an unstamped event from an aggregate's createEvent");
497
+ if (isFactoryOwnedDomainEventStamp(stamp)) return buildStampedEvent(event, stamp.eventId, stamp.occurredAt, stamp.metadata);
1724
498
  assertNonBlankEventField(stamp.eventId, "eventId", "EVENT_ID_INVALID");
1725
499
  const occurredAt = deepFreeze(copyValidEventDate(stamp.occurredAt));
1726
- const metadata = ownedEventMetadata(stamp.metadata, "recordDomainEvent");
1727
- return mintRecordedEvent(event, stamp.eventId, occurredAt, metadata === void 0 ? void 0 : deepFreeze(metadata));
500
+ const metadata = ownedEventMetadata(stamp.metadata, "stampDomainEvent");
501
+ return buildStampedEvent(event, stamp.eventId, occurredAt, metadata === void 0 ? void 0 : deepFreeze(metadata));
1728
502
  }
1729
503
  /**
1730
- * Single mint tail for both stamp provenances. The stamp fields arrive
504
+ * Single build tail for both stamp provenances. The stamp fields arrive
1731
505
  * pre-validated, copied, and frozen (by `createStamp` for factory-owned
1732
- * stamps, by `recordDomainEvent` for caller-built stamps); the uncommitted
506
+ * stamps, by `stampDomainEvent` for caller-built stamps); the unstamped
1733
507
  * event's payload is already defensively cloned and deeply frozen by its
1734
508
  * constructor and is shared instead of paying a second deep copy per event.
1735
509
  */
1736
- function mintRecordedEvent(event, eventId, occurredAt, metadata) {
510
+ function buildStampedEvent(event, eventId, occurredAt, metadata) {
1737
511
  assertProducerOwnedEventFields(event.type, event);
1738
512
  const recorded = {
1739
513
  eventId,
@@ -1745,42 +519,42 @@ function mintRecordedEvent(event, eventId, occurredAt, metadata) {
1745
519
  schemaVersion: event.schemaVersion,
1746
520
  metadata
1747
521
  };
1748
- stampRecordedBrand(recorded);
522
+ brandStampedEvent(recorded);
1749
523
  Object.freeze(recorded);
1750
- RECORDED_EVENTS.add(recorded);
524
+ STAMPED_EVENTS.add(recorded);
1751
525
  return recorded;
1752
526
  }
1753
527
  /**
1754
- * Brands, freezes, and registers a kit-derived copy of a recorded event
528
+ * Brands, freezes, and registers a kit-derived copy of a stamped event
1755
529
  * (e.g. the copy `apply()` stamps with the aggregate identity) as recorded
1756
530
  * itself.
1757
531
  * The copy shares the already-frozen payload/metadata of its source,
1758
- * so the mint guarantee carries over. Stamping the cooperative brand
532
+ * so the kit-built guarantee carries over. Applying the cooperative brand
1759
533
  * before freezing keeps the copy recognizable by another loaded kit
1760
534
  * instance as well as by this instance's WeakSet. Module-internal
1761
535
  * export; not part of the package entries.
1762
536
  */
1763
- function adoptRecordedDomainEvent(copy) {
1764
- stampRecordedBrand(copy);
537
+ function adoptStampedDomainEvent(copy) {
538
+ brandStampedEvent(copy);
1765
539
  Object.freeze(copy);
1766
- RECORDED_EVENTS.add(copy);
540
+ STAMPED_EVENTS.add(copy);
1767
541
  return copy;
1768
542
  }
1769
543
  function createDomainEvent(type, payload, options) {
1770
544
  return defaultDomainEventFactory.create(type, payload, options);
1771
545
  }
1772
- function createDomainEventFromFacts(type, payload, options) {
546
+ function createStampedDomainEvent(type, payload, options) {
1773
547
  if (options?.eventId === void 0) missingExplicitEventId();
1774
548
  if (options.occurredAt === void 0) missingExplicitOccurredAt();
1775
- return mintDomainEvent(type, payload, options, missingExplicitEventId, missingExplicitOccurredAt, "createDomainEventFromFacts");
549
+ return buildDomainEvent(type, payload, options, missingExplicitEventId, missingExplicitOccurredAt, "createStampedDomainEvent");
1776
550
  }
1777
551
  function missingExplicitEventId() {
1778
- throw new DomainEventValidationError("EVENT_ID_REQUIRED", "eventId", "createDomainEventFromFacts requires an explicit eventId");
552
+ throw new DomainEventValidationError("EVENT_ID_REQUIRED", "eventId", "createStampedDomainEvent requires an explicit eventId");
1779
553
  }
1780
554
  function missingExplicitOccurredAt() {
1781
- throw new DomainEventValidationError("EVENT_OCCURRED_AT_REQUIRED", "occurredAt", "createDomainEventFromFacts requires an explicit occurredAt");
555
+ throw new DomainEventValidationError("EVENT_OCCURRED_AT_REQUIRED", "occurredAt", "createStampedDomainEvent requires an explicit occurredAt");
1782
556
  }
1783
- function mintDomainEvent(type, payload, options, eventIdFactory, clock, operation, factorySource) {
557
+ function buildDomainEvent(type, payload, options, eventIdFactory, clock, operation, factorySource) {
1784
558
  assertProducerOwnedEventFields(type, options);
1785
559
  const eventId = options?.eventId ?? eventIdFactory();
1786
560
  assertNonBlankEventField(eventId, "eventId", "EVENT_ID_INVALID");
@@ -1796,10 +570,10 @@ function mintDomainEvent(type, payload, options, eventIdFactory, clock, operatio
1796
570
  schemaVersion,
1797
571
  metadata: ownedEventMetadata(options?.metadata, operation, factorySource)
1798
572
  };
1799
- stampRecordedBrand(event);
1800
- const minted = deepFreeze(event);
1801
- RECORDED_EVENTS.add(minted);
1802
- return minted;
573
+ brandStampedEvent(event);
574
+ const built = deepFreeze(event);
575
+ STAMPED_EVENTS.add(built);
576
+ return built;
1803
577
  }
1804
578
  function assertProducerOwnedEventFields(type, options) {
1805
579
  assertNonBlankEventField(type, "type", "EVENT_TYPE_INVALID");
@@ -1820,11 +594,11 @@ function readEventClock(clock) {
1820
594
  }
1821
595
  /**
1822
596
  * Copies the caller's metadata and checks the copy. Metadata is a plain
1823
- * record or absent; a null from a JSON envelope would mint and then fail
597
+ * record or absent; a null from a JSON envelope would build and then fail
1824
598
  * every metadata read far from the producer. A factory origin fills
1825
599
  * `source` only after the check, and a call site that names its own source
1826
600
  * keeps it: an explicit fact about one event outranks the default of the
1827
- * factory that mints it.
601
+ * factory that builds it.
1828
602
  */
1829
603
  function ownedEventMetadata(metadata, operation, factorySource) {
1830
604
  if (metadata === void 0) return factorySource === void 0 ? void 0 : { source: factorySource };
@@ -1854,12 +628,12 @@ function isPlainRecordCopy(copy) {
1854
628
  /**
1855
629
  * Deep-clones caller-supplied event data (payload, metadata) before the
1856
630
  * event is frozen, so a constructor never freezes or aliases the caller's
1857
- * own object graph. {@link detachFreezableState} checks the data and the
631
+ * own object graph. {@link copyFreezableState} checks the data and the
1858
632
  * clone; its JSDoc states the rule and its limits.
1859
633
  */
1860
634
  function cloneOwnedEventData(value, field, operation) {
1861
- if (value !== null && typeof value === "object") assertNoHostileOwnProtoKey(value, `Event ${field} of ${operation}`);
1862
- return detachFreezableState(value, `${operation}: ${field}`);
635
+ if (value !== null && typeof value === "object") assertNoOwnProtoKey(value, `Event ${field} of ${operation}`);
636
+ return copyFreezableState(value, `${operation}: ${field}`);
1863
637
  }
1864
638
  /**
1865
639
  * Copies metadata from a source event to a new event.
@@ -1875,8 +649,8 @@ function cloneOwnedEventData(value, field, operation) {
1875
649
  * ```
1876
650
  */
1877
651
  function copyMetadata(sourceEvent, additionalMetadata) {
1878
- if (sourceEvent.metadata !== void 0) assertNoHostileOwnProtoKey(sourceEvent.metadata, "Event metadata");
1879
- if (additionalMetadata !== void 0) assertNoHostileOwnProtoKey(additionalMetadata, "Event metadata");
652
+ if (sourceEvent.metadata !== void 0) assertNoOwnProtoKey(sourceEvent.metadata, "Event metadata");
653
+ if (additionalMetadata !== void 0) assertNoOwnProtoKey(additionalMetadata, "Event metadata");
1880
654
  return {
1881
655
  ...sourceEvent.metadata ?? {},
1882
656
  ...additionalMetadata ?? {}
@@ -1899,7 +673,7 @@ function mergeMetadata(...metadataObjects) {
1899
673
  const merged = {};
1900
674
  for (const metadata of metadataObjects) {
1901
675
  if (!metadata) continue;
1902
- assertNoHostileOwnProtoKey(metadata, "Event metadata");
676
+ assertNoOwnProtoKey(metadata, "Event metadata");
1903
677
  for (const key of Reflect.ownKeys(metadata)) {
1904
678
  if (!Object.getOwnPropertyDescriptor(metadata, key)?.enumerable) continue;
1905
679
  Object.defineProperty(merged, key, {
@@ -1994,7 +768,7 @@ function assertNonNegativeSafeInteger(context, field, value) {
1994
768
  /** Default bound for delivery and post-commit operations. */
1995
769
  const DEFAULT_EXECUTION_TIMEOUT_MS = 3e4;
1996
770
  /**
1997
- * Owner signal of each child signal that {@link runBoundedExecution} minted.
771
+ * Owner signal of each child signal that {@link runBoundedExecution} created.
1998
772
  *
1999
773
  * One bounded operation often wraps another, and every hop derives a fresh
2000
774
  * signal. A consumer that follows a call chain by signal identity alone loses
@@ -2299,5 +1073,5 @@ function throwIfAborted(signal) {
2299
1073
  }
2300
1074
 
2301
1075
  //#endregion
2302
- export { SnapshotTimeValidationError as A, stampCooperativeBrand as B, defaultDomainEventFactory as C, mintUncommittedDomainEvent as D, mergeMetadata as E, vo as F, isBuiltInObject as G, deepOmit as H, voEquals as I, isIntrinsicConstructorPrototype as K, voEqualsExcept as L, detachState as M, ValueObject as N, recordDomainEvent as O, deepFreeze as P, voWithValidation 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, hasCooperativeBrand as z };
1076
+ 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 };
2303
1077
  //# sourceMappingURL=read-stream-pages.js.map