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