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

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.
@@ -49,7 +49,7 @@ const sharedArrayBufferByteLengthGet = typeof SharedArrayBuffer === "undefined"
49
49
  const regExpSourceGet = intrinsicGetter(RegExp.prototype, "source");
50
50
  const booleanValueOf = Boolean.prototype.valueOf;
51
51
  const numberValueOf = Number.prototype.valueOf;
52
- const stringValueOf = String.prototype.valueOf;
52
+ const stringValueOf$1 = String.prototype.valueOf;
53
53
  const bigIntValueOf = BigInt.prototype.valueOf;
54
54
  const functionToString = Function.prototype.toString;
55
55
  const PROBE_KEY = {};
@@ -197,6 +197,10 @@ function builtInTagWithoutInvokingAccessors(value) {
197
197
  if (BUILT_IN_TAGS.has(tag) && hasBrand(value, tag)) return tag;
198
198
  return descriptor === void 0 ? void 0 : builtInTagFromBrand(value);
199
199
  }
200
+ /** The type name in a tag: `"Map"` for `"[object Map]"`. */
201
+ function builtInTypeName(tag) {
202
+ return tag.slice(8, -1);
203
+ }
200
204
  function builtInTagFromBrand(value) {
201
205
  if (hasBrand(value, "[object Date]")) return "[object Date]";
202
206
  if (hasBrand(value, "[object RegExp]")) return "[object RegExp]";
@@ -268,7 +272,7 @@ function hasBrand(obj, tag) {
268
272
  numberValueOf.call(obj);
269
273
  return true;
270
274
  case "[object String]":
271
- stringValueOf.call(obj);
275
+ stringValueOf$1.call(obj);
272
276
  return true;
273
277
  case "[object BigInt]":
274
278
  bigIntValueOf.call(obj);
@@ -646,47 +650,106 @@ function deepEqualExcept(a, b, options) {
646
650
  }
647
651
 
648
652
  //#endregion
649
- //#region src/domain/value-object/value-object.ts
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
650
701
  /**
651
702
  * `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).
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.
658
708
  */
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) {
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) {
681
742
  const key = `${typeName}.${method}`;
682
- let thrower = mutationThrowers.get(key);
683
- if (!thrower) {
684
- thrower = function throwFrozenMutation() {
743
+ let shadow = shadows.get(key);
744
+ if (shadow === void 0) {
745
+ shadow = function throwFrozenMutation() {
685
746
  throw new TypeError(`Cannot call ${method}() on a ${typeName} inside a deeply frozen value`);
686
747
  };
687
- mutationThrowers.set(key, thrower);
748
+ stampCooperativeBrand(shadow, mutatorShadowBrand);
749
+ Object.freeze(shadow);
750
+ shadows.set(key, shadow);
688
751
  }
689
- return thrower;
752
+ return shadow;
690
753
  }
691
754
  const shadowDescriptor = {
692
755
  value: void 0,
@@ -694,15 +757,36 @@ const shadowDescriptor = {
694
757
  enumerable: false,
695
758
  configurable: false
696
759
  };
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);
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);
703
772
  }
704
773
  return true;
705
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;
706
790
  const VALUE_OBJECT_CLASS = Symbol.for("@shirudo/ddd-kit/value-object-class/v1");
707
791
  function recordValueObjectClass(instance, valueObjectClass) {
708
792
  Object.defineProperty(instance, VALUE_OBJECT_CLASS, {
@@ -795,27 +879,14 @@ function freezeDeep(obj, walk) {
795
879
  walk.inProgress.add(obj);
796
880
  let sealed = true;
797
881
  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]") {
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]") {
811
887
  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
888
  }
818
- if (shadowed) KIT_SHADOWED.add(obj);
889
+ if (installMutatorShadows(obj, builtInTag) || KIT_SHADOWED.has(obj)) KIT_SHADOWED.add(obj);
819
890
  else sealed = false;
820
891
  }
821
892
  const keys = Reflect.ownKeys(obj);
@@ -1200,53 +1271,6 @@ var ValueObject = class {
1200
1271
  }
1201
1272
  };
1202
1273
 
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
1274
  //#endregion
1251
1275
  //#region src/internal/structural/detach-state.ts
