@shirudo/ddd-kit 3.0.0-rc.5 → 3.0.0-rc.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/ports.js +82 -16
- package/dist/chunks/ports.js.map +1 -1
- package/dist/chunks/snapshot-store.d.ts +4 -2
- package/dist/index.d.ts +63 -11
- package/dist/index.js +121 -65
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/chunks/ports.js
CHANGED
|
@@ -703,6 +703,37 @@ function shadowMutators(obj, typeName, methods) {
|
|
|
703
703
|
}
|
|
704
704
|
return true;
|
|
705
705
|
}
|
|
706
|
+
const VALUE_OBJECT_CLASS = Symbol.for("@shirudo/ddd-kit/value-object-class/v1");
|
|
707
|
+
function recordValueObjectClass(instance, valueObjectClass) {
|
|
708
|
+
Object.defineProperty(instance, VALUE_OBJECT_CLASS, {
|
|
709
|
+
value: valueObjectClass,
|
|
710
|
+
enumerable: false,
|
|
711
|
+
writable: false,
|
|
712
|
+
configurable: false
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
function isValueObjectInstance(value) {
|
|
716
|
+
if (value === null || typeof value !== "object") return false;
|
|
717
|
+
const record = Reflect.getOwnPropertyDescriptor(value, VALUE_OBJECT_CLASS);
|
|
718
|
+
if (record === void 0 || typeof record.value !== "function" || record.enumerable !== false || record.writable !== false || record.configurable !== false) return false;
|
|
719
|
+
const props = Reflect.getOwnPropertyDescriptor(value, "props");
|
|
720
|
+
return props !== void 0 && isSealedProps(props.value);
|
|
721
|
+
}
|
|
722
|
+
function isSealedProps(props) {
|
|
723
|
+
if (typeof props !== "object" || props === null) return false;
|
|
724
|
+
return Object.isFrozen(props) || builtInTagWithoutInvokingAccessors(props) === "[object RegExp]";
|
|
725
|
+
}
|
|
726
|
+
function looksLikeValueObject(instance) {
|
|
727
|
+
if (Object.hasOwn(instance, VALUE_OBJECT_CLASS)) return true;
|
|
728
|
+
const props = Reflect.getOwnPropertyDescriptor(instance, "props");
|
|
729
|
+
return props !== void 0 && isSealedProps(props.value);
|
|
730
|
+
}
|
|
731
|
+
function openValueObjectKeys(instance) {
|
|
732
|
+
return Reflect.ownKeys(instance).filter((key) => key !== "props" && key !== VALUE_OBJECT_CLASS);
|
|
733
|
+
}
|
|
734
|
+
function rejectValueObjectAsInput(input, entry) {
|
|
735
|
+
if (isValueObjectInstance(input)) throw new TypeError(`${entry} does not accept a value object as its input: nest the value object under a key, or pass its props`);
|
|
736
|
+
}
|
|
706
737
|
/**
|
|
707
738
|
* Deep freezes an object and all its nested properties recursively, then
|
|
708
739
|
* returns it. Iterates both string-keyed and symbol-keyed own properties
|
|
@@ -809,11 +840,12 @@ function freezeDeep(obj, walk) {
|
|
|
809
840
|
* `deepEqual` DOES consider symbol keys) and shared references / cycles
|
|
810
841
|
* keep their identity across Map boundaries. Function values throw,
|
|
811
842
|
* preserving `vo()`'s documented data-not-behaviour gate. Built-ins without
|
|
812
|
-
* immutable value semantics throw a descriptive `TypeError`.
|
|
813
|
-
*
|
|
814
|
-
*
|
|
815
|
-
*
|
|
816
|
-
*
|
|
843
|
+
* immutable value semantics throw a descriptive `TypeError`. A kit
|
|
844
|
+
* `ValueObject` instance is admitted by reference (see
|
|
845
|
+
* `VALUE_OBJECT_CLASS`). Every other custom class instance and every
|
|
846
|
+
* subclass of a built-in is rejected because cloning it without invoking
|
|
847
|
+
* its constructor can silently lose private or non-enumerable state. Map
|
|
848
|
+
* keys and Set members must be primitive because their equality is
|
|
817
849
|
* identity-based and object identity cannot survive defensive cloning.
|
|
818
850
|
* Accessor properties are rejected without invoking them. Admitted atomic
|
|
819
851
|
* built-ins (Date, RegExp and primitive wrappers) delegate to
|
|
@@ -828,7 +860,7 @@ function cloneForVo(value, visited) {
|
|
|
828
860
|
if (ArrayBuffer.isView(obj)) throwUnsupportedValueSemantics(builtInTagWithoutInvokingAccessors(obj) ?? "[object ArrayBuffer view]");
|
|
829
861
|
if (visited.has(obj)) return visited.get(obj);
|
|
830
862
|
if (Array.isArray(obj)) {
|
|
831
|
-
if (!hasIntrinsicPrototypeChain(obj, "Array")) throwUnsupportedClassInstance();
|
|
863
|
+
if (!hasIntrinsicPrototypeChain(obj, "Array")) throwUnsupportedClassInstance(obj);
|
|
832
864
|
const clone = new Array(obj.length);
|
|
833
865
|
visited.set(obj, clone);
|
|
834
866
|
for (const key of Reflect.ownKeys(obj)) {
|
|
@@ -844,7 +876,7 @@ function cloneForVo(value, visited) {
|
|
|
844
876
|
}
|
|
845
877
|
const tag = builtInTagWithoutInvokingAccessors(obj);
|
|
846
878
|
if (tag !== void 0) {
|
|
847
|
-
if (!hasIntrinsicPrototypeChain(obj)) throwUnsupportedClassInstance();
|
|
879
|
+
if (!hasIntrinsicPrototypeChain(obj)) throwUnsupportedClassInstance(obj);
|
|
848
880
|
if (tag === "[object Map]") {
|
|
849
881
|
const clone = /* @__PURE__ */ new Map();
|
|
850
882
|
visited.set(obj, clone);
|
|
@@ -874,7 +906,14 @@ function cloneForVo(value, visited) {
|
|
|
874
906
|
return builtInClone;
|
|
875
907
|
}
|
|
876
908
|
const prototype = Object.getPrototypeOf(obj);
|
|
877
|
-
if (prototype !== null && (!isIntrinsicConstructorPrototype(prototype, "Object") || Object.getPrototypeOf(prototype) !== null))
|
|
909
|
+
if (prototype !== null && (!isIntrinsicConstructorPrototype(prototype, "Object") || Object.getPrototypeOf(prototype) !== null)) {
|
|
910
|
+
if (isValueObjectInstance(obj)) {
|
|
911
|
+
const openKeys = openValueObjectKeys(obj);
|
|
912
|
+
if (openKeys.length > 0) throwOpenValueObjectFields(openKeys);
|
|
913
|
+
return obj;
|
|
914
|
+
}
|
|
915
|
+
throwUnsupportedClassInstance(obj);
|
|
916
|
+
}
|
|
878
917
|
const clone = Object.create(prototype === null ? null : Object.prototype);
|
|
879
918
|
visited.set(obj, clone);
|
|
880
919
|
for (const key of Reflect.ownKeys(obj)) {
|
|
@@ -891,8 +930,12 @@ function cloneForVo(value, visited) {
|
|
|
891
930
|
}
|
|
892
931
|
return clone;
|
|
893
932
|
}
|
|
894
|
-
function throwUnsupportedClassInstance() {
|
|
895
|
-
|
|
933
|
+
function throwUnsupportedClassInstance(instance) {
|
|
934
|
+
const valueObjectHint = looksLikeValueObject(instance) ? ". A value object is recognized only when a copy of this kit version built it and its props are frozen" : "";
|
|
935
|
+
throw new TypeError(`vo() cannot clone custom class instances: Value Objects are plain data${valueObjectHint}`);
|
|
936
|
+
}
|
|
937
|
+
function throwOpenValueObjectFields(keys) {
|
|
938
|
+
throw new TypeError(`vo() cannot nest a value object with own fields outside props (${keys.map(String).join(", ")}): keep the state of a value object in props`);
|
|
896
939
|
}
|
|
897
940
|
function throwUnsupportedAccessorProperty() {
|
|
898
941
|
throw new TypeError("vo() cannot clone accessor properties: Value Objects are plain data");
|
|
@@ -910,9 +953,12 @@ function isPrimitiveValue(value) {
|
|
|
910
953
|
* The input is first deep-cloned, then the clone is frozen, so calling
|
|
911
954
|
* `vo(input)` never freezes the caller's own object graph as a
|
|
912
955
|
* side-effect. Mutating the input afterwards does not bleed into the VO.
|
|
913
|
-
* Symbol-keyed properties are preserved (matching `voEquals`)
|
|
914
|
-
*
|
|
915
|
-
*
|
|
956
|
+
* Symbol-keyed properties are preserved (matching `voEquals`). A kit
|
|
957
|
+
* `ValueObject` instance nested in the input is kept by reference and
|
|
958
|
+
* frozen in place; it must keep all of its state in `props`. A value
|
|
959
|
+
* object as the input itself is rejected.
|
|
960
|
+
* Function values and every other custom class instance are rejected
|
|
961
|
+
* (Value Objects are plain data, not behaviour-bearing object graphs). Inputs must be trusted and
|
|
916
962
|
* Proxy-free: ECMAScript provides no portable way to identify a transparent
|
|
917
963
|
* Proxy without potentially executing its traps, so `vo()` is not a sandbox
|
|
918
964
|
* for hostile in-process objects. Built-ins that cannot provide immutable,
|
|
@@ -927,6 +973,7 @@ function isPrimitiveValue(value) {
|
|
|
927
973
|
* ```
|
|
928
974
|
*/
|
|
929
975
|
function vo(t) {
|
|
976
|
+
rejectValueObjectAsInput(t, "vo()");
|
|
930
977
|
return deepFreeze(cloneForVo(t, /* @__PURE__ */ new WeakMap()));
|
|
931
978
|
}
|
|
932
979
|
/**
|
|
@@ -967,6 +1014,11 @@ function voEquals(a, b) {
|
|
|
967
1014
|
* Useful for comparing value objects that contain metadata or optional fields
|
|
968
1015
|
* that should not affect equality comparison.
|
|
969
1016
|
*
|
|
1017
|
+
* The walk enters a nested `ValueObject` instance like any other object,
|
|
1018
|
+
* so inside it the path continues with `props`; `ignoreKeys: ["props"]`
|
|
1019
|
+
* empties every nested value object. The key under which the kit records
|
|
1020
|
+
* the class of the instance is never ignored.
|
|
1021
|
+
*
|
|
970
1022
|
* @param a - First value object
|
|
971
1023
|
* @param b - Second value object
|
|
972
1024
|
* @param options - Options specifying which keys to ignore during comparison
|
|
@@ -1000,7 +1052,15 @@ function voEquals(a, b) {
|
|
|
1000
1052
|
* ```
|
|
1001
1053
|
*/
|
|
1002
1054
|
function voEqualsExcept(a, b, options) {
|
|
1003
|
-
return deepEqualExcept(a, b, options);
|
|
1055
|
+
return deepEqualExcept(a, b, keepValueObjectClass(options));
|
|
1056
|
+
}
|
|
1057
|
+
function keepValueObjectClass(options) {
|
|
1058
|
+
const { ignoreKeys, ignoreKeyPredicate } = options;
|
|
1059
|
+
return {
|
|
1060
|
+
...options,
|
|
1061
|
+
ignoreKeys: ignoreKeys?.filter((key) => key !== VALUE_OBJECT_CLASS),
|
|
1062
|
+
ignoreKeyPredicate: ignoreKeyPredicate && ((key, path) => key !== VALUE_OBJECT_CLASS && ignoreKeyPredicate(key, path))
|
|
1063
|
+
};
|
|
1004
1064
|
}
|
|
1005
1065
|
/**
|
|
1006
1066
|
* Creates a value object with optional validation.
|
|
@@ -1049,7 +1109,11 @@ function describeValue(value) {
|
|
|
1049
1109
|
}
|
|
1050
1110
|
/**
|
|
1051
1111
|
* Abstract base class for creating Value Objects.
|
|
1052
|
-
* Value Objects are immutable and defined by their properties.
|
|
1112
|
+
* Value Objects are immutable and defined by their properties. A value
|
|
1113
|
+
* object can hold other value objects in its props. Every instance
|
|
1114
|
+
* records its class under an own symbol key, so `equals` compares a
|
|
1115
|
+
* nested value object by class and props and does not call its `equals`
|
|
1116
|
+
* method.
|
|
1053
1117
|
*
|
|
1054
1118
|
* @template T - The shape of the value object's properties
|
|
1055
1119
|
*/
|
|
@@ -1076,8 +1140,10 @@ var ValueObject = class {
|
|
|
1076
1140
|
* ```
|
|
1077
1141
|
*/
|
|
1078
1142
|
constructor(props) {
|
|
1143
|
+
rejectValueObjectAsInput(props, "new ValueObject()");
|
|
1079
1144
|
this.validate(props);
|
|
1080
1145
|
this.props = deepFreeze(cloneForVo(props, /* @__PURE__ */ new WeakMap()));
|
|
1146
|
+
recordValueObjectClass(this, this.constructor);
|
|
1081
1147
|
}
|
|
1082
1148
|
/**
|
|
1083
1149
|
* Optional validation hook that can be overridden by subclasses.
|
|
@@ -1621,5 +1687,5 @@ function isDispatchTrackingOutbox(outbox) {
|
|
|
1621
1687
|
}
|
|
1622
1688
|
|
|
1623
1689
|
//#endregion
|
|
1624
|
-
export {
|
|
1690
|
+
export { isBuiltInObject as A, voWithValidation as C, builtInTagWithoutInvokingAccessors as D, deepEqual as E, isWeakMap as M, findPropertyDescriptor as O, voEqualsExcept as S, deepOmit as T, stampCooperativeBrand as _, createDomainEvent as a, vo as b, createUncommittedDomainEvent as c, isUncommittedDomainEvent as d, mergeMetadata as f, hasCooperativeBrand as g, SnapshotTimeValidationError as h, copyMetadata as i, isIntrinsicConstructorPrototype as j, hasIntrinsicPrototypeChain as k, defaultDomainEventFactory as l, DomainEventValidationError as m, adoptRecordedDomainEvent as n, createDomainEventFactory as o, recordDomainEvent as p, adoptUncommittedDomainEvent as r, createDomainEventFromFacts as s, isDispatchTrackingOutbox as t, isRecordedDomainEvent as u, ValueObject as v, deepEqualExcept as w, voEquals as x, deepFreeze as y };
|
|
1625
1691
|
//# sourceMappingURL=ports.js.map
|