@superhero/deep 4.4.0 → 4.5.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/README.md CHANGED
@@ -134,7 +134,33 @@ console.log(result)
134
134
 
135
135
  ---
136
136
 
137
- ## 6. **Deep**
137
+ ## 6. **DeepEqual**
138
+
139
+ ### Purpose:
140
+ Performs a deep equal compare between two values. Supports objects, arrays, and nested structures, and handles circular references. Values are strictly compared by default.
141
+
142
+ ### Features:
143
+ - Compares first and second argument - returns a boolean.
144
+ - Can perform a loose compare by passing a false third argument.
145
+
146
+ ### Example:
147
+ ```javascript
148
+ import deepequal from '@superhero/deep/equal'
149
+
150
+ const a = { foo: { bar: 1 }, arr: [1, 2, 3] }
151
+ const b = { foo: { bar: 1 }, arr: [1, 2, 3] }
152
+ const c = { foo: { bar: 1 }, arr: ['1', '2', '3'] }
153
+
154
+ deepequal(a, b) // true
155
+ deepequal(a, c) // false
156
+ deepequal(a, c, false) // true
157
+ deepequal(a, b, false) // true
158
+ deepequal(a, b, c) // throws AssertionError
159
+ ```
160
+
161
+ ---
162
+
163
+ ## 7. **Deep**
138
164
 
139
165
  ### Purpose:
140
166
  Makes the functions accessible through the imported default object.
@@ -149,6 +175,7 @@ import deep from '@superhero/deep'
149
175
 
150
176
  deep.assign(/* ... */)
151
177
  deep.clone(/* ... */)
178
+ deep.equal(/* ... */)
152
179
  deep.freeze(/* ... */)
153
180
  deep.intersect(/* ... */)
154
181
  deep.merge(/* ... */)
@@ -156,10 +183,11 @@ deep.merge(/* ... */)
156
183
 
157
184
  ### Example:
158
185
  ```javascript
159
- import { assign, clone, freeze, intersect, merge } from '@superhero/deep'
186
+ import { assign, clone, equal, freeze, intersect, merge } from '@superhero/deep'
160
187
 
161
188
  assign(/* ... */)
162
189
  clone(/* ... */)
190
+ equal(/* ... */)
163
191
  freeze(/* ... */)
164
192
  intersect(/* ... */)
165
193
  merge(/* ... */)
@@ -180,103 +208,115 @@ npm test
180
208
 
181
209
  ```
182
210
  ▶ @superhero/deep/assign
183
- ✔ Assigns arrays correctly (3.552823ms)
184
- ✔ Assigns objects correctly (0.702394ms)
185
- ✔ Overwrites non-object properties correctly (1.106746ms)
186
- ✔ Handles undefined values correctly (0.592877ms)
187
-
211
+ ✔ Assigns arrays correctly (2.973718ms)
212
+ ✔ Assigns objects correctly (0.70144ms)
213
+ ✔ Overwrites non-object properties correctly (0.318183ms)
214
+ ✔ Handles undefined values correctly (1.009161ms)
188
215
  ▶ Descriptor properties
189
216
  ▶ Retains
190
- ✔ non-writable, non-configurable and non-enumarable (0.774514ms)
191
- ✔ writable but non-configurable and non-enumarable (0.490026ms)
192
- ✔ writable and configurable but non-enumarable (0.314062ms)
193
- ✔ Retains (2.377181ms)
194
-
217
+ ✔ non-writable, non-configurable and non-enumarable (0.479579ms)
218
+ ✔ writable but non-configurable and non-enumarable (0.466066ms)
219
+ ✔ writable and configurable but non-enumarable (0.309409ms)
220
+ ✔ Retains (1.735145ms)
221
+
195
222
  ▶ Assigns
196
- ✔ non-writable, non-configurable and non-enumarable (0.415235ms)
197
- ✔ Assigns (0.824789ms)
198
- ✔ Descriptor properties (3.881836ms)
199
-
200
- ✔ Merges nested arrays correctly (6.642858ms)
201
- Merges nested objects correctly (0.731397ms)
202
- Does not alter objects with no conflicts (0.27423ms)
203
- ✔ @superhero/deep/assign (20.322649ms)
223
+ ✔ non-writable, non-configurable and non-enumarable (0.384251ms)
224
+ ✔ Assigns (0.73481ms)
225
+ ✔ Descriptor properties (3.130721ms)
226
+ ✔ Merges nested arrays correctly (2.917128ms)
227
+ ✔ Merges nested objects correctly (0.937044ms)
228
+ Does not alter objects with no conflicts (0.274969ms)
229
+ @superhero/deep/assign (15.154613ms)
204
230
 