1252
1276
  /**
@@ -1259,7 +1283,9 @@ function hasCooperativeBrand(value, brand) {
1259
1283
  * `DataCloneError`. An Error, a Promise, a WeakMap, or a WeakSet cannot be
1260
1284
  * detached at all. A SharedArrayBuffer and a view over one keep sharing
1261
1285
  * 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.
1286
+ * the clone is rethrown as a `TypeError` with the cause. An error that a
1287
+ * Proxy trap throws in the walk passes unchanged, so a coded error of the
1288
+ * caller keeps its code.
1263
1289
  *
1264
1290
  * The walk reads the graph through property descriptors and walks a Map
1265
1291
  * or a Set with the built-in iteration, so no accessor and no own iterator
@@ -1267,17 +1293,22 @@ function hasCooperativeBrand(value, brand) {
1267
1293
  * change the graph, and an accessor that a trap adds runs during the
1268
1294
  * clone. A second walk checks the clone, so a value that a trap swapped in
1269
1295
  * 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.
1296
+ * too. A trap that changes the graph after the first walk can still hide a
1297
+ * value that the copy drops: the walks catch accidents, not a hostile
1298
+ * Proxy. The second walk also follows the steps of the first walk: the kind
1299
+ * of each object and the key of each data property, in walk order. A copy
1300
+ * that differs throws, because a built-in whose prototype was replaced is
1301
+ * a built-in again in the copy and loses its own properties. `subject`
1302
+ * names the state in the messages.
1274
1303
  *
1275
1304
  * Plain objects (from any realm), arrays, Dates, Maps, Sets, bigints, and
1276
1305
  * typed arrays pass. A RegExp passes: pattern and flags survive the clone,
1277
1306
  * 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.
1307
+ * pattern is not domain data. On a built-in, the mutator shadows of a deep
1308
+ * freeze, the `lastIndex` of a RegExp, and the `length` of a boxed String
1309
+ * pass: they are machinery, not data. Every other non-enumerable property
1310
+ * throws. A non-enumerable symbol key passes anywhere: it is metadata by
1311
+ * convention.
1281
1312
  *
1282
1313
  * The concrete entity uses it for a detached read DTO of a plain-data
1283
1314
  * state. The snapshot model uses it for the captured DTO and the restored
@@ -1296,41 +1327,18 @@ function detachState(state, subject = "detachState: state") {
1296
1327
  function detachFreezableState(state, subject) {
1297
1328
  return detach(state, subject, "freeze");
1298
1329
  }
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
1330
  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);
1331
+ const recording = new StepRecording();
1332
+ assertDetachable(state, "", newWalk(subject, rule, recording));
1320
1333
  let copy;
1321
1334
  try {
1322
1335
  copy = structuredClone(state);
1323
1336
  } catch (cause) {
1324
- throw new TypeError(`${subject} holds a Proxy or a host object that cannot be cloned; map it to plain data`, { cause });
1337
+ 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
1338
  }
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`);
1339
+ const comparison = new StepComparison(recording, subject);
1340
+ assertDetachable(copy, "", newWalk(subject, rule, comparison));
1341
+ comparison.assertComplete();
1334
1342
  return copy;
1335
1343
  }
1336
1344
  const INDEX_KEY = /^(0|[1-9]\d*)$/;
@@ -1341,8 +1349,76 @@ const BINARY_DATA_TAGS = /* @__PURE__ */ new Set([
1341
1349
  "[object SharedArrayBuffer]"
1342
1350
  ]);
1343
1351
  const REGEXP_TAG = "[object RegExp]";
1352
+ const STRING_TAG = "[object String]";
1344
1353
  const mapEntries = Map.prototype.entries;
1345
1354
  const setValues = Set.prototype.values;
