@shirudo/ddd-kit 0.16.0 → 1.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +157 -218
- package/dist/index.d.ts +1141 -513
- package/dist/index.js +1144 -1
- package/dist/index.js.map +1 -1
- package/dist/utils.d.ts +92 -1
- package/dist/utils.js +282 -0
- package/dist/utils.js.map +1 -1
- package/package.json +69 -65
- package/dist/deep-equal-except-C8yoSk4L.d.ts +0 -57
- package/dist/result-jCwPSjFa.d.ts +0 -352
- package/dist/result.d.ts +0 -204
- package/dist/result.js +0 -2
- package/dist/result.js.map +0 -1
- package/dist/utils-array.d.ts +0 -47
- package/dist/utils-array.js +0 -2
- package/dist/utils-array.js.map +0 -1
package/dist/utils.d.ts
CHANGED
|
@@ -1,2 +1,93 @@
|
|
|
1
|
+
type Key = string | symbol;
|
|
2
|
+
type PathSegment = string | number | symbol;
|
|
3
|
+
interface DeepOmitOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Keys to ignore everywhere in the object tree.
|
|
6
|
+
* Only applies to object properties, not Map/Set/TypedArray contents.
|
|
7
|
+
*/
|
|
8
|
+
readonly ignoreKeys?: readonly Key[];
|
|
9
|
+
/**
|
|
10
|
+
* Fine-grained control: Key + path (without current key).
|
|
11
|
+
* Example path: ["user", "meta", 0, "data"]
|
|
12
|
+
*/
|
|
13
|
+
readonly ignoreKeyPredicate?: (key: Key, path: readonly PathSegment[]) => boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Creates a deep copy of `value` with certain keys removed according to the
|
|
17
|
+
* provided rules.
|
|
18
|
+
*
|
|
19
|
+
* Walks the object tree and skips keys that match `ignoreKeys` /
|
|
20
|
+
* `ignoreKeyPredicate`. Built-in atomic types (Date, RegExp, Map, Set,
|
|
21
|
+
* TypedArrays, ArrayBuffer, DataView) are cloned by type rather than walked
|
|
22
|
+
* — their internal structure has no key filtering to apply. Cycles are
|
|
23
|
+
* preserved: a cycle `a → a` clones to `a' → a'`.
|
|
24
|
+
*
|
|
25
|
+
* **Prototype-pollution safety.** `__proto__` and `constructor` keys
|
|
26
|
+
* encountered as *own* properties of the input (typical of `JSON.parse`
|
|
27
|
+
* output) are copied as inert data properties via `Object.defineProperty`
|
|
28
|
+
* so the clone graph cannot bleed into `Object.prototype`.
|
|
29
|
+
*
|
|
30
|
+
* **Class instances.** When the input is a class instance, the clone is
|
|
31
|
+
* built via `Object.create(proto)` so the prototype is preserved, but the
|
|
32
|
+
* constructor is NOT re-invoked — so class invariants enforced by the
|
|
33
|
+
* constructor are not re-checked. `deepOmit` is therefore best used for
|
|
34
|
+
* comparison/serialisation (`voEqualsExcept`, `deepEqualExcept`), not as
|
|
35
|
+
* a general-purpose clone for behaviour-carrying objects.
|
|
36
|
+
*
|
|
37
|
+
* @param value - The value to create a deep copy from
|
|
38
|
+
* @param options - Options specifying which keys to ignore
|
|
39
|
+
* @returns A deep copy of `value` with specified keys removed
|
|
40
|
+
*/
|
|
41
|
+
declare function deepOmit<T>(value: T, options: DeepOmitOptions): T;
|
|
1
42
|
|
|
2
|
-
|
|
43
|
+
type DeepEqualExceptOptions = DeepOmitOptions;
|
|
44
|
+
/**
|
|
45
|
+
* Performs a deep equality comparison between two values after omitting specified keys.
|
|
46
|
+
*
|
|
47
|
+
* This function first removes the specified keys from both values using `deepOmit`,
|
|
48
|
+
* then performs a deep equality check using `deepEqual`.
|
|
49
|
+
*
|
|
50
|
+
* @param a - The first value to compare
|
|
51
|
+
* @param b - The second value to compare
|
|
52
|
+
* @param options - Options specifying which keys to omit before comparison
|
|
53
|
+
* @returns `true` if the values are deeply equal after omitting specified keys, `false` otherwise
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* const obj1 = { id: 1, name: "Alice", updatedAt: "2024-01-01" };
|
|
58
|
+
* const obj2 = { id: 2, name: "Alice", updatedAt: "2024-01-02" };
|
|
59
|
+
*
|
|
60
|
+
* deepEqualExcept(obj1, obj2, { ignoreKeys: ["id", "updatedAt"] }); // true
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
declare function deepEqualExcept(a: unknown, b: unknown, options: DeepEqualExceptOptions): boolean;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Performs a deep equality check between two values.
|
|
67
|
+
*
|
|
68
|
+
* This function compares values recursively, handling:
|
|
69
|
+
* - Primitives (with special handling for NaN)
|
|
70
|
+
* - Arrays (nested arrays supported)
|
|
71
|
+
* - Objects (plain objects and class instances)
|
|
72
|
+
* - TypedArrays (Uint8Array, Int32Array, etc.)
|
|
73
|
+
* - DataView
|
|
74
|
+
* - Maps and Sets
|
|
75
|
+
* - Dates and RegExp
|
|
76
|
+
* - Wrapper objects (Boolean, Number, String)
|
|
77
|
+
* - Circular references (detected and handled)
|
|
78
|
+
*
|
|
79
|
+
* @param a - The first value to compare
|
|
80
|
+
* @param b - The second value to compare
|
|
81
|
+
* @returns `true` if the values are deeply equal, `false` otherwise
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```ts
|
|
85
|
+
* deepEqual([1, 2, 3], [1, 2, 3]); // true
|
|
86
|
+
* deepEqual({ a: 1, b: [2, 3] }, { a: 1, b: [2, 3] }); // true
|
|
87
|
+
* deepEqual(NaN, NaN); // true
|
|
88
|
+
* deepEqual([1, 2], [1, 2, 3]); // false
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
declare function deepEqual(a: unknown, b: unknown): boolean;
|
|
92
|
+
|
|
93
|
+
export { type DeepEqualExceptOptions, type DeepOmitOptions, type Key, type PathSegment, deepEqual, deepEqualExcept, deepOmit };
|
package/dist/utils.js
CHANGED
|
@@ -1,2 +1,284 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// src/utils/array/is-built-in.ts
|
|
5
|
+
var BUILT_IN_TAGS = /* @__PURE__ */ new Set([
|
|
6
|
+
"[object Date]",
|
|
7
|
+
"[object RegExp]",
|
|
8
|
+
"[object Map]",
|
|
9
|
+
"[object Set]",
|
|
10
|
+
"[object WeakMap]",
|
|
11
|
+
"[object WeakSet]",
|
|
12
|
+
"[object Promise]",
|
|
13
|
+
"[object Error]",
|
|
14
|
+
"[object Boolean]",
|
|
15
|
+
"[object Number]",
|
|
16
|
+
"[object String]",
|
|
17
|
+
"[object ArrayBuffer]",
|
|
18
|
+
"[object SharedArrayBuffer]",
|
|
19
|
+
"[object DataView]"
|
|
20
|
+
]);
|
|
21
|
+
function isBuiltInObject(obj, tag) {
|
|
22
|
+
if (tag.endsWith("Array]")) return true;
|
|
23
|
+
if (ArrayBuffer.isView(obj)) return true;
|
|
24
|
+
return BUILT_IN_TAGS.has(tag);
|
|
25
|
+
}
|
|
26
|
+
__name(isBuiltInObject, "isBuiltInObject");
|
|
27
|
+
|
|
28
|
+
// src/utils/array/deep-equal.ts
|
|
29
|
+
var objProto = Object.prototype;
|
|
30
|
+
var objToString = objProto.toString;
|
|
31
|
+
var objHasOwn = objProto.hasOwnProperty;
|
|
32
|
+
function deepEqual(a, b) {
|
|
33
|
+
return deepEqualInner(a, b, /* @__PURE__ */ new WeakMap());
|
|
34
|
+
}
|
|
35
|
+
__name(deepEqual, "deepEqual");
|
|
36
|
+
function deepEqualInner(a, b, visited) {
|
|
37
|
+
if (a === b) return true;
|
|
38
|
+
const typeA = typeof a;
|
|
39
|
+
const typeB = typeof b;
|
|
40
|
+
if (typeA !== "object" || a === null || typeB !== "object" || b === null) {
|
|
41
|
+
if (typeA === "number" && typeB === "number") {
|
|
42
|
+
return Number.isNaN(a) && Number.isNaN(b);
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
const objA = a;
|
|
47
|
+
const objB = b;
|
|
48
|
+
let cachedBs = visited.get(objA);
|
|
49
|
+
if (cachedBs?.has(objB)) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
if (!cachedBs) {
|
|
53
|
+
cachedBs = /* @__PURE__ */ new WeakSet();
|
|
54
|
+
visited.set(objA, cachedBs);
|
|
55
|
+
}
|
|
56
|
+
cachedBs.add(objB);
|
|
57
|
+
if (ArrayBuffer.isView(objA) || ArrayBuffer.isView(objB)) {
|
|
58
|
+
if (!ArrayBuffer.isView(objA) || !ArrayBuffer.isView(objB)) return false;
|
|
59
|
+
const tagA2 = objToString.call(objA);
|
|
60
|
+
const tagB2 = objToString.call(objB);
|
|
61
|
+
if (tagA2 !== tagB2) return false;
|
|
62
|
+
if (tagA2 === "[object DataView]") {
|
|
63
|
+
const viewA = objA;
|
|
64
|
+
const viewB = objB;
|
|
65
|
+
if (viewA.byteLength !== viewB.byteLength) return false;
|
|
66
|
+
const len2 = viewA.byteLength;
|
|
67
|
+
for (let i = 0; i < len2; i++) {
|
|
68
|
+
if (viewA.getUint8(i) !== viewB.getUint8(i)) return false;
|
|
69
|
+
}
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
const arrA = objA;
|
|
73
|
+
const arrB = objB;
|
|
74
|
+
const len = arrA.length;
|
|
75
|
+
if (len !== arrB.length) return false;
|
|
76
|
+
for (let i = 0; i < len; i++) {
|
|
77
|
+
if (arrA[i] !== arrB[i]) return false;
|
|
78
|
+
}
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
const tagA = objToString.call(objA);
|
|
82
|
+
const tagB = objToString.call(objB);
|
|
83
|
+
if (tagA !== tagB) return false;
|
|
84
|
+
switch (tagA) {
|
|
85
|
+
case "[object Array]": {
|
|
86
|
+
const arrA = objA;
|
|
87
|
+
const arrB = objB;
|
|
88
|
+
const len = arrA.length;
|
|
89
|
+
if (len !== arrB.length) return false;
|
|
90
|
+
for (let i = 0; i < len; i++) {
|
|
91
|
+
if (!deepEqualInner(arrA[i], arrB[i], visited)) return false;
|
|
92
|
+
}
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
case "[object Map]": {
|
|
96
|
+
const mapA = objA;
|
|
97
|
+
const mapB = objB;
|
|
98
|
+
if (mapA.size !== mapB.size) return false;
|
|
99
|
+
for (const [key, valA] of mapA) {
|
|
100
|
+
if (!mapB.has(key)) return false;
|
|
101
|
+
const valB = mapB.get(key);
|
|
102
|
+
if (!deepEqualInner(valA, valB, visited)) return false;
|
|
103
|
+
}
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
case "[object Set]": {
|
|
107
|
+
const setA = objA;
|
|
108
|
+
const setB = objB;
|
|
109
|
+
if (setA.size !== setB.size) return false;
|
|
110
|
+
for (const value of setA) {
|
|
111
|
+
if (!setB.has(value)) return false;
|
|
112
|
+
}
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
case "[object Date]": {
|
|
116
|
+
const timeA = objA.getTime();
|
|
117
|
+
const timeB = objB.getTime();
|
|
118
|
+
return timeA === timeB;
|
|
119
|
+
}
|
|
120
|
+
case "[object RegExp]": {
|
|
121
|
+
const regA = objA;
|
|
122
|
+
const regB = objB;
|
|
123
|
+
return regA.source === regB.source && regA.flags === regB.flags;
|
|
124
|
+
}
|
|
125
|
+
case "[object Boolean]":
|
|
126
|
+
case "[object Number]":
|
|
127
|
+
case "[object String]": {
|
|
128
|
+
return objA.valueOf() === objB.valueOf();
|
|
129
|
+
}
|
|
130
|
+
default: {
|
|
131
|
+
if (isBuiltInObject(objA, tagA) && isBuiltInObject(objB, tagB)) {
|
|
132
|
+
return objA === objB;
|
|
133
|
+
}
|
|
134
|
+
const recA = objA;
|
|
135
|
+
const recB = objB;
|
|
136
|
+
const stringKeysA = Object.keys(objA);
|
|
137
|
+
const stringKeysB = Object.keys(objB);
|
|
138
|
+
if (stringKeysA.length !== stringKeysB.length) return false;
|
|
139
|
+
const symbolKeysA = Object.getOwnPropertySymbols(objA);
|
|
140
|
+
const symbolKeysB = Object.getOwnPropertySymbols(objB);
|
|
141
|
+
if (symbolKeysA.length !== symbolKeysB.length) return false;
|
|
142
|
+
const symbolKeysBSet = new Set(symbolKeysB);
|
|
143
|
+
for (const key of stringKeysA) {
|
|
144
|
+
if (!objHasOwn.call(objB, key)) return false;
|
|
145
|
+
}
|
|
146
|
+
for (const key of symbolKeysA) {
|
|
147
|
+
if (!symbolKeysBSet.has(key)) return false;
|
|
148
|
+
}
|
|
149
|
+
for (const key of stringKeysA) {
|
|
150
|
+
if (!deepEqualInner(recA[key], recB[key], visited)) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
for (const key of symbolKeysA) {
|
|
155
|
+
if (!deepEqualInner(recA[key], recB[key], visited)) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
__name(deepEqualInner, "deepEqualInner");
|
|
164
|
+
|
|
165
|
+
// src/utils/array/deep-omit.ts
|
|
166
|
+
function deepOmit(value, options) {
|
|
167
|
+
const visited = /* @__PURE__ */ new WeakMap();
|
|
168
|
+
const ignoreKeys = options.ignoreKeys ? new Set(options.ignoreKeys) : void 0;
|
|
169
|
+
return omitInternal(value, options, ignoreKeys, [], visited);
|
|
170
|
+
}
|
|
171
|
+
__name(deepOmit, "deepOmit");
|
|
172
|
+
function omitInternal(value, options, ignoreKeys, path, visited) {
|
|
173
|
+
if (value === null) return value;
|
|
174
|
+
if (typeof value !== "object") return value;
|
|
175
|
+
const obj = value;
|
|
176
|
+
if (visited.has(obj)) {
|
|
177
|
+
return visited.get(obj);
|
|
178
|
+
}
|
|
179
|
+
const tag = Object.prototype.toString.call(obj);
|
|
180
|
+
if (tag === "[object Array]") {
|
|
181
|
+
const arr = obj;
|
|
182
|
+
const clone2 = new Array(arr.length);
|
|
183
|
+
visited.set(obj, clone2);
|
|
184
|
+
for (let i = 0; i < arr.length; i++) {
|
|
185
|
+
path.push(i);
|
|
186
|
+
clone2[i] = omitInternal(arr[i], options, ignoreKeys, path, visited);
|
|
187
|
+
path.pop();
|
|
188
|
+
}
|
|
189
|
+
return clone2;
|
|
190
|
+
}
|
|
191
|
+
if (isBuiltInObject(obj, tag)) {
|
|
192
|
+
const builtInClone = cloneBuiltIn(obj, tag);
|
|
193
|
+
visited.set(obj, builtInClone);
|
|
194
|
+
return builtInClone;
|
|
195
|
+
}
|
|
196
|
+
const clone = Object.create(Object.getPrototypeOf(obj));
|
|
197
|
+
visited.set(obj, clone);
|
|
198
|
+
const stringKeys = Object.keys(obj);
|
|
199
|
+
const symbolKeys = Object.getOwnPropertySymbols(obj);
|
|
200
|
+
for (const key of stringKeys) {
|
|
201
|
+
if (shouldIgnoreKey(key, path, ignoreKeys, options)) continue;
|
|
202
|
+
path.push(key);
|
|
203
|
+
assignOwn(
|
|
204
|
+
clone,
|
|
205
|
+
key,
|
|
206
|
+
omitInternal(
|
|
207
|
+
obj[key],
|
|
208
|
+
options,
|
|
209
|
+
ignoreKeys,
|
|
210
|
+
path,
|
|
211
|
+
visited
|
|
212
|
+
)
|
|
213
|
+
);
|
|
214
|
+
path.pop();
|
|
215
|
+
}
|
|
216
|
+
for (const key of symbolKeys) {
|
|
217
|
+
if (shouldIgnoreKey(key, path, ignoreKeys, options)) continue;
|
|
218
|
+
path.push(key);
|
|
219
|
+
assignOwn(
|
|
220
|
+
clone,
|
|
221
|
+
key,
|
|
222
|
+
omitInternal(
|
|
223
|
+
obj[key],
|
|
224
|
+
options,
|
|
225
|
+
ignoreKeys,
|
|
226
|
+
path,
|
|
227
|
+
visited
|
|
228
|
+
)
|
|
229
|
+
);
|
|
230
|
+
path.pop();
|
|
231
|
+
}
|
|
232
|
+
return clone;
|
|
233
|
+
}
|
|
234
|
+
__name(omitInternal, "omitInternal");
|
|
235
|
+
function assignOwn(target, key, value) {
|
|
236
|
+
Object.defineProperty(target, key, {
|
|
237
|
+
value,
|
|
238
|
+
writable: true,
|
|
239
|
+
enumerable: true,
|
|
240
|
+
configurable: true
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
__name(assignOwn, "assignOwn");
|
|
244
|
+
function cloneBuiltIn(obj, tag) {
|
|
245
|
+
switch (tag) {
|
|
246
|
+
case "[object Date]":
|
|
247
|
+
return new Date(obj.getTime());
|
|
248
|
+
case "[object RegExp]": {
|
|
249
|
+
const re = obj;
|
|
250
|
+
const copy = new RegExp(re.source, re.flags);
|
|
251
|
+
copy.lastIndex = re.lastIndex;
|
|
252
|
+
return copy;
|
|
253
|
+
}
|
|
254
|
+
case "[object Map]": {
|
|
255
|
+
const m = obj;
|
|
256
|
+
return new Map(m);
|
|
257
|
+
}
|
|
258
|
+
case "[object Set]": {
|
|
259
|
+
const s = obj;
|
|
260
|
+
return new Set(s);
|
|
261
|
+
}
|
|
262
|
+
default:
|
|
263
|
+
return structuredClone(obj);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
__name(cloneBuiltIn, "cloneBuiltIn");
|
|
267
|
+
function shouldIgnoreKey(key, path, ignoreKeys, options) {
|
|
268
|
+
if (ignoreKeys?.has(key)) return true;
|
|
269
|
+
if (options.ignoreKeyPredicate?.(key, path)) return true;
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
__name(shouldIgnoreKey, "shouldIgnoreKey");
|
|
273
|
+
|
|
274
|
+
// src/utils/array/deep-equal-except.ts
|
|
275
|
+
function deepEqualExcept(a, b, options) {
|
|
276
|
+
const prunedA = deepOmit(a, options);
|
|
277
|
+
const prunedB = deepOmit(b, options);
|
|
278
|
+
return deepEqual(prunedA, prunedB);
|
|
279
|
+
}
|
|
280
|
+
__name(deepEqualExcept, "deepEqualExcept");
|
|
281
|
+
|
|
282
|
+
export { deepEqual, deepEqualExcept, deepOmit };
|
|
1
283
|
//# sourceMappingURL=utils.js.map
|
|
2
284
|
//# sourceMappingURL=utils.js.map
|
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"utils.js"}
|
|
1
|
+
{"version":3,"sources":["../src/utils/array/is-built-in.ts","../src/utils/array/deep-equal.ts","../src/utils/array/deep-omit.ts","../src/utils/array/deep-equal-except.ts"],"names":["tagA","tagB","len","clone"],"mappings":"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","file":"utils.js","sourcesContent":["/**\n * Set of `Object.prototype.toString.call(x)` tags that the library treats\n * as built-in atomic types. Members of this set are compared/cloned by\n * reference (or with type-specific logic) rather than walked structurally.\n *\n * The detection is purely tag-based — `Object.prototype.toString` reads\n * `Symbol.toStringTag`, which gives the same answer across realms (an\n * iframe's `Date` has the same tag as the main window's `Date`). The\n * previous strategy also checked `globalThis[name] === constructor` and a\n * `proto !== Object.prototype` heuristic; both broke for cross-realm\n * objects and the latter additionally misclassified ordinary user classes\n * as built-ins. Tag-only is the simplest robust choice.\n */\nconst BUILT_IN_TAGS: ReadonlySet<string> = new Set([\n\t\"[object Date]\",\n\t\"[object RegExp]\",\n\t\"[object Map]\",\n\t\"[object Set]\",\n\t\"[object WeakMap]\",\n\t\"[object WeakSet]\",\n\t\"[object Promise]\",\n\t\"[object Error]\",\n\t\"[object Boolean]\",\n\t\"[object Number]\",\n\t\"[object String]\",\n\t\"[object ArrayBuffer]\",\n\t\"[object SharedArrayBuffer]\",\n\t\"[object DataView]\",\n]);\n\n/**\n * Returns `true` when `obj` is a built-in JavaScript type that should be\n * treated atomically (compared/cloned as a unit, not walked structurally).\n * Cross-realm safe.\n *\n * @param obj - The object to classify\n * @param tag - The result of `Object.prototype.toString.call(obj)` — passed\n * in so callers that already computed it don't pay twice\n */\nexport function isBuiltInObject(obj: object, tag: string): boolean {\n\t// All TypedArrays produce tags ending in \"Array]\" — future-proof match.\n\tif (tag.endsWith(\"Array]\")) return true;\n\t// ArrayBuffer view covers DataView + all TypedArrays as a belt-and-braces.\n\tif (ArrayBuffer.isView(obj)) return true;\n\treturn BUILT_IN_TAGS.has(tag);\n}\n","import { isBuiltInObject } from \"./is-built-in\";\n\nconst objProto = Object.prototype;\nconst objToString = objProto.toString;\nconst objHasOwn = objProto.hasOwnProperty;\n\n/**\n * Performs a deep equality check between two values.\n *\n * This function compares values recursively, handling:\n * - Primitives (with special handling for NaN)\n * - Arrays (nested arrays supported)\n * - Objects (plain objects and class instances)\n * - TypedArrays (Uint8Array, Int32Array, etc.)\n * - DataView\n * - Maps and Sets\n * - Dates and RegExp\n * - Wrapper objects (Boolean, Number, String)\n * - Circular references (detected and handled)\n *\n * @param a - The first value to compare\n * @param b - The second value to compare\n * @returns `true` if the values are deeply equal, `false` otherwise\n *\n * @example\n * ```ts\n * deepEqual([1, 2, 3], [1, 2, 3]); // true\n * deepEqual({ a: 1, b: [2, 3] }, { a: 1, b: [2, 3] }); // true\n * deepEqual(NaN, NaN); // true\n * deepEqual([1, 2], [1, 2, 3]); // false\n * ```\n */\nexport function deepEqual(a: unknown, b: unknown): boolean {\n\treturn deepEqualInner(a, b, new WeakMap<object, WeakSet<object>>());\n}\n\n/**\n * Visited pair tracker for cycle detection. The cache is a pair-set:\n * for every left-hand object we keep the set of right-hand objects we\n * have already paired it with. Encountering an already-known pair returns\n * the cycle hypothesis (assume equal); a new (a, b') pair with b' ≠ any\n * previously cached b for that a is walked normally — the previous shape\n * (`WeakMap<object, object>`) could only remember one B per A, which\n * could short-circuit unrelated comparisons that happened to revisit the\n * same A with a different B.\n */\ntype VisitedPairs = WeakMap<object, WeakSet<object>>;\n\n/**\n * Internal recursive function for deep equality comparison.\n *\n * @internal\n */\nfunction deepEqualInner(\n\ta: unknown,\n\tb: unknown,\n\tvisited: VisitedPairs,\n): boolean {\n\t// 1. Fast path: reference equality\n\tif (a === b) return true;\n\n\tconst typeA = typeof a;\n\tconst typeB = typeof b;\n\n\t// 2. If one is not an object → primitive / function\n\tif (typeA !== \"object\" || a === null || typeB !== \"object\" || b === null) {\n\t\t// Special case: NaN should be equal\n\t\tif (typeA === \"number\" && typeB === \"number\") {\n\t\t\treturn Number.isNaN(a as number) && Number.isNaN(b as number);\n\t\t}\n\t\t// Everything else is directly unequal with !== (including functions)\n\t\treturn false;\n\t}\n\n\t// From here on: both are non-null objects\n\n\tconst objA = a as object;\n\tconst objB = b as object;\n\n\t// 3. Cycles: already seen this exact (a, b) pair?\n\tlet cachedBs = visited.get(objA);\n\tif (cachedBs?.has(objB)) {\n\t\t// Cycle hypothesis: pretend equal so the walk can terminate. If the\n\t\t// structure is actually unequal elsewhere, a different recursive\n\t\t// branch will surface the mismatch.\n\t\treturn true;\n\t}\n\tif (!cachedBs) {\n\t\tcachedBs = new WeakSet();\n\t\tvisited.set(objA, cachedBs);\n\t}\n\tcachedBs.add(objB);\n\n\t// 4. Handle Typed Arrays / DataView first\n\tif (ArrayBuffer.isView(objA) || ArrayBuffer.isView(objB)) {\n\t\tif (!ArrayBuffer.isView(objA) || !ArrayBuffer.isView(objB)) return false;\n\n\t\tconst tagA = objToString.call(objA);\n\t\tconst tagB = objToString.call(objB);\n\t\tif (tagA !== tagB) return false;\n\n\t\t// DataView: compare byte by byte\n\t\tif (tagA === \"[object DataView]\") {\n\t\t\tconst viewA = objA as DataView;\n\t\t\tconst viewB = objB as DataView;\n\t\t\tif (viewA.byteLength !== viewB.byteLength) return false;\n\n\t\t\tconst len = viewA.byteLength;\n\t\t\tfor (let i = 0; i < len; i++) {\n\t\t\t\tif (viewA.getUint8(i) !== viewB.getUint8(i)) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t// Typed Arrays: element by element (length + numeric index access are\n\t\t// part of the TypedArray contract; the indexed read is sound).\n\t\tconst arrA = objA as unknown as Record<number, unknown> & { length: number };\n\t\tconst arrB = objB as unknown as Record<number, unknown> & { length: number };\n\n\t\tconst len = arrA.length;\n\t\tif (len !== arrB.length) return false;\n\n\t\tfor (let i = 0; i < len; i++) {\n\t\t\tif (arrA[i] !== arrB[i]) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t// 5. Tag-based type detection (robust across realms)\n\tconst tagA = objToString.call(objA);\n\tconst tagB = objToString.call(objB);\n\tif (tagA !== tagB) return false;\n\n\tswitch (tagA) {\n\t\tcase \"[object Array]\": {\n\t\t\tconst arrA = objA as unknown[];\n\t\t\tconst arrB = objB as unknown[];\n\t\t\tconst len = arrA.length;\n\t\t\tif (len !== arrB.length) return false;\n\n\t\t\tfor (let i = 0; i < len; i++) {\n\t\t\t\tif (!deepEqualInner(arrA[i], arrB[i], visited)) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tcase \"[object Map]\": {\n\t\t\tconst mapA = objA as Map<unknown, unknown>;\n\t\t\tconst mapB = objB as Map<unknown, unknown>;\n\n\t\t\tif (mapA.size !== mapB.size) return false;\n\n\t\t\tfor (const [key, valA] of mapA) {\n\t\t\t\t// Map keys according to JS semantics: reference / SameValueZero\n\t\t\t\tif (!mapB.has(key)) return false;\n\t\t\t\tconst valB = mapB.get(key);\n\t\t\t\tif (!deepEqualInner(valA, valB, visited)) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tcase \"[object Set]\": {\n\t\t\tconst setA = objA as Set<unknown>;\n\t\t\tconst setB = objB as Set<unknown>;\n\n\t\t\tif (setA.size !== setB.size) return false;\n\n\t\t\t// Set elements: same reference (JS semantics)\n\t\t\tfor (const value of setA) {\n\t\t\t\tif (!setB.has(value)) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tcase \"[object Date]\": {\n\t\t\tconst timeA = (objA as Date).getTime();\n\t\t\tconst timeB = (objB as Date).getTime();\n\t\t\treturn timeA === timeB;\n\t\t}\n\n\t\tcase \"[object RegExp]\": {\n\t\t\tconst regA = objA as RegExp;\n\t\t\tconst regB = objB as RegExp;\n\t\t\treturn regA.source === regB.source && regA.flags === regB.flags;\n\t\t}\n\n\t\tcase \"[object Boolean]\":\n\t\tcase \"[object Number]\":\n\t\tcase \"[object String]\": {\n\t\t\t// Wrapper objects (new Boolean/Number/String)\n\t\t\treturn (objA as any).valueOf() === (objB as any).valueOf();\n\t\t}\n\n\t\tdefault: {\n\t\t\t// 6. Check if this is an unhandled built-in type (future-proof)\n\t\t\t// If both are built-ins but not handled above, they should be compared by reference\n\t\t\t// (since we don't know their internal structure)\n\t\t\tif (isBuiltInObject(objA, tagA) && isBuiltInObject(objB, tagB)) {\n\t\t\t\t// Unhandled built-in types: compare by reference as fallback\n\t\t\t\t// This ensures new built-ins don't fall through to plain object comparison\n\t\t\t\treturn objA === objB;\n\t\t\t}\n\n\t\t\t// 7. Fallback: plain / custom objects → compare own enumerable keys + values\n\t\t\tconst recA = objA as Record<string | symbol, unknown>;\n\t\t\tconst recB = objB as Record<string | symbol, unknown>;\n\n\t\t\tconst stringKeysA = Object.keys(objA as object);\n\t\t\tconst stringKeysB = Object.keys(objB as object);\n\t\t\tif (stringKeysA.length !== stringKeysB.length) return false;\n\n\t\t\tconst symbolKeysA = Object.getOwnPropertySymbols(objA as object);\n\t\t\tconst symbolKeysB = Object.getOwnPropertySymbols(objB as object);\n\t\t\tif (symbolKeysA.length !== symbolKeysB.length) return false;\n\n\t\t\t// Build the B-side symbol set once; the previous impl rebuilt the\n\t\t\t// array and ran .includes per key, which was quadratic.\n\t\t\tconst symbolKeysBSet = new Set<symbol>(symbolKeysB);\n\n\t\t\tfor (const key of stringKeysA) {\n\t\t\t\tif (!objHasOwn.call(objB, key)) return false;\n\t\t\t}\n\t\t\tfor (const key of symbolKeysA) {\n\t\t\t\tif (!symbolKeysBSet.has(key)) return false;\n\t\t\t}\n\n\t\t\tfor (const key of stringKeysA) {\n\t\t\t\tif (!deepEqualInner(recA[key], recB[key], visited)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const key of symbolKeysA) {\n\t\t\t\tif (!deepEqualInner(recA[key], recB[key], visited)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\t}\n}\n","import { isBuiltInObject } from \"./is-built-in\";\n\nexport type Key = string | symbol;\nexport type PathSegment = string | number | symbol;\n\nexport interface DeepOmitOptions {\n\t/**\n\t * Keys to ignore everywhere in the object tree.\n\t * Only applies to object properties, not Map/Set/TypedArray contents.\n\t */\n\treadonly ignoreKeys?: readonly Key[];\n\n\t/**\n\t * Fine-grained control: Key + path (without current key).\n\t * Example path: [\"user\", \"meta\", 0, \"data\"]\n\t */\n\treadonly ignoreKeyPredicate?: (\n\t\tkey: Key,\n\t\tpath: readonly PathSegment[],\n\t) => boolean;\n}\n\n/**\n * Creates a deep copy of `value` with certain keys removed according to the\n * provided rules.\n *\n * Walks the object tree and skips keys that match `ignoreKeys` /\n * `ignoreKeyPredicate`. Built-in atomic types (Date, RegExp, Map, Set,\n * TypedArrays, ArrayBuffer, DataView) are cloned by type rather than walked\n * — their internal structure has no key filtering to apply. Cycles are\n * preserved: a cycle `a → a` clones to `a' → a'`.\n *\n * **Prototype-pollution safety.** `__proto__` and `constructor` keys\n * encountered as *own* properties of the input (typical of `JSON.parse`\n * output) are copied as inert data properties via `Object.defineProperty`\n * so the clone graph cannot bleed into `Object.prototype`.\n *\n * **Class instances.** When the input is a class instance, the clone is\n * built via `Object.create(proto)` so the prototype is preserved, but the\n * constructor is NOT re-invoked — so class invariants enforced by the\n * constructor are not re-checked. `deepOmit` is therefore best used for\n * comparison/serialisation (`voEqualsExcept`, `deepEqualExcept`), not as\n * a general-purpose clone for behaviour-carrying objects.\n *\n * @param value - The value to create a deep copy from\n * @param options - Options specifying which keys to ignore\n * @returns A deep copy of `value` with specified keys removed\n */\nexport function deepOmit<T>(value: T, options: DeepOmitOptions): T {\n\tconst visited = new WeakMap<object, unknown>();\n\t// Materialise ignoreKeys as a Set once so the inner loop probes O(1).\n\tconst ignoreKeys = options.ignoreKeys\n\t\t? new Set<Key>(options.ignoreKeys)\n\t\t: undefined;\n\treturn omitInternal(value, options, ignoreKeys, [], visited) as T;\n}\n\nfunction omitInternal(\n\tvalue: unknown,\n\toptions: DeepOmitOptions,\n\tignoreKeys: ReadonlySet<Key> | undefined,\n\tpath: PathSegment[],\n\tvisited: WeakMap<object, unknown>,\n): unknown {\n\tif (value === null) return value;\n\tif (typeof value !== \"object\") return value;\n\n\tconst obj = value as object;\n\n\t// Cycles: return cached clone if already visited. Use `has` (not\n\t// `cached !== undefined`) so a legitimately-undefined cached clone\n\t// would not be misclassified as \"never seen\".\n\tif (visited.has(obj)) {\n\t\treturn visited.get(obj);\n\t}\n\n\tconst tag = Object.prototype.toString.call(obj);\n\n\t// Arrays: recursively process elements\n\tif (tag === \"[object Array]\") {\n\t\tconst arr = obj as unknown[];\n\t\tconst clone: unknown[] = new Array(arr.length);\n\t\tvisited.set(obj, clone);\n\t\tfor (let i = 0; i < arr.length; i++) {\n\t\t\tpath.push(i);\n\t\t\tclone[i] = omitInternal(arr[i], options, ignoreKeys, path, visited);\n\t\t\tpath.pop();\n\t\t}\n\t\treturn clone;\n\t}\n\n\t// Built-in atomic types: clone by type rather than walk.\n\tif (isBuiltInObject(obj, tag)) {\n\t\tconst builtInClone = cloneBuiltIn(obj, tag);\n\t\tvisited.set(obj, builtInClone);\n\t\treturn builtInClone;\n\t}\n\n\t// Plain / Custom Objects: filter keys, recursively process values.\n\tconst clone = Object.create(Object.getPrototypeOf(obj));\n\tvisited.set(obj, clone);\n\n\tconst stringKeys = Object.keys(obj);\n\tconst symbolKeys = Object.getOwnPropertySymbols(obj);\n\n\tfor (const key of stringKeys) {\n\t\tif (shouldIgnoreKey(key, path, ignoreKeys, options)) continue;\n\t\tpath.push(key);\n\t\tassignOwn(\n\t\t\tclone,\n\t\t\tkey,\n\t\t\tomitInternal(\n\t\t\t\t(obj as Record<PropertyKey, unknown>)[key],\n\t\t\t\toptions,\n\t\t\t\tignoreKeys,\n\t\t\t\tpath,\n\t\t\t\tvisited,\n\t\t\t),\n\t\t);\n\t\tpath.pop();\n\t}\n\tfor (const key of symbolKeys) {\n\t\tif (shouldIgnoreKey(key, path, ignoreKeys, options)) continue;\n\t\tpath.push(key);\n\t\tassignOwn(\n\t\t\tclone,\n\t\t\tkey,\n\t\t\tomitInternal(\n\t\t\t\t(obj as Record<PropertyKey, unknown>)[key],\n\t\t\t\toptions,\n\t\t\t\tignoreKeys,\n\t\t\t\tpath,\n\t\t\t\tvisited,\n\t\t\t),\n\t\t);\n\t\tpath.pop();\n\t}\n\n\treturn clone;\n}\n\n/**\n * Assigns `value` as an OWN data property on `target` without going through\n * any inherited setter — critically, never invokes the `__proto__` setter\n * even when `key === \"__proto__\"`. Required to defeat prototype-pollution\n * payloads that ship `__proto__` as a parsed-JSON own key.\n */\nfunction assignOwn(target: object, key: PropertyKey, value: unknown): void {\n\tObject.defineProperty(target, key, {\n\t\tvalue,\n\t\twritable: true,\n\t\tenumerable: true,\n\t\tconfigurable: true,\n\t});\n}\n\n/**\n * Clones a built-in atomic type by case. Falls back to `structuredClone`\n * for anything not explicitly enumerated (e.g. ArrayBuffer, DataView,\n * Boolean/Number/String wrappers).\n */\nfunction cloneBuiltIn(obj: object, tag: string): unknown {\n\tswitch (tag) {\n\t\tcase \"[object Date]\":\n\t\t\treturn new Date((obj as Date).getTime());\n\t\tcase \"[object RegExp]\": {\n\t\t\tconst re = obj as RegExp;\n\t\t\tconst copy = new RegExp(re.source, re.flags);\n\t\t\tcopy.lastIndex = re.lastIndex;\n\t\t\treturn copy;\n\t\t}\n\t\tcase \"[object Map]\": {\n\t\t\tconst m = obj as Map<unknown, unknown>;\n\t\t\treturn new Map(m);\n\t\t}\n\t\tcase \"[object Set]\": {\n\t\t\tconst s = obj as Set<unknown>;\n\t\t\treturn new Set(s);\n\t\t}\n\t\tdefault:\n\t\t\treturn structuredClone(obj);\n\t}\n}\n\nfunction shouldIgnoreKey(\n\tkey: Key,\n\tpath: readonly PathSegment[],\n\tignoreKeys: ReadonlySet<Key> | undefined,\n\toptions: DeepOmitOptions,\n): boolean {\n\tif (ignoreKeys?.has(key)) return true;\n\tif (options.ignoreKeyPredicate?.(key, path)) return true;\n\treturn false;\n}\n","import { deepEqual } from \"./deep-equal\";\nimport { type DeepOmitOptions, deepOmit } from \"./deep-omit\";\n\nexport type DeepEqualExceptOptions = DeepOmitOptions;\n\n/**\n * Performs a deep equality comparison between two values after omitting specified keys.\n * \n * This function first removes the specified keys from both values using `deepOmit`,\n * then performs a deep equality check using `deepEqual`.\n * \n * @param a - The first value to compare\n * @param b - The second value to compare\n * @param options - Options specifying which keys to omit before comparison\n * @returns `true` if the values are deeply equal after omitting specified keys, `false` otherwise\n * \n * @example\n * ```ts\n * const obj1 = { id: 1, name: \"Alice\", updatedAt: \"2024-01-01\" };\n * const obj2 = { id: 2, name: \"Alice\", updatedAt: \"2024-01-02\" };\n * \n * deepEqualExcept(obj1, obj2, { ignoreKeys: [\"id\", \"updatedAt\"] }); // true\n * ```\n */\nexport function deepEqualExcept(\n\ta: unknown,\n\tb: unknown,\n\toptions: DeepEqualExceptOptions,\n): boolean {\n\tconst prunedA = deepOmit(a, options);\n\tconst prunedB = deepOmit(b, options);\n\treturn deepEqual(prunedA, prunedB);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,67 +1,71 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
2
|
+
"name": "@shirudo/ddd-kit",
|
|
3
|
+
"version": "1.0.0-rc.2",
|
|
4
|
+
"description": "Composable TypeScript toolkit for tactical DDD",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/shi-rudo/ddd-kit-ts.git"
|
|
9
|
+
},
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./utils": {
|
|
18
|
+
"import": "./dist/utils.js",
|
|
19
|
+
"types": "./dist/utils.d.ts"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"keywords": [
|
|
27
|
+
"ddd",
|
|
28
|
+
"domain",
|
|
29
|
+
"typescript",
|
|
30
|
+
"toolkit"
|
|
31
|
+
],
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@biomejs/biome": "2.1.4",
|
|
38
|
+
"@shirudo/result": "^0.0.6",
|
|
39
|
+
"tsup": "^8.5.0",
|
|
40
|
+
"typedoc": "^0.28.19",
|
|
41
|
+
"typedoc-plugin-markdown": "^4.11.0",
|
|
42
|
+
"typedoc-vitepress-theme": "^1.1.2",
|
|
43
|
+
"typescript": "^5.9.2",
|
|
44
|
+
"vitepress": "^1.6.4",
|
|
45
|
+
"vitest": "^3.2.4"
|
|
46
|
+
},
|
|
47
|
+
"private": false,
|
|
48
|
+
"author": "Shirudo",
|
|
49
|
+
"contributors": [
|
|
50
|
+
"Shirudo"
|
|
51
|
+
],
|
|
52
|
+
"homepage": "https://github.com/shi-rudo/ddd-kit-ts",
|
|
53
|
+
"bugs": {
|
|
54
|
+
"url": "https://github.com/shi-rudo/ddd-kit-ts/issues"
|
|
55
|
+
},
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"@shirudo/result": "^0.0.6"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsup",
|
|
61
|
+
"test": "vitest",
|
|
62
|
+
"clean": "rm -rf dist",
|
|
63
|
+
"lint": "biome lint .",
|
|
64
|
+
"format": "pnpm exec biome format --write . && pnpm exec biome lint --write . && pnpm exec biome check --write .",
|
|
65
|
+
"typecheck": "tsc --noEmit",
|
|
66
|
+
"docs:api": "typedoc",
|
|
67
|
+
"docs:dev": "pnpm run docs:api && vitepress dev docs",
|
|
68
|
+
"docs:build": "pnpm run docs:api && vitepress build docs",
|
|
69
|
+
"docs:preview": "vitepress preview docs"
|
|
70
|
+
}
|
|
67
71
|
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
type Key = string | symbol;
|
|
2
|
-
type PathSegment = string | number | symbol;
|
|
3
|
-
interface DeepOmitOptions {
|
|
4
|
-
/**
|
|
5
|
-
* Keys to ignore everywhere in the object tree.
|
|
6
|
-
* Only applies to object properties, not Map/Set/TypedArray contents.
|
|
7
|
-
*/
|
|
8
|
-
readonly ignoreKeys?: readonly Key[];
|
|
9
|
-
/**
|
|
10
|
-
* Fine-grained control: Key + path (without current key).
|
|
11
|
-
* Example path: ["user", "meta", 0, "data"]
|
|
12
|
-
*/
|
|
13
|
-
readonly ignoreKeyPredicate?: (key: Key, path: readonly PathSegment[]) => boolean;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Creates a deep copy of `value` with certain keys removed according to the provided rules.
|
|
17
|
-
*
|
|
18
|
-
* This function recursively traverses the object tree and removes keys that match
|
|
19
|
-
* the criteria specified in `options`. Built-in types (Date, Map, Set, TypedArrays, etc.)
|
|
20
|
-
* are treated atomically and not modified.
|
|
21
|
-
*
|
|
22
|
-
* @param value - The value to create a deep copy from
|
|
23
|
-
* @param options - Options specifying which keys to ignore
|
|
24
|
-
* @returns A deep copy of `value` with specified keys removed
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```ts
|
|
28
|
-
* const obj = { a: 1, b: { c: 2, d: 3 } };
|
|
29
|
-
* const result = deepOmit(obj, { ignoreKeys: ['d'] });
|
|
30
|
-
* // result: { a: 1, b: { c: 2 } }
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
declare function deepOmit<T>(value: T, options: DeepOmitOptions): T;
|
|
34
|
-
|
|
35
|
-
type DeepEqualExceptOptions = DeepOmitOptions;
|
|
36
|
-
/**
|
|
37
|
-
* Performs a deep equality comparison between two values after omitting specified keys.
|
|
38
|
-
*
|
|
39
|
-
* This function first removes the specified keys from both values using `deepOmit`,
|
|
40
|
-
* then performs a deep equality check using `deepEqual`.
|
|
41
|
-
*
|
|
42
|
-
* @param a - The first value to compare
|
|
43
|
-
* @param b - The second value to compare
|
|
44
|
-
* @param options - Options specifying which keys to omit before comparison
|
|
45
|
-
* @returns `true` if the values are deeply equal after omitting specified keys, `false` otherwise
|
|
46
|
-
*
|
|
47
|
-
* @example
|
|
48
|
-
* ```ts
|
|
49
|
-
* const obj1 = { id: 1, name: "Alice", updatedAt: "2024-01-01" };
|
|
50
|
-
* const obj2 = { id: 2, name: "Alice", updatedAt: "2024-01-02" };
|
|
51
|
-
*
|
|
52
|
-
* deepEqualExcept(obj1, obj2, { ignoreKeys: ["id", "updatedAt"] }); // true
|
|
53
|
-
* ```
|
|
54
|
-
*/
|
|
55
|
-
declare function deepEqualExcept(a: unknown, b: unknown, options: DeepEqualExceptOptions): boolean;
|
|
56
|
-
|
|
57
|
-
export { type DeepOmitOptions as D, type Key as K, type PathSegment as P, type DeepEqualExceptOptions as a, deepEqualExcept as b, deepOmit as d };
|