objwalk 1.5.0 → 1.7.0

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/index.cjs CHANGED
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  clone: () => clone,
24
+ diff: () => diff,
24
25
  each: () => each,
25
26
  equally: () => equally,
26
27
  extract: () => extract,
@@ -34,6 +35,38 @@ __export(index_exports, {
34
35
  });
35
36
  module.exports = __toCommonJS(index_exports);
36
37
 
38
+ // src/helpers/createPath.ts
39
+ var createPath = (parentPath, key, parentIsArray) => {
40
+ if (parentIsArray) {
41
+ return parentPath + "[" + key + "]";
42
+ }
43
+ return parentPath ? parentPath + "." + key : String(key);
44
+ };
45
+
46
+ // src/helpers/isWalkable.ts
47
+ var isWalkable = (value) => {
48
+ return value !== null && typeof value === "object";
49
+ };
50
+
51
+ // src/each.ts
52
+ var each = (data, callback, { root = false, descend } = {}) => {
53
+ const walk = (value, path, segments, key) => {
54
+ const proceed = segments.length > 0 || root ? callback(key, value, path, [...segments]) !== false : true;
55
+ if (proceed && isWalkable(value) && (descend === void 0 || descend({ key, value, path, segments: [...segments] }))) {
56
+ if (Array.isArray(value)) {
57
+ return value.every(
58
+ (item, index) => walk(item, createPath(path, index, true), [...segments, index], index)
59
+ );
60
+ }
61
+ return Object.keys(value).every(
62
+ (childKey) => walk(value[childKey], createPath(path, childKey, false), [...segments, childKey], childKey)
63
+ );
64
+ }
65
+ return proceed;
66
+ };
67
+ walk(data, "", [], void 0);
68
+ };
69
+
37
70
  // src/helpers/getValueByKey.ts