205
231
  ▶ @superhero/deep/clone
206
- ✔ Clones simple objects (6.103605ms)
207
- ✔ Clones nested objects (0.771236ms)
208
- Preserves descriptors (1.57539ms)
209
- Clones arrays (1.604074ms)
210
- Handles circular references (0.507477ms)
211
- ✔ Clones objects with null prototype (1.230476ms)
212
- @superhero/deep/clone (14.513864ms)
232
+ ✔ Clones simple objects (3.281531ms)
233
+ ✔ Clones nested objects (0.470928ms)
234
+ Do not preserves descriptors (0.56486ms)
235
+ Preserves descriptors (0.547388ms)
236
+ Does not preserve frozen object state (1.833744ms)
237
+ ✔ Clones arrays (0.707413ms)
238
+ Handles circular references (0.320116ms)
239
+ ✔ Clones objects with null prototype (0.620148ms)
240
+ ✔ @superhero/deep/clone (12.239426ms)
241
+
242
+ ▶ @superhero/deep/equal
243
+ ✔ Strict equality: same values, same types (5.24189ms)
244
+ ✔ Strict equality: fails on type mismatch (0.422575ms)
245
+ ✔ Loose equality: same values, different types (0.457711ms)
246
+ ✔ Loose equality: different values (1.583009ms)
247
+ ✔ Deep equality with nested structure (0.678751ms)
248
+ ✔ Fails on missing property (0.721941ms)
249
+ ✔ Validates third argument is boolean (2.323427ms)
250
+ ✔ @superhero/deep/equal (13.789463ms)
213
251
 
214
252
  ▶ @superhero/deep/freeze
215
- ✔ Freezes a simple object (2.735609ms)
216
- ✔ Freezes nested objects recursively (0.40638ms)
217
- ✔ Handles circular references gracefully (0.781639ms)
218
- ✔ Freezes objects with symbols (0.455776ms)
219
- ✔ Handles already frozen objects without error (0.342712ms)
220
- ✔ Freezes objects with non-enumerable properties (0.455515ms)
221
- ✔ Freezes arrays (0.61924ms)
222
- ✔ Handles objects with null prototype (0.57424ms)
223
- ✔ Freezes objects with multiple property types (0.984788ms)
224
- ✔ @superhero/deep/freeze (12.988297ms)
253
+ ✔ Freezes a simple object (3.92657ms)
254
+ ✔ Freezes nested objects recursively (0.371321ms)
255
+ ✔ Handles circular references gracefully (1.267506ms)
256
+ ✔ Freezes objects with symbols (0.444381ms)
257
+ ✔ Handles already frozen objects without error (0.930388ms)
258
+ ✔ Freezes objects with non-enumerable properties (0.455779ms)
259
+ ✔ Freezes arrays (0.477364ms)
260
+ ✔ Handles objects with null prototype (0.536402ms)
261
+ ✔ Freezes objects with multiple property types (0.776249ms)
262
+ ✔ @superhero/deep/freeze (15.887295ms)
225
263
 
226
264
  ▶ @superhero/deep
227
- ✔ All functions are accessible as a member to the default import object (1.908797ms)
228
- ✔ All functions are accessible to import from the default import object (0.603152ms)
229
- ✔ @superhero/deep (7.176949ms)
265
+ ✔ All functions are accessible as a member to the default import object (1.711564ms)
266
+ ✔ All functions are accessible to import from the default import object (0.375857ms)
267
+ ✔ @superhero/deep (4.37013ms)
230
268
 
231
269
  ▶ @superhero/deep/intersect
