@domql/utils 3.2.3 → 3.2.10

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.
Files changed (53) hide show
  1. package/array.js +11 -5
  2. package/cache.js +3 -0
  3. package/component.js +3 -4
  4. package/dist/cjs/array.js +11 -5
  5. package/dist/cjs/component.js +4 -6
  6. package/dist/cjs/element.js +5 -5
  7. package/dist/cjs/extends.js +43 -27
  8. package/dist/cjs/function.js +3 -3
  9. package/dist/cjs/index.js +1 -0
  10. package/dist/cjs/key.js +2 -2
  11. package/dist/cjs/keys.js +30 -16
  12. package/dist/cjs/methods.js +64 -28
  13. package/dist/cjs/object.js +141 -125
  14. package/dist/cjs/props.js +41 -32
  15. package/dist/cjs/scope.js +1 -2
  16. package/dist/cjs/state.js +9 -8
  17. package/dist/cjs/string.js +15 -20
  18. package/dist/cjs/tags.js +69 -4
  19. package/dist/cjs/triggerEvent.js +90 -0
  20. package/dist/cjs/types.js +4 -12
  21. package/dist/esm/array.js +11 -5
  22. package/dist/esm/component.js +4 -6
  23. package/dist/esm/element.js +8 -26
  24. package/dist/esm/extends.js +47 -49
  25. package/dist/esm/function.js +3 -3
  26. package/dist/esm/index.js +1 -0
  27. package/dist/esm/key.js +2 -2
  28. package/dist/esm/keys.js +30 -16
  29. package/dist/esm/methods.js +63 -42
  30. package/dist/esm/object.js +145 -149
  31. package/dist/esm/props.js +41 -48
  32. package/dist/esm/scope.js +1 -2
  33. package/dist/esm/state.js +17 -34
  34. package/dist/esm/string.js +15 -20
  35. package/dist/esm/tags.js +69 -4
  36. package/dist/esm/triggerEvent.js +70 -0
  37. package/dist/esm/types.js +4 -12
  38. package/dist/iife/index.js +2779 -0
  39. package/element.js +2 -2
  40. package/extends.js +28 -17
  41. package/function.js +4 -6
  42. package/index.js +1 -0
  43. package/keys.js +26 -16
  44. package/methods.js +63 -18
  45. package/object.js +142 -200
  46. package/package.json +33 -12
  47. package/props.js +42 -25
  48. package/state.js +7 -7
  49. package/string.js +20 -38
  50. package/tags.js +43 -4
  51. package/triggerEvent.js +76 -0
  52. package/types.js +4 -23
  53. package/dist/cjs/package.json +0 -4
package/object.js CHANGED
@@ -7,9 +7,7 @@ import {
7
7
  isObject,
8
8
  isArray,
9
9
  isString,
10
- is,
11
- isUndefined,
12
- isNull
10
+ is
13
11
  } from './types.js'
14
12
  import { unstackArrayOfObjects } from './array.js'
15
13
  import { stringIncludesAny } from './string.js'
@@ -18,8 +16,11 @@ import { METHODS_EXL } from './keys.js'
18
16
 
19
17
  const ENV = process.env.NODE_ENV
20
18
 