38
71
  var getValueByKey = (object, key, { inherited = true, ignore = [] } = {}) => {
39
72
  if (object === null || object === void 0 || ignore.includes(key) || !inherited && !Object.hasOwn(object, key)) {
@@ -85,51 +118,6 @@ var parsePathSelection = (path = "") => {
85
118
  };
86
119
  };
87
120
 
88
- // src/helpers/createPath.ts
89
- var createPath = (parentPath, key, parentIsArray) => {
90
- if (parentIsArray) {
91
- return parentPath + "[" + key + "]";
92
- }
93
- return parentPath ? parentPath + "." + key : String(key);
94
- };
95
-
96
- // src/helpers/isWalkable.ts
97
- var isWalkable = (value) => {
98
- return value !== null && typeof value === "object";
99
- };
100
-
101
- // src/each.ts
102
- var each = (data, callback) => {
103
- const walk = (value, path, key, parentIsArray) => {
104
- const currentPath = path || createPath("", key, parentIsArray);
105
- callback(key, value, currentPath);
106
- if (!isWalkable(value)) {
107
- return;
108
- }
109
- if (Array.isArray(value)) {
110
- value.forEach((item, index) => {
111
- walk(item, createPath(currentPath, index, true), index, true);
112
- });
113
- return;
114
- }
115
- Object.entries(value).forEach(([childKey, item]) => {
116
- walk(item, createPath(currentPath, childKey, false), childKey, false);
117
- });
118
- };
119
- if (!isWalkable(data)) {
120
- return;
121
- }
122
- if (Array.isArray(data)) {
123
- data.forEach((item, index) => {
124
- walk(item, createPath("", index, true), index, true);
125
- });
126
- return;
127
- }
128
- Object.entries(data).forEach(([key, value]) => {
129
- walk(value, createPath("", key, false), key, false);
130
- });
131
- };
132
-
133
121
  // src/pick.ts
134
122
  var pick = (data = {}, path = "", options = {}) => {
135
123
  const { cleanPath, keys } = parsePathSelection(path);
@@ -223,13 +211,13 @@ var extract = (source, mapping) => {
223
211
  return;
224
212
  }
225
213
  if (Array.isArray(sourcePath)) {
226
- for (const item of sourcePath) {
214
+ each(sourcePath, (_key, item) => {
227
215
  const value = pick(source, item);
228
216
  if (value !== void 0) {
229
217
  set(result, destinationPath, value);
230
- break;
218
+ return false;
231
219
  }
232
- }
220
+ });
233
221
  return;
234
222
  }
235
223
  set(result, destinationPath, pick(source, sourcePath));
@@ -245,114 +233,144 @@ var map = (data, callback, store = {}) => {
245
233
  return store;
246
234
  };
247
235
 
248
- // src/merge.ts
249
- var unsafeKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
236
+ // src/helpers/isPlainObject.ts
250
237
  var isPlainObject = (value) => {
251
- if (value === null || typeof value !== "object") {
252
- return false;
238
+ if (value !== null && typeof value === "object") {
239
+ const prototype = Object.getPrototypeOf(value);
240
+ return prototype === Object.prototype || prototype === null;
253
241
  }
254
- const prototype = Object.getPrototypeOf(value);
255
- return prototype === Object.prototype || prototype === null;
242
+ return false;
256
243
  };
244
+
245
+ // src/merge.ts
246
+ var unsafeKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
257
247
  var merge = (target, ...sources) => {
258
248
  const targetRecord = target;
259
- for (const source of sources) {
260
- if (!isPlainObject(source)) {
261
- continue;
262
- }
263
- for (const [key, sourceValue] of Object.entries(source)) {
264
- if (unsafeKeys.has(key)) {
265
- continue;
266
- }
267
- const targetValue = targetRecord[key];
268
- if (isPlainObject(targetValue) && isPlainObject(sourceValue)) {
269
- merge(targetValue, sourceValue);
270
- } else {
271
- targetRecord[key] = sourceValue;
272
- }
249
+ each(sources, (_index, source) => {
250
+ if (isPlainObject(source)) {
251
+ each(Object.entries(source), (_index2, [key, sourceValue]) => {
252
+ if (unsafeKeys.has(key) === false) {
253
+ const targetValue = targetRecord[key];
254
+ if (isPlainObject(targetValue) && isPlainObject(sourceValue)) {
255
+ merge(targetValue, sourceValue);
256
+ } else {
257
+ targetRecord[key] = sourceValue;
258
+ }
259
+ }
260
+ }, { descend: ({ segments }) => segments.length === 0 });
273
261
  }
274
- }
262
+ }, { descend: ({ segments }) => segments.length === 0 });
275
263
  return target;
276
264
  };
277
265
 
278
266
  // src/flush.ts
279
267
  var flush = (data = {}, path) => {
280
- if (path.length > 0) {
281
- const keys = path.split(".");
282
- const lastKey = keys.pop() ?? "";
283
- const match = lastKey.match(/^(\w+)\[(\d+)\]$/);
284
- let parent = getValueByPath(data, keys.join("."), { inherited: false });
285
- let key = lastKey;
286
- if (match) {
287
- const [, arrayKey, index] = match;
288
- const array = getValueByKey(parent, arrayKey, { inherited: false });
289
- parent = Array.isArray(array) ? array : void 0;
290
- key = index;
291
- }
292
- if (parent !== null && typeof parent === "object" && Object.hasOwn(parent, key)) {
293
- delete parent[key];
268
+ const paths = Array.isArray(path) ? path : [path];
269
+ each(paths, (_key, path2) => {
270
+ if (path2.length > 0) {
271
+ const keys = path2.split(".");
272
+ const lastKey = keys.pop() ?? "";
273
+ const match = lastKey.match(/^(\w+)\[(\d+)\]$/);
274
+ let parent = getValueByPath(data, keys.join("."), { inherited: false });
275
+ let key = lastKey;
276
+ if (match) {
277
+ const [, arrayKey, index] = match;
278
+ const array = getValueByKey(parent, arrayKey, { inherited: false });
279
+ parent = Array.isArray(array) ? array : void 0;
280
+ key = index;
281
+ }
282
+ if (parent !== null && typeof parent === "object" && Object.hasOwn(parent, key)) {
283
+ delete parent[key];
284
+ }
294
285
  }
295
- }
286
+ });
296
287
  return data;
297
288
  };
298
289
 
299
290
  // src/deepEqual.ts
300
291
  var deepEqual = (x, y, store) => {
301
- if (Number.isNaN(x) && Number.isNaN(y)) {
292
+ if (x === y || Number.isNaN(x) && Number.isNaN(y)) {
302
293
  return true;
303
294
  }
304
- if (x === y) {
305
- return true;
306
- }
307
- if (x == null || y == null) {
308
- return false;
309
- }
310
- if (typeof x !== "object" || typeof y !== "object") {
311
- return false;
312
- }
313
- let seen = store.get(x);
314
- if (seen !== void 0 && seen.has(y)) {
315
- return true;
316
- }
317
- if (seen === void 0) {
318
- seen = /* @__PURE__ */ new WeakSet();
319
- store.set(x, seen);
320
- }
321
- seen.add(y);
322
- if (x instanceof Date && y instanceof Date) {
323
- return x.getTime() === y.getTime();
324
- }
325
- if (x instanceof RegExp && y instanceof RegExp) {
326
- return x.toString() === y.toString();
327
- }
328
- const isArrayX = Array.isArray(x);
329
- const isArrayY = Array.isArray(y);
330
- if (isArrayX !== isArrayY) {
331
- return false;
332
- }
333
- if (isArrayX) {
334
- return equalArrays(x, y, store);
295
+ if (x !== null && y !== null && typeof x === "object" && typeof y === "object") {
296
+ let seen = store.get(x);
297
+ if (seen !== void 0 && seen.has(y)) {
298
+ return true;
299
+ }
300
+ if (seen === void 0) {
301
+ seen = /* @__PURE__ */ new WeakSet();
302
+ store.set(x, seen);
303
+ }
304
+ seen.add(y);
305
+ if (x instanceof Date || y instanceof Date) {
306
+ return x instanceof Date && y instanceof Date && x.getTime() === y.getTime();
307
+ }
308
+ if (x instanceof RegExp || y instanceof RegExp) {
309
+ return x instanceof RegExp && y instanceof RegExp && x.toString() === y.toString();
310
+ }
311
+ if (Array.isArray(x) && Array.isArray(y)) {
312
+ const { length } = x;
313
+ let equal = length === y.length;
314
+ let index = 0;
315
+ while (equal && index < length) {
316
+ equal = deepEqual(x[index], y[index], store);
317
+ index += 1;
318
+ }
319
+ return equal;
320
+ } else if (Array.isArray(x) === false && Array.isArray(y) === false) {
321
+ const keysX = Reflect.ownKeys(x);
322
+ const keysY = Reflect.ownKeys(y);
323
+ return keysX.length === keysY.length && keysX.every(
324
+ (key) => Object.hasOwn(y, key) && deepEqual(x[key], y[key], store)
325
+ );
326
+ }
335
327
  }
336
- return equalObjects(x, y, store);
328
+ return false;
337
329
  };
338
- var equalArrays = (x, y, store) => x.length === y.length && x.every((value, index) => deepEqual(value, y[index], store));
339
- var equalObjects = (x, y, store) => {
340
- const keysX = Reflect.ownKeys(x);
341
- const keysY = Reflect.ownKeys(y);
342
- if (keysX.length !== keysY.length) {
343
- return false;
330
+
331
+ // src/equally.ts
332
+ var equally = (a, b, paths) => {
333
+ if (paths !== void 0) {
334
+ return paths.every((path) => equally(getValueByPath(a, path), getValueByPath(b, path)));
344
335
  }
345
- return keysX.every((key) => {
346
- if (!Object.hasOwn(y, key)) {
347
- return false;
336
+ return deepEqual(a, b, /* @__PURE__ */ new WeakMap());
337
+ };
338
+
339
+ // src/diff.ts
340
+ var diff = (first, last) => {
341
+ const operations = [];
342
+ const frames = [];
343
+ const finish = (depth) => {
344
+ each(frames.splice(depth).reverse(), (_index, { first: first2, last: last2, path }) => {
345
+ each(Object.keys(first2), (_index2, key) => {
346
+ if (Object.prototype.propertyIsEnumerable.call(last2, key) === false) {
347
+ operations.push({ type: "delete", path: [...path, key] });
348
+ }
349
+ });
350
+ }, { descend: ({ segments }) => segments.length === 0 });
351
+ return frames.at(-1);
352
+ };
353
+ let descend = false;
354
+ each(last, (key, value, _path, segments) => {
355
+ const path = segments.map(String);
356
+ const frame = finish(segments.length);
357
+ const present = key === void 0 || frame !== void 0 && Object.prototype.propertyIsEnumerable.call(frame.first, key);
358
+ const previous = key === void 0 ? first : present ? frame?.first[key] : void 0;
359
+ descend = false;
360
+ if (present && isPlainObject(previous) && isPlainObject(value)) {
361
+ frames.push({ first: previous, last: value, path });
362
+ descend = true;
363
+ } else if (present === false || equally(previous, value) === false) {
364
+ operations.push({ type: "set", path, value });
348
365
  }
349
- return deepEqual(x[key], y[key], store);
366
+ }, {
367
+ root: true,
368
+ descend: () => descend
350
369
  });
370
+ finish(0);
371
+ return operations;
351
372
  };
352
373
 
353
- // src/equally.ts
354
- var equally = (a, b) => deepEqual(a, b, /* @__PURE__ */ new WeakMap());
355
-
356
374
  // src/clone.ts
357
375
  var clone = (value) => {
358
376
  const store = /* @__PURE__ */ new WeakMap();
@@ -380,9 +398,9 @@ var clone = (value) => {
380
398
  }
381
399
  const result = {};
382
400
  store.set(obj, result);
383
- for (const key of Reflect.ownKeys(obj)) {
401
+ each(Reflect.ownKeys(obj), (_index, key) => {
384
402
  result[key] = cloneValue(obj[key]);
385
- }
403
+ });
386
404
  return result;
387
405
  };
388
406
  return cloneValue(value);
@@ -423,6 +441,7 @@ var stringify = (value, { fallback, onError, replacer, space } = {}) => {
423
441
  // Annotate the CommonJS export names for ESM import in node:
424
442
  0 && (module.exports = {
425
443
  clone,
444
+ diff,
426
445
  each,
427
446
  equally,
428
447
  extract,
package/dist/index.d.cts CHANGED
@@ -1,4 +1,12 @@
1
1
  type WalkKey = string | number;
2
+ type DiffOperation = {
3
+ type: 'set';
4
+ path: string[];
5
+ value: unknown;
6
+ } | {
7
+ type: 'delete';
8
+ path: string[];
9
+ };
2
10
  type PickOptions = {
3
11
  inherited?: boolean;
4
12
  ignore?: readonly string[];
@@ -15,8 +23,24 @@ type StringifyOptions = {
15
23
  space?: string | number;
16
24
  };
17
25
 
18
- type EachCallback = (key: WalkKey, value: any, path: string) => void;
19
- declare const each: (data: any, callback: EachCallback) => void;
26
+ type EachCallback<TKey extends WalkKey | undefined = WalkKey> = (key: TKey, value: any, path: string, segments: WalkKey[]) => unknown;
27
+ type EachEntry = {
28
+ key: WalkKey | undefined;
29
+ value: any;
30
+ path: string;
31
+ segments: WalkKey[];
32
+ };
33
+ type EachOptions = {
34
+ root?: boolean;
35
+ descend?: (entry: EachEntry) => boolean;
36
+ };
37
+ type Each = {
38
+ (data: any, callback: EachCallback, options?: EachOptions & {
39
+ root?: false;
40
+ }): void;
41
+ (data: any, callback: EachCallback<WalkKey | undefined>, options: EachOptions): void;
42
+ };
43
+ declare const each: Each;
20
44
 
21
45
  type ObjwalkObject$1 = Record<string, any>;
22
46
  declare const pick: (data?: ObjwalkObject$1, path?: string, options?: PickOptions) => any;
@@ -32,9 +56,11 @@ declare const merge: <T extends ObjwalkObject$1>(target: T, ...sources: unknown[
32
56
  type ObjwalkObject = Record<string, any>;
33
57
  declare const set: (data: ObjwalkObject | undefined, path: string, value: any) => ObjwalkObject;
34
58
 
35
- declare const flush: (data: ObjwalkObject$1 | undefined, path: string) => ObjwalkObject$1;
59
+ declare const flush: (data: ObjwalkObject$1 | undefined, path: string | string[]) => ObjwalkObject$1;
60
+
61
+ declare const equally: (a: unknown, b: unknown, paths?: string[]) => boolean;
36
62
 
37
- declare const equally: (a: unknown, b: unknown) => boolean;
63
+ declare const diff: (first: unknown, last: unknown) => DiffOperation[];
38
64
 
39
65
  declare const clone: <T>(value: T) => T;
40
66
 
@@ -42,4 +68,4 @@ declare const parse: (raw: unknown, { fallback, onError, reviver }?: ParseOption
42
68
 
43
69
  declare const stringify: (value: unknown, { fallback, onError, replacer, space }?: StringifyOptions) => string | undefined;
44
70
 
45
- export { type EachCallback, type ExtractMapping, type MapCallback, type ObjwalkObject$1 as ObjwalkObject, type ParseOptions, type PickOptions, type StringifyOptions, type WalkKey, clone, each, equally, extract, flush, map, merge, parse, pick, set, stringify };
71
+ export { type DiffOperation, type EachCallback, type EachEntry, type EachOptions, type ExtractMapping, type MapCallback, type ObjwalkObject$1 as ObjwalkObject, type ParseOptions, type PickOptions, type StringifyOptions, type WalkKey, clone, diff, each, equally, extract, flush, map, merge, parse, pick, set, stringify };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,12 @@
1
1
  type WalkKey = string | number;
2
+ type DiffOperation = {
3
+ type: 'set';
4
+ path: string[];
5
+ value: unknown;
6
+ } | {
7
+ type: 'delete';
8
+ path: string[];
9
+ };
2
10
  type PickOptions = {
3
11
  inherited?: boolean;
4
12
  ignore?: readonly string[];
@@ -15,8 +23,24 @@ type StringifyOptions = {
15
23
  space?: string | number;
16
24
  };
17
25
 
18
- type EachCallback = (key: WalkKey, value: any, path: string) => void;
19
- declare const each: (data: any, callback: EachCallback) => void;
26
+ type EachCallback<TKey extends WalkKey | undefined = WalkKey> = (key: TKey, value: any, path: string, segments: WalkKey[]) => unknown;
27
+ type EachEntry = {
28
+ key: WalkKey | undefined;
29
+ value: any;
30
+ path: string;
31
+ segments: WalkKey[];
32
+ };
33
+ type EachOptions = {
34
+ root?: boolean;
35
+ descend?: (entry: EachEntry) => boolean;
36
+ };
37
+ type Each = {
38
+ (data: any, callback: EachCallback, options?: EachOptions & {
39
+ root?: false;
40
+ }): void;
41
+ (data: any, callback: EachCallback<WalkKey | undefined>, options: EachOptions): void;
42
+ };
43
+ declare const each: Each;
20
44
 
21
45
  type ObjwalkObject$1 = Record<string, any>;
22
46
  declare const pick: (data?: ObjwalkObject$1, path?: string, options?: PickOptions) => any;
@@ -32,9 +56,11 @@ declare const merge: <T extends ObjwalkObject$1>(target: T, ...sources: unknown[
32
56
  type ObjwalkObject = Record<string, any>;
33
57
  declare const set: (data: ObjwalkObject | undefined, path: string, value: any) => ObjwalkObject;
34
58
 
35
- declare const flush: (data: ObjwalkObject$1 | undefined, path: string) => ObjwalkObject$1;
59
+ declare const flush: (data: ObjwalkObject$1 | undefined, path: string | string[]) => ObjwalkObject$1;
60
+
61
+ declare const equally: (a: unknown, b: unknown, paths?: string[]) => boolean;
36
62
 
37
- declare const equally: (a: unknown, b: unknown) => boolean;
63
+ declare const diff: (first: unknown, last: unknown) => DiffOperation[];
38
64
 
39
65
  declare const clone: <T>(value: T) => T;
40
66
 
@@ -42,4 +68,4 @@ declare const parse: (raw: unknown, { fallback, onError, reviver }?: ParseOption
42
68
 
43
69
  declare const stringify: (value: unknown, { fallback, onError, replacer, space }?: StringifyOptions) => string | undefined;
44
70
 
45
- export { type EachCallback, type ExtractMapping, type MapCallback, type ObjwalkObject$1 as ObjwalkObject, type ParseOptions, type PickOptions, type StringifyOptions, type WalkKey, clone, each, equally, extract, flush, map, merge, parse, pick, set, stringify };
71
+ export { type DiffOperation, type EachCallback, type EachEntry, type EachOptions, type ExtractMapping, type MapCallback, type ObjwalkObject$1 as ObjwalkObject, type ParseOptions, type PickOptions, type StringifyOptions, type WalkKey, clone, diff, each, equally, extract, flush, map, merge, parse, pick, set, stringify };