232
- ✔ Intersects arrays by value and position (5.556412ms)
233
- ✔ Intersects nested arrays (0.312899ms)
234
- ✔ Handles empty array intersection (0.297251ms)
235
- ✔ Intersects objects with matching keys and values (0.554782ms)
236
- ✔ Deeply intersects nested objects (0.568448ms)
237
- ✔ Intersection stops at type mismatch (0.278157ms)
238
- ✔ Throws on circular references (1.779685ms)
239
- ✔ Intersects arrays with undefined positions (0.702569ms)
240
- ✔ Handles intersection of primitive types (0.525931ms)
241
- ✔ Returns undefined for non-intersecting primitives (0.867342ms)
242
- ✔ Handles multiple sequential intersections (0.836957ms)
243
- ✔ @superhero/deep/intersect (15.753992ms)
270
+ ✔ Intersects arrays by value and position (5.404912ms)
271
+ ✔ Intersects nested arrays (0.367099ms)
272
+ ✔ Handles empty array intersection (0.687107ms)
273
+ ✔ Intersects objects with matching keys and values (2.015991ms)
274
+ ✔ Deeply intersects nested objects (0.501549ms)
275
+ ✔ Intersection stops at type mismatch (0.423998ms)
276
+ ✔ Throws on circular references (1.079638ms)
277
+ ✔ Intersects arrays with undefined positions (0.399501ms)
278
+ ✔ Handles intersection of primitive types (0.576801ms)
279
+ ✔ Returns undefined for non-intersecting primitives (2.684012ms)
280
+ ✔ Handles multiple sequential intersections (0.511929ms)
281
+ ✔ @superhero/deep/intersect (19.049326ms)
244
282
 
245
283
  ▶ @superhero/deep/merge
246
- ✔ Merges arrays with unique values (4.014593ms)
247
- ✔ Merges arrays with order preserved (0.431737ms)
248
- ✔ Handles empty arrays correctly (0.409507ms)
249
- ✔ Handles arrays with duplicate values (0.501899ms)
250
- ✔ Merges objects and prioritizes restrictive descriptors (0.804508ms)
251
- ✔ Merges objects with non-enumerable properties (0.802831ms)
252
- ✔ Handles nested object merging (0.709563ms)
253
- ✔ Stops at circular references (0.768717ms)
254
- ✔ Stops when nested and with circular references (1.386111ms)
255
- ✔ Returns second value for non-object types (1.438328ms)
256
- ✔ Handles multiple merges sequentially (0.494506ms)
257
- ✔ @superhero/deep/merge (14.798621ms)
258
-
259
- tests 50
260
- suites 9
261
- pass 50
284
+ ✔ Merges arrays with unique values (4.448622ms)
285
+ ✔ Merges arrays with order preserved (0.439982ms)
286
+ ✔ Handles empty arrays correctly (0.291365ms)
287
+ ✔ Handles arrays with duplicate values (0.497621ms)
288
+ ✔ Merges objects and prioritizes restrictive descriptors (3.844952ms)
289
+ ✔ Merges objects with non-enumerable properties (0.710082ms)
290
+ ✔ Handles nested object merging (0.412465ms)
291
+ ✔ Stops at circular references (0.446904ms)
292
+ ✔ Stops when nested and with circular references (1.003584ms)
293
+ ✔ Returns second value for non-object types (3.370647ms)
294
+ ✔ Handles multiple merges sequentially (10.429993ms)
295
+ ✔ @superhero/deep/merge (37.960347ms)
296
+
297
+ tests 59
298
+ suites 10
299
+ pass 59
262
300
 
263
301
  ------------------------------------------------------------------------
264
302
  file | line % | branch % | funcs % | uncovered lines
265
303
  ------------------------------------------------------------------------
266
304
  assign.js | 97.80 | 96.15 | 100.00 | 15-16
267
305
  assign.test.js | 100.00 | 100.00 | 100.00 |
268
- clone.js | 95.83 | 93.33 | 100.00 | 22-23
306
+ clone.js | 89.47 | 83.33 | 100.00 | 27-28 87-94
269
307
  clone.test.js | 100.00 | 100.00 | 100.00 |
308
+ equal.js | 87.10 | 85.71 | 100.00 | 26-29
309
+ equal.test.js | 100.00 | 100.00 | 100.00 |
270
310
  freeze.js | 100.00 | 100.00 | 100.00 |
271
311
  freeze.test.js | 100.00 | 100.00 | 100.00 |
272
312
  index.js | 100.00 | 100.00 | 100.00 |
273
313
  index.test.js | 100.00 | 100.00 | 100.00 |
274
- intersect.js | 94.77 | 91.18 | 100.00 | 70-71 85-86 134-137
314
+ intersect.js | 95.15 | 91.67 | 100.00 | 70-71 97-98 146-149
275
315
  intersect.test.js | 100.00 | 100.00 | 100.00 |
276
316
  merge.js | 98.72 | 96.30 | 100.00 | 81-82
277
317
  merge.test.js | 100.00 | 100.00 | 100.00 |
278
318
  ------------------------------------------------------------------------