1355
+ const RECORD_STEP = Symbol("record");
1356
+ const ARRAY_STEP = Symbol("array");
1357
+ const builtInSteps = /* @__PURE__ */ new Map();
1358
+ function builtInStep(tag) {
1359
+ let kind = builtInSteps.get(tag);
1360
+ if (kind === void 0) {
1361
+ kind = Symbol(tag);
1362
+ builtInSteps.set(tag, kind);
1363
+ }
1364
+ return kind;
1365
+ }
1366
+ /**
1367
+ * The steps of the walk over the original, in walk order.
1368
+ */
1369
+ var StepRecording = class {
1370
+ steps = [];
1371
+ take(next) {
1372
+ this.steps.push(next);
1373
+ }
1374
+ };
1375
+ /**
1376
+ * The walk over the copy compares each of its steps with the step of the
1377
+ * walk over the original at the same position. Structured clone keeps the
1378
+ * order of the keys, of Map entries, and of Set members, and it keeps
1379
+ * shared references, so the walk over an unchanged copy takes the same
1380
+ * steps. A step that differs, or a step that is missing, means that the
1381
+ * copy changed.
1382
+ */
1383
+ var StepComparison = class {
1384
+ recording;
1385
+ subject;
1386
+ #position = 0;
1387
+ constructor(recording, subject) {
1388
+ this.recording = recording;
1389
+ this.subject = subject;
1390
+ }
1391
+ take(next, path) {
1392
+ const expected = this.recording.steps[this.#position];
1393
+ if (expected !== next) throwChangedInCopy(this.subject, path, expected, next);
1394
+ this.#position += 1;
1395
+ }
1396
+ assertComplete() {
1397
+ if (this.#position < this.recording.steps.length) throwChangedInCopy(this.subject, "", this.recording.steps[this.#position], void 0);
1398
+ }
1399
+ };
1400
+ function newWalk(subject, rule, trace) {
1401
+ return {
1402
+ seen: /* @__PURE__ */ new WeakSet(),
1403
+ subject,
1404
+ rule,
1405
+ trace
1406
+ };
1407
+ }
1408
+ function step(walk, next, path) {
1409
+ walk.trace.take(next, path);
1410
+ }
1411
+ function throwChangedInCopy(subject, path, expected, found) {
1412
+ 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`);
1413
+ }
1414
+ function describeStep(step) {
1415
+ if (step === void 0) return "nothing";
1416
+ if (typeof step === "string") return `the key "${step}"`;
1417
+ if (step === RECORD_STEP) return "a record";
1418
+ if (step === ARRAY_STEP) return "an array";
1419
+ const name = builtInTypeName(step.description);
1420
+ return /^[AEIOU]/.test(name) ? `an ${name}` : `a ${name}`;
1421
+ }
1346
1422
  function assertDetachable(value, path, walk) {
1347
1423
  if (typeof value === "function") throw new TypeError(`${walk.subject}${path} is a function; map it to plain data`);
1348
1424
  if (typeof value === "symbol") throw new TypeError(`${walk.subject}${path} is a symbol; map it to plain data`);
@@ -1352,6 +1428,7 @@ function assertDetachable(value, path, walk) {
1352
1428
  walk.seen.add(object);
1353
1429
  if (Array.isArray(object)) {
1354
1430
  if (!hasIntrinsicPrototypeChain(object, "Array")) throwClassInstance(object, path, walk.subject);
1431
+ step(walk, ARRAY_STEP, path);
1355
1432
  assertOwnPropertiesDetachable(object, path, walk, "array");
1356
1433
  return;
1357
1434
  }
@@ -1359,6 +1436,7 @@ function assertDetachable(value, path, walk) {
1359
1436
  if (tag !== void 0) {
1360
1437
  if (walk.rule === "freeze") assertFreezable(tag, path, walk.subject);
1361
1438
  if (!hasIntrinsicPrototypeChain(object)) throwClassInstance(object, path, walk.subject);
1439
+ step(walk, builtInStep(tag), path);
1362
1440
  if (tag === "[object Map]") {
1363
1441
  let index = 0;
1364
1442
  for (const [key, entry] of mapEntries.call(object)) {
@@ -1372,25 +1450,30 @@ function assertDetachable(value, path, walk) {
1372
1450
  assertDetachable(member, `${path}<set member #${index}>`, walk);
1373
1451
  index++;
1374
1452
  }
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`);
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`);
1376
1454
  else if (tag === "[object Error]") throw new TypeError(`${walk.subject}${path} is an Error; map it to plain data`);
1377
1455
  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");
1456
+ assertOwnPropertiesDetachable(object, path, walk, tag);
1379
1457
  return;
1380
1458
  }
1381
1459
  const prototype = Object.getPrototypeOf(object);
1382
1460
  if (!(prototype === null || isIntrinsicConstructorPrototype(prototype, "Object") && Object.getPrototypeOf(prototype) === null)) throwClassInstance(object, path, walk.subject);
1461
+ step(walk, RECORD_STEP, path);
1383
1462
  assertOwnPropertiesDetachable(object, path, walk, "record");
1384
1463
  }
1385
1464
  /**
1386
1465
  * Audits the own properties the clone would copy or drop. The clone keeps
1387
1466
  * 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.
1467
+ * and drops every own key of another built-in. `length` on an array is
1468
+ * its own machinery, not data, and so are the keys that
1469
+ * {@link isBuiltInMachinery} names. The index keys of a built-in below its
1470
+ * content length are its content and pass as such. Another index key of a
1471
+ * built-in is an expando. `owner` is the kind of `object`: an array, a
1472
+ * record, or the tag of a built-in.
1392
1473
  */