19
+ const _startsWithDunder = e => e.charCodeAt(0) === 95 && e.charCodeAt(1) === 95
20
+
21
21
  export const exec = (param, element, state, context) => {
22
22
  if (isFunction(param)) {
23
+ if (!element) return
23
24
  const result = param.call(
24
25
  element,
25
26
  element,
@@ -45,26 +46,24 @@ export const map = (obj, extention, element) => {
45
46
  }
46
47
 
47
48
  export const merge = (element, obj, excludeFrom = []) => {
49
+ const useSet = excludeFrom instanceof Set
48
50
  for (const e in obj) {
49
- const hasOwnProperty = Object.prototype.hasOwnProperty.call(obj, e)
50
- if (!hasOwnProperty || excludeFrom.includes(e) || e.startsWith('__')) {
51
- continue
52
- }
53
- const elementProp = element[e]
54
- const objProp = obj[e]
55
- if (elementProp === undefined) {
56
- element[e] = objProp
51
+ if (!Object.prototype.hasOwnProperty.call(obj, e)) continue
52
+ if (_startsWithDunder(e)) continue
53
+ if (useSet ? excludeFrom.has(e) : excludeFrom.includes(e)) continue
54
+ if (element[e] === undefined) {
55
+ element[e] = obj[e]
57
56
  }
58
57
  }
59
58
  return element
60
59
  }
61
60
 
62
61
  export const deepMerge = (element, extend, excludeFrom = METHODS_EXL) => {
62
+ const useSet = excludeFrom instanceof Set
63
63
  for (const e in extend) {
64
- const hasOwnProperty = Object.prototype.hasOwnProperty.call(extend, e)
65
- if (!hasOwnProperty || excludeFrom.includes(e) || e.startsWith('__')) {
66
- continue
67
- }
64
+ if (!Object.prototype.hasOwnProperty.call(extend, e)) continue
65
+ if (_startsWithDunder(e)) continue
66
+ if (useSet ? excludeFrom.has(e) : excludeFrom.includes(e)) continue
68
67
  const elementProp = element[e]
69
68
  const extendProp = extend[e]
70
69
  if (isObjectLike(elementProp) && isObjectLike(extendProp)) {
@@ -77,16 +76,12 @@ export const deepMerge = (element, extend, excludeFrom = METHODS_EXL) => {
77
76
  }
78
77
 
79
78
  export const clone = (obj, excludeFrom = []) => {
79
+ const useSet = excludeFrom instanceof Set
80
80
  const o = {}
81
81
  for (const prop in obj) {
82
- const hasOwnProperty = Object.prototype.hasOwnProperty.call(obj, prop)
83
- if (
84
- !hasOwnProperty ||
85
- excludeFrom.includes(prop) ||
86
- prop.startsWith('__')
87
- ) {
88
- continue
89
- }
82
+ if (!Object.prototype.hasOwnProperty.call(obj, prop)) continue
83
+ if (_startsWithDunder(prop)) continue
84
+ if (useSet ? excludeFrom.has(prop) : excludeFrom.includes(prop)) continue
90
85
  o[prop] = obj[prop]
91
86
  }
92
87
  return o
@@ -127,35 +122,28 @@ export const deepClone = (obj, options = {}) => {
127
122
  }
128
123
 
129
124
  // Create appropriate container based on type and window context
130
- const clone = contentWindow
131
- ? isArray(obj)
132
- ? new contentWindow.Array()
133
- : new contentWindow.Object()
134
- : isArray(obj)
135
- ? []
136
- : {}
125
+ const isArr = isArray(obj)
126
+ const clone = isArr ? [] : {}
137
127
 
138
128
  // Store the clone to handle circular references
139
129
  visited.set(obj, clone)
140
130
 
131
+ // Convert exclude to Set for O(1) lookups when list is non-trivial
132
+ const excludeSet = exclude instanceof Set ? exclude : (exclude.length > 3 ? new Set(exclude) : null)
133
+
141
134
  // Clone properties
142
135
  for (const key in obj) {
143
136
  if (!Object.prototype.hasOwnProperty.call(obj, key)) continue
144
137
 
145
138
  // Skip excluded properties
146
- if (exclude.includes(key) || key.startsWith('__') || key === '__proto__') {
147
- continue
148
- }
139
+ if (_startsWithDunder(key) || key === '__proto__') continue
140
+ if (excludeSet ? excludeSet.has(key) : exclude.includes(key)) continue
149
141
 
150
142
  const value = obj[key]
151
143
 
152
144
  // Skip based on cleanup options
153
- if (
154
- (cleanUndefined && isUndefined(value)) ||
155
- (cleanNull && isNull(value))
156
- ) {
157
- continue
158
- }
145
+ if (cleanUndefined && value === undefined) continue
146
+ if (cleanNull && value === null) continue
159
147
 
160
148
  // Handle special cases
161
149
  if (isDOMNode(value)) {
@@ -209,17 +197,18 @@ export const deepStringify = (obj, stringified = {}) => {
209
197
  stringified[prop] = {}
210
198
  deepStringify(objProp, stringified[prop])
211
199
  } else if (isArray(objProp)) {
212
- stringified[prop] = []
213
- objProp.forEach((v, i) => {
200
+ const arr = stringified[prop] = []
201
+ for (let i = 0; i < objProp.length; i++) {
202
+ const v = objProp[i]
214
203
  if (isObject(v)) {
215
- stringified[prop][i] = {}
216
- deepStringify(v, stringified[prop][i])
204
+ arr[i] = {}
205
+ deepStringify(v, arr[i])
217
206
  } else if (isFunction(v)) {
218
- stringified[prop][i] = v.toString()
207
+ arr[i] = v.toString()
219
208
  } else {
220
- stringified[prop][i] = v
209
+ arr[i] = v
221
210
  }
222
- })
211
+ }
223
212
  } else {
224
213
  stringified[prop] = objProp
225
214
  }
@@ -227,38 +216,32 @@ export const deepStringify = (obj, stringified = {}) => {
227
216
  return stringified
228
217
  }
229
218
 
219
+ const OBJ_TO_STR_SPECIAL_CHARS = new Set([
220
+ '&', '*', '-', ':', '%', '{', '}', '>', '<', '@', '.', '/', '!', ' '
221
+ ])
222
+
230
223
  export const objectToString = (obj = {}, indent = 0) => {
231
224
  // Handle empty object case
232
225
  if (obj === null || typeof obj !== 'object') {
233
226
  return String(obj)
234
227
  }
235
228
 
236
- // Handle empty object case
237
- if (Object.keys(obj).length === 0) {
238
- return '{}'
239
- }
229
+ // Handle empty object case - avoid Object.keys allocation
230
+ let hasKeys = false
231
+ for (const _k in obj) { hasKeys = true; break } // eslint-disable-line
232
+ if (!hasKeys) return '{}'
240
233
 
241
234
  const spaces = ' '.repeat(indent)
242
235
  let str = '{\n'
243
236
 
244
- for (const [key, value] of Object.entries(obj)) {
245
- const keyNotAllowdChars = stringIncludesAny(key, [
246
- '&',
247
- '*',
248
- '-',
249
- ':',
250
- '%',
251
- '{',
252
- '}',
253
- '>',
254
- '<',
255
- '@',
256
- '.',
257
- '/',
258
- '!',
259
- ' '
260
- ])
261
- const stringedKey = keyNotAllowdChars ? `'${key}'` : key
237
+ for (const key in obj) {
238
+ if (!Object.prototype.hasOwnProperty.call(obj, key)) continue
239
+ const value = obj[key]
240
+ let keyNeedsQuotes = false
241
+ for (let i = 0; i < key.length; i++) {
242
+ if (OBJ_TO_STR_SPECIAL_CHARS.has(key[i])) { keyNeedsQuotes = true; break }
243
+ }
244
+ const stringedKey = keyNeedsQuotes ? `'${key}'` : key
262
245
  str += `${spaces} ${stringedKey}: `
263
246
 
264
247
  if (isArray(value)) {
@@ -290,44 +273,46 @@ export const objectToString = (obj = {}, indent = 0) => {
290
273
  return str
291
274
  }
292
275
 
276
+ const FN_PATTERNS = [
277
+ /^\(\s*\{[^}]*\}\s*\)\s*=>/,
278
+ /^(\([^)]*\)|[^=]*)\s*=>/,
279
+ /^function[\s(]/,
280
+ /^async\s+/,
281
+ /^\(\s*function/,
282
+ /^[a-zA-Z_$][a-zA-Z0-9_$]*\s*=>/
283
+ ]
284
+ const RE_JSON_LIKE = /^["[{]/
285
+
293
286
  export const hasFunction = str => {
294
287
  if (!str) return false
295
288
 
296
289
  const trimmed = str.trim().replace(/\n\s*/g, ' ').trim()
297
290
 
298
- if (trimmed === '') return false
299
- if (trimmed === '{}') return false
300
- if (trimmed === '[]') return false
301
-
302
- const patterns = [
303
- /^\(\s*\{[^}]*\}\s*\)\s*=>/,
304
- /^(\([^)]*\)|[^=]*)\s*=>/,
305
- /^function[\s(]/,
306
- /^async\s+/,
307
- /^\(\s*function/,
308
- /^[a-zA-Z_$][a-zA-Z0-9_$]*\s*=>/
309
- ]
310
-
311
- const isFunction = patterns.some(pattern => pattern.test(trimmed))
312
- const isObjectLiteral = trimmed.startsWith('{') && !trimmed.includes('=>')
313
- const isArrayLiteral = trimmed.startsWith('[')
314
- const isJSONLike = /^["[{]/.test(trimmed) && !trimmed.includes('=>')
315
-
316
- return isFunction && !isObjectLiteral && !isArrayLiteral && !isJSONLike
291
+ if (trimmed === '' || trimmed === '{}' || trimmed === '[]') return false
292
+
293
+ const isFn = FN_PATTERNS.some(pattern => pattern.test(trimmed))
294
+ if (!isFn) return false
295
+
296
+ const firstChar = trimmed.charCodeAt(0)
297
+ const hasArrow = trimmed.includes('=>')
298
+ // '{' = 123, '[' = 91
299
+ if (firstChar === 123 && !hasArrow) return false // object literal
300
+ if (firstChar === 91) return false // array literal
301
+ if (RE_JSON_LIKE.test(trimmed) && !hasArrow) return false
302
+
303
+ return true
317
304
  }
318
305
 
319
306
  export const deepDestringify = (obj, destringified = {}) => {
320
307
  for (const prop in obj) {
321
- const hasOwnProperty = Object.prototype.hasOwnProperty.call(obj, prop)
322
- if (!hasOwnProperty) continue
308
+ if (!Object.prototype.hasOwnProperty.call(obj, prop)) continue
323
309
 
324
310
  const objProp = obj[prop]
325
311
 
326
312
  if (isString(objProp)) {
327
313
  if (hasFunction(objProp)) {
328
314
  try {
329
- const evalProp = window.eval(`(${objProp})`)
330
- destringified[prop] = evalProp
315
+ destringified[prop] = window.eval(`(${objProp})`)
331
316
  } catch (e) {
332
317
  if (e) destringified[prop] = objProp
333
318
  }
@@ -335,25 +320,25 @@ export const deepDestringify = (obj, destringified = {}) => {
335
320
  destringified[prop] = objProp
336
321
  }
337
322
  } else if (isArray(objProp)) {
338
- destringified[prop] = []
339
- objProp.forEach(arrProp => {
323
+ const arr = destringified[prop] = []
324
+ for (let i = 0; i < objProp.length; i++) {
325
+ const arrProp = objProp[i]
340
326
  if (isString(arrProp)) {
341
327
  if (hasFunction(arrProp)) {
342
328
  try {
343
- const evalProp = window.eval(`(${arrProp})`)
344
- destringified[prop].push(evalProp)
329
+ arr.push(window.eval(`(${arrProp})`))
345
330
  } catch (e) {
346
- if (e) destringified[prop].push(arrProp)
331
+ if (e) arr.push(arrProp)
347
332
  }
348
333
  } else {
349
- destringified[prop].push(arrProp)
334
+ arr.push(arrProp)
350
335
  }
351
336
  } else if (isObject(arrProp)) {
352
- destringified[prop].push(deepDestringify(arrProp))
337
+ arr.push(deepDestringify(arrProp))
353
338
  } else {
354
- destringified[prop].push(arrProp)
339
+ arr.push(arrProp)
355
340
  }
356
- })
341
+ }
357
342
  } else if (isObject(objProp)) {
358
343
  destringified[prop] = deepDestringify(objProp, destringified[prop])
359
344
  } else {
@@ -374,7 +359,10 @@ export const stringToObject = (str, opts = { verbose: true }) => {
374
359
  export const hasOwnProperty = (o, ...args) =>
375
360
  Object.prototype.hasOwnProperty.call(o, ...args)
376
361
 
377
- export const isEmpty = o => Object.keys(o).length === 0
362
+ export const isEmpty = o => {
363
+ for (const _ in o) return false // eslint-disable-line
364
+ return true
365
+ }
378
366
 
379
367
  export const isEmptyObject = o => isObject(o) && isEmpty(o)
380
368
 
@@ -388,12 +376,9 @@ export const overwrite = (element, params, opts = {}) => {
388
376
  const allowUnderscore = opts.preventUnderscore
389
377
 
390
378
  for (const e in params) {
391
- if (excl.includes(e) || (!allowUnderscore && e.startsWith('__'))) continue
392
-
393
- const paramsProp = params[e]
394
-
395
- if (paramsProp !== undefined) {
396
- element[e] = paramsProp
379
+ if (excl.includes(e) || (!allowUnderscore && _startsWithDunder(e))) continue
380
+ if (params[e] !== undefined) {
381
+ element[e] = params[e]
397
382
  }
398
383
  }
399
384
 
@@ -401,8 +386,10 @@ export const overwrite = (element, params, opts = {}) => {
401
386
  }
402
387
 
403
388
  export const overwriteShallow = (obj, params, excludeFrom = []) => {
389
+ const useSet = excludeFrom instanceof Set
404
390
  for (const e in params) {
405
- if (excludeFrom.includes(e) || e.startsWith('__')) continue
391
+ if (_startsWithDunder(e)) continue
392
+ if (useSet ? excludeFrom.has(e) : excludeFrom.includes(e)) continue
406
393
  obj[e] = params[e]
407
394
  }
408
395
  return obj
@@ -417,9 +404,6 @@ export const overwriteDeep = (
417
404
  opts = {},
418
405
  visited = new WeakMap()
419
406
  ) => {
420
- const excl = opts.exclude || []
421
- const forcedExclude = opts.preventForce ? [] : ['node', 'window']
422
-
423
407
  if (
424
408
  !isObjectLike(obj) ||
425
409
  !isObjectLike(params) ||
@@ -432,9 +416,13 @@ export const overwriteDeep = (
432
416
  if (visited.has(obj)) return visited.get(obj)
433
417
  visited.set(obj, obj)
434
418
 
419
+ const excl = opts.exclude
420
+ const exclSet = excl ? (excl instanceof Set ? excl : new Set(excl)) : null
421
+ const forcedExclude = !opts.preventForce
422
+
435
423
  for (const e in params) {
436
- if (!Object.hasOwnProperty.call(params, e)) continue
437
- if (excl.includes(e) || (forcedExclude && e.startsWith('__'))) continue
424
+ if (!Object.prototype.hasOwnProperty.call(params, e)) continue
425
+ if ((exclSet && exclSet.has(e)) || (forcedExclude && _startsWithDunder(e))) continue
438
426
 
439
427
  const objProp = obj[e]
440
428
  const paramsProp = params[e]
@@ -453,52 +441,19 @@ export const overwriteDeep = (
453
441
 
454
442
  /**
455
443
  * Recursively compares two values to determine if they are deeply equal.
456
- *
457
- * This function checks for deep equality between two values, including
458
- * objects, arrays, and nested structures. It handles circular references to
459
- * prevent infinite loops.
460
- *
461
- * @param {*} param - The first value to compare.
462
- * @param {*} element - The second value to compare.
463
- * @param {Set} [visited] - (Optional) A set to track visited objects during recursion
464
- * to handle circular references. You can omit this parameter when calling
465
- * the function; it is used internally for tracking visited objects.
466
- *
467
- * @returns {boolean} Returns `true` if the values are deeply equal, `false` otherwise.
468
- *
469
- * @example
470
- * // Comparing primitive values
471
- * isEqualDeep(42, 42); // true
472
- * isEqualDeep('hello', 'hello'); // true
473
- * isEqualDeep(true, true); // true
474
- * isEqualDeep(42, '42'); // false
475
- *
476
- * // Comparing simple objects
477
- * const obj1 = { a: 1, b: { c: 2 } };
478
- * const obj2 = { a: 1, b: { c: 2 } };
479
- * isEqualDeep(obj1, obj2); // true
480
- *
481
- * // Handling circular references
482
- * const circularObj = { prop: null };
483
- * circularObj.prop = circularObj;
484
- * const anotherObj = { prop: null };
485
- * anotherObj.prop = anotherObj;
486
- * isEqualDeep(circularObj, anotherObj); // true
487
444
  */
488
445
  export const isEqualDeep = (param, element, visited = new Set()) => {
489
- // Check if both values are non-null objects
490
446
  if (
491
447
  typeof param !== 'object' ||
492
448
  typeof element !== 'object' ||
493
449
  param === null ||
494
450
  element === null
495
451
  ) {
496
- return param === element // Compare non-object values directly
452
+ return param === element
497
453
  }
498
454
 
499
- // Check for circular references
500
455
  if (visited.has(param) || visited.has(element)) {
501
- return true // Assume equality to break the circular reference
456
+ return true
502
457
  }
503
458
 
504
459
  visited.add(param)
@@ -507,22 +462,16 @@ export const isEqualDeep = (param, element, visited = new Set()) => {
507
462
  const keysParam = Object.keys(param)
508
463
  const keysElement = Object.keys(element)
509
464
 
510
- // Check if both objects have the same number of properties
511
465
  if (keysParam.length !== keysElement.length) {
512
466
  return false
513
467
  }
514
468
 
515
- // Check if all properties in param also exist in element
516
- for (const key of keysParam) {
517
- if (!keysElement.includes(key)) {
469
+ for (let i = 0; i < keysParam.length; i++) {
470
+ const key = keysParam[i]
471
+ if (!Object.prototype.hasOwnProperty.call(element, key)) {
518
472
  return false
519
473
  }
520
-
521
- const paramProp = param[key]
522
- const elementProp = element[key]
523
-
524
- // Recursively check property values
525
- if (!isEqualDeep(paramProp, elementProp, visited)) {
474
+ if (!isEqualDeep(param[key], element[key], visited)) {
526
475
  return false
527
476
  }
528
477
  }
@@ -530,23 +479,23 @@ export const isEqualDeep = (param, element, visited = new Set()) => {
530
479
  return true
531
480
  }
532
481
 
533
- export const deepContains = (obj1, obj2, ignoredKeys = ['node', '__ref']) => {
482
+ const DEEP_CONTAINS_IGNORED = new Set(['node', '__ref'])
483
+
484
+ export const deepContains = (obj1, obj2, ignoredKeys = DEEP_CONTAINS_IGNORED) => {
534
485
  if (obj1 === obj2) return true
535
486
  if (!isObjectLike(obj1) || !isObjectLike(obj2)) return obj1 === obj2
536
487
  if (isDOMNode(obj1) || isDOMNode(obj2)) return obj1 === obj2
537
488
 
489
+ const ignored = ignoredKeys instanceof Set ? ignoredKeys : new Set(ignoredKeys)
538
490
  const visited = new WeakSet()
539
491
 
540
492
  function checkContains (target, source) {
541
493
  if (visited.has(source)) return true
542
494
  visited.add(source)
543
495
 
544
- // Check each property in source
545
496
  for (const key in source) {
546
497
  if (!Object.prototype.hasOwnProperty.call(source, key)) continue
547
- if (ignoredKeys.includes(key)) continue
548
-
549
- // If key doesn't exist in target, return false
498
+ if (ignored.has(key)) continue
550
499
  if (!Object.prototype.hasOwnProperty.call(target, key)) return false
551
500
 
552
501
  const sourceValue = source[key]
@@ -572,7 +521,7 @@ export const removeFromObject = (obj, props) => {
572
521
  if (is(props)('string', 'number')) {
573
522
  delete obj[props]
574
523
  } else if (isArray(props)) {
575
- props.forEach(prop => delete obj[prop])
524
+ for (let i = 0; i < props.length; i++) delete obj[props[i]]
576
525
  } else {
577
526
  throw new Error(
578
527
  'Invalid input: props must be a string or an array of strings'
@@ -583,14 +532,14 @@ export const removeFromObject = (obj, props) => {
583
532
 
584
533
  export const createObjectWithoutPrototype = obj => {
585
534
  if (obj === null || typeof obj !== 'object') {
586
- return obj // Return the value if obj is not an object
535
+ return obj
587
536
  }
588
537
 
589
- const newObj = Object.create(null) // Create an object without prototype
538
+ const newObj = Object.create(null)
590
539
 
591
540
  for (const key in obj) {
592
541
  if (Object.prototype.hasOwnProperty.call(obj, key)) {
593
- newObj[key] = createObjectWithoutPrototype(obj[key]) // Recursively copy each property
542
+ newObj[key] = createObjectWithoutPrototype(obj[key])
594
543
  }
595
544
  }
596
545
 
@@ -598,21 +547,19 @@ export const createObjectWithoutPrototype = obj => {
598
547
  }
599
548
 
600
549
  export const createNestedObject = (arr, lastValue) => {
601
- const nestedObject = {}
550
+ if (arr.length === 0) return lastValue
602
551
 
603
- if (arr.length === 0) {
604
- return lastValue
605
- }
552
+ const nestedObject = {}
553
+ let current = nestedObject
606
554
 
607
- arr.reduce((obj, value, index) => {
608
- if (!obj[value]) {
609
- obj[value] = {}
610
- }
611
- if (index === arr.length - 1 && lastValue) {
612
- obj[value] = lastValue
555
+ for (let i = 0; i < arr.length; i++) {
556
+ if (i === arr.length - 1 && lastValue) {
557
+ current[arr[i]] = lastValue
558
+ } else {
559
+ current[arr[i]] = {}
560
+ current = current[arr[i]]
613
561
  }
614
- return obj[value]
615
- }, nestedObject)
562
+ }
616
563
 
617
564
  return nestedObject
618
565
  }
@@ -625,14 +572,12 @@ export const removeNestedKeyByPath = (obj, path) => {
625
572
  let current = obj
626
573
 
627
574
  for (let i = 0; i < path.length - 1; i++) {
628
- if (current[path[i]] === undefined) {
629
- return // Path does not exist, so nothing to remove.
630
- }
575
+ if (current[path[i]] === undefined) return
631
576
  current = current[path[i]]
632
577
  }
633
578
 
634
579
  const lastKey = path[path.length - 1]
635
- if (current && Object.hasOwnProperty.call(current, lastKey)) {
580
+ if (current && Object.prototype.hasOwnProperty.call(current, lastKey)) {
636
581
  delete current[lastKey]
637
582
  }
638
583
  }
@@ -645,15 +590,13 @@ export const setInObjectByPath = (obj, path, value) => {
645
590
  let current = obj
646
591
 
647
592
  for (let i = 0; i < path.length - 1; i++) {
648
- // If the current path segment doesn't exist or isn't an object, create it
649
593
  if (!current[path[i]] || typeof current[path[i]] !== 'object') {
650
594
  current[path[i]] = {}
651
595
  }
652
596
  current = current[path[i]]
653
597
  }
654
598
 
655
- const lastKey = path[path.length - 1]
656
- current[lastKey] = value
599
+ current[path[path.length - 1]] = value
657
600
 
658
601
  return obj
659
602
  }
@@ -676,25 +619,21 @@ export const getInObjectByPath = (obj, path) => {
676
619
  }
677
620
 
678
621
  export const detectInfiniteLoop = arr => {
679
- const maxRepeats = 10 // Maximum allowed repetitions
622
+ const maxRepeats = 10
680
623
  let pattern = []
681
624
  let repeatCount = 0
682
625
 
683
626
  for (let i = 0; i < arr.length; i++) {
684
627
  if (pattern.length < 2) {
685
- // Build the initial pattern with two consecutive elements
686
628
  pattern.push(arr[i])
687
629
  } else {
688
- // Check if the current element follows the repeating pattern
689
630
  if (arr[i] === pattern[i % 2]) {
690
631
  repeatCount++
691
632
  } else {
692
- // If there's a mismatch, reset the pattern and repeat counter
693
633
  pattern = [arr[i - 1], arr[i]]
694
- repeatCount = 1 // Reset to 1 because we start a new potential pattern
634
+ repeatCount = 1
695
635
  }
696
636
 
697
- // If the pattern repeats more than `maxRepeats`, throw a warning
698
637
  if (repeatCount >= maxRepeats * 2) {
699
638
  if (ENV === 'test' || ENV === 'development') {
700
639
  console.warn(
@@ -709,16 +648,14 @@ export const detectInfiniteLoop = arr => {
709
648
  }
710
649
 
711
650
  export const isCyclic = obj => {
712
- const seenObjects = []
651
+ const seen = new WeakSet()
713
652
 
714
653
  function detect (obj) {
715
654
  if (obj && typeof obj === 'object') {
716
- if (seenObjects.indexOf(obj) !== -1) {
717
- return true
718
- }
719
- seenObjects.push(obj)
655
+ if (seen.has(obj)) return true
656
+ seen.add(obj)
720
657
  for (const key in obj) {
721
- if (Object.hasOwnProperty.call(obj, key) && detect(obj[key])) {
658
+ if (Object.prototype.hasOwnProperty.call(obj, key) && detect(obj[key])) {
722
659
  console.log(obj, 'cycle at ' + key)
723
660
  return true
724
661
  }
@@ -731,7 +668,12 @@ export const isCyclic = obj => {
731
668
  }
732
669
 
733
670
  export const excludeKeysFromObject = (obj, excludedKeys) => {
734
- const result = { ...obj }
735
- excludedKeys.forEach(key => delete result[key])
671
+ const excluded = excludedKeys instanceof Set ? excludedKeys : new Set(excludedKeys)
672
+ const result = {}
673
+ for (const key in obj) {
674
+ if (Object.prototype.hasOwnProperty.call(obj, key) && !excluded.has(key)) {
675
+ result[key] = obj[key]
676
+ }
677
+ }
736
678
  return result
737
679
  }