279
- all files | 98.84 | 96.81 | 100.00 |
319
+ all files | 98.15 | 95.43 | 100.00 |
280
320
  ------------------------------------------------------------------------
281
321
  ```
282
322
 
package/equal.js ADDED
@@ -0,0 +1,31 @@
1
+ import assert from 'node:assert'
2
+ import util from 'node:util'
3
+
4
+ export default function equal(a, b, strict = true)
5
+ {
6
+ assert.strictEqual(typeof strict, 'boolean', 'Third argument "loose" must be a boolean')
7
+
8
+ return strict
9
+ ? util.isDeepStrictEqual(a, b)
10
+ : isDeepLooseEqual(a, b)
11
+ }
12
+
13
+ function isDeepLooseEqual(a, b)
14
+ {
15
+ try
16
+ {
17
+ assert.deepEqual(a, b)
18
+ return true
19
+ }
20
+ catch(error)
21
+ {
22
+ if(error instanceof assert.AssertionError)
23
+ {
24
+ return false
25
+ }
26
+ else
27
+ {
28
+ throw error
29
+ }
30
+ }
31
+ }
package/equal.test.js ADDED
@@ -0,0 +1,62 @@
1
+ import assert from 'assert'
2
+ import { suite, test } from 'node:test'
3
+ import deepequal from './equal.js'
4
+
5
+ suite('@superhero/deep/equal', () =>
6
+ {
7
+ test('Strict equality: same values, same types', () =>
8
+ {
9
+ const a = { foo: 'bar', baz: 42 }
10
+ const b = { foo: 'bar', baz: 42 }
11
+
12
+ assert.strictEqual(deepequal(a, b), true)
13
+ })
14
+
15
+ test('Strict equality: fails on type mismatch', () =>
16
+ {
17
+ const a = { foo: 'bar', baz: 42 }
18
+ const b = { foo: 'bar', baz: '42' }
19
+
20
+ assert.strictEqual(deepequal(a, b, true), false)
21
+ })
22
+
23
+ test('Loose equality: same values, different types', () =>
24
+ {
25
+ const a = { foo: 'bar', baz: 42 }
26
+ const b = { foo: 'bar', baz: '42' }
27
+
28
+ assert.strictEqual(deepequal(a, b, false), true)
29
+ })
30
+
31
+ test('Loose equality: different values', () =>
32
+ {
33
+ const a = { foo: 'bar', baz: 42 }
34
+ const b = { foo: 'bar', baz: 99 }
35
+
36
+ assert.strictEqual(deepequal(a, b, false), false)
37
+ })
38
+
39
+ test('Deep equality with nested structure', () =>
40
+ {
41
+ const a = { foo: { bar: [1, 2] } }
42
+ const b = { foo: { bar: [1, 2] } }
43
+
44
+ assert.strictEqual(deepequal(a, b), true)
45
+ assert.strictEqual(deepequal(a, b, false), true)
46
+ })
47
+
48
+ test('Fails on missing property', () =>
49
+ {
50
+ const a = { foo: 'bar', baz: 42 }
51
+ const b = { foo: 'bar' }
52
+
53
+ assert.strictEqual(deepequal(a, b), false)
54
+ assert.strictEqual(deepequal(a, b, false), false)
55
+ })
56
+
57
+ test('Validates third argument is boolean', () =>
58
+ {
59
+ assert.throws(() => deepequal({}, {}, 'loose'),
60
+ { name : 'AssertionError' })
61
+ })
62
+ })
package/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import assign from '@superhero/deep/assign'
2
2
  import clone from '@superhero/deep/clone'
3
+ import equal from '@superhero/deep/equal'
3
4
  import freeze from '@superhero/deep/freeze'
4
5
  import intersect from '@superhero/deep/intersect'
5
6
  import merge from '@superhero/deep/merge'
6
7
 
7
- export { assign, clone, freeze, intersect, merge }
8
- export default { assign, clone, freeze, intersect, merge }
8
+ export { assign, clone, equal, freeze, intersect, merge }
9
+ export default { assign, clone, equal, freeze, intersect, merge }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@superhero/deep",
3
- "version": "4.4.0",
3
+ "version": "4.5.0",
4
4
  "description": "A collection of deep structure operations",
5
- "keywords": ["deep", "assign", "clone", "freeze", "intersect", "merge"],
5
+ "keywords": ["deep", "assign", "clone", "equal", "freeze", "intersect", "merge"],
6
6
  "main": "index.js",
7
7
  "license": "MIT",
8
8
  "type": "module",