1393
- function assertOwnPropertiesDetachable(object, path, walk, kind) {
1474
+ function assertOwnPropertiesDetachable(object, path, walk, owner) {
1475
+ const tag = owner === "array" || owner === "record" ? void 0 : owner;
1476
+ const contentLength = tag === void 0 ? 0 : contentLengthOf(object, tag);
1394
1477
  for (const key of Reflect.ownKeys(object)) {
1395
1478
  const descriptor = Object.getOwnPropertyDescriptor(object, key);
1396
1479
  if (descriptor === void 0) continue;
@@ -1398,20 +1481,41 @@ function assertOwnPropertiesDetachable(object, path, walk, kind) {
1398
1481
  if (!descriptor.enumerable) continue;
1399
1482
  throw new TypeError(`${walk.subject}${path} has a symbol-keyed property; map it to plain data`);
1400
1483
  }
1401
- if (kind === "array" && key === "length") continue;
1484
+ if (owner === "array" && key === "length") continue;
1402
1485
  const isIndex = INDEX_KEY.test(key);
1486
+ const memberPath = isIndex ? `${path}[${key}]` : `${path}.${key}`;
1403
1487
  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`);
1488
+ if (tag !== void 0 && isBuiltInMachinery(tag, key, descriptor)) continue;
1489
+ throw new TypeError(`${walk.subject}${memberPath} is not enumerable and the clone would drop it; map it to plain data`);
1406
1490
  }
1407
- if (kind === "built-in" && isIndex) continue;
1408
- const memberPath = isIndex ? `${path}[${key}]` : `${path}.${key}`;
1491
+ if (tag !== void 0 && isIndex && Number(key) < contentLength) continue;
1409
1492
  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;
1493
+ 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
+ step(walk, key, memberPath);
1412
1495
  assertDetachable(descriptor.value, memberPath, walk);
1413
1496
  }
1414
1497
  }
1498
+ /**
1499
+ * Whether an own non-enumerable property of a built-in is machinery, not
1500
+ * data: a function that shadows a mutator of a frozen Date, Map, or Set,
1501
+ * the scan position of a RegExp, or the length of a boxed String. The
1502
+ * clone drops the shadow and rebuilds the other two from the internal
1503
+ * slots.
1504
+ */
1505
+ function isBuiltInMachinery(tag, key, descriptor) {
1506
+ return isMutatorShadow(tag, key, descriptor) || tag === REGEXP_TAG && key === "lastIndex" || tag === STRING_TAG && key === "length";
1507
+ }
1508
+ /**
1509
+ * The number of index keys that hold the content of a built-in: the
1510
+ * elements of a typed array and the characters of a boxed String.
1511
+ */
1512
+ function contentLengthOf(object, tag) {
1513
+ if (tag === "[object TypedArray]") return typedArrayLength.call(object);
1514
+ if (tag === STRING_TAG) return stringValueOf.call(object).length;
1515
+ return 0;
1516
+ }
1517
+ const typedArrayLength = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Uint8Array.prototype), "length")?.get;
1518
+ const stringValueOf = String.prototype.valueOf;
1415
1519
  const typedArrayBuffer = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Uint8Array.prototype), "buffer")?.get;
1416
1520
  const dataViewBuffer = Object.getOwnPropertyDescriptor(DataView.prototype, "buffer")?.get;
1417
1521
  function sharesMemory(object, tag) {
@@ -1544,48 +1648,6 @@ function buildDomainEventFactory(options, createOperation) {
1544
1648
  * with {@link createDomainEventFactory} for custom policy.
1545
1649
  */
1546
1650
  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
1651
  const RECORDED_EVENTS = /* @__PURE__ */ new WeakSet();
1590
1652
  const UNCOMMITTED_EVENTS = /* @__PURE__ */ new WeakSet();
1591
1653
  const FACTORY_OWNED_EVENT_STAMPS = /* @__PURE__ */ new WeakSet();
@@ -1792,25 +1854,12 @@ function isPlainRecordCopy(copy) {
1792
1854
  /**
1793
1855
  * Deep-clones caller-supplied event data (payload, metadata) before the
1794
1856
  * 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.
1857
+ * own object graph. {@link detachFreezableState} checks the data and the
1858
+ * clone; its JSDoc states the rule and its limits.
1798
1859
  */
1799
1860
  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;
1861
+ if (value !== null && typeof value === "object") assertNoHostileOwnProtoKey(value, `Event ${field} of ${operation}`);
1862
+ return detachFreezableState(value, `${operation}: ${field}`);
1814
1863
  }
1815
1864
  /**
1816
1865
  * Copies metadata from a source event to a new event.
@@ -2250,5 +2299,5 @@ function throwIfAborted(signal) {
2250
2299
  }
2251
2300
 
2252
2301
  //#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 };
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 };
2254
2303
  //# sourceMappingURL=read-stream-pages.js.map