@pyreon/ui-core 0.11.4 → 0.11.6

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.
@@ -1,46 +1,46 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
2
- import { get, merge, omit, pick, set, throttle } from "../utils"
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { get, merge, omit, pick, set, throttle } from '../utils'
3
3
 
4
4
  // --------------------------------------------------------
5
5
  // omit
6
6
  // --------------------------------------------------------
7
- describe("omit", () => {
8
- it("should return object without specified keys", () => {
7
+ describe('omit', () => {
8
+ it('should return object without specified keys', () => {
9
9
  const obj = { a: 1, b: 2, c: 3 }
10
- expect(omit(obj, ["b"])).toEqual({ a: 1, c: 3 })
10
+ expect(omit(obj, ['b'])).toEqual({ a: 1, c: 3 })
11
11
  })
12
12
 
13
- it("should return shallow copy when no keys specified", () => {
13
+ it('should return shallow copy when no keys specified', () => {
14
14
  const obj = { a: 1, b: 2 }
15
15
  const result = omit(obj)
16
16
  expect(result).toEqual({ a: 1, b: 2 })
17
17
  expect(result).not.toBe(obj)
18
18
  })
19
19
 
20
- it("should return shallow copy when keys is empty array", () => {
20
+ it('should return shallow copy when keys is empty array', () => {
21
21
  const obj = { a: 1 }
22
22
  expect(omit(obj, [])).toEqual({ a: 1 })
23
23
  })
24
24
 
25
- it("should return empty object for null", () => {
25
+ it('should return empty object for null', () => {
26
26
  expect(omit(null)).toEqual({})
27
27
  })
28
28
 
29
- it("should return empty object for undefined", () => {
29
+ it('should return empty object for undefined', () => {
30
30
  expect(omit(undefined)).toEqual({})
31
31
  })
32
32
 
33
- it("should handle multiple keys", () => {
33
+ it('should handle multiple keys', () => {
34
34
  const obj = { a: 1, b: 2, c: 3, d: 4 }
35
- expect(omit(obj, ["a", "c"])).toEqual({ b: 2, d: 4 })
35
+ expect(omit(obj, ['a', 'c'])).toEqual({ b: 2, d: 4 })
36
36
  })
37
37
 
38
- it("should ignore keys not present in object", () => {
38
+ it('should ignore keys not present in object', () => {
39
39
  const obj = { a: 1 }
40
- expect(omit(obj, ["b", "c"])).toEqual({ a: 1 })
40
+ expect(omit(obj, ['b', 'c'])).toEqual({ a: 1 })
41
41
  })
42
42
 
43
- it("should only include own properties", () => {
43
+ it('should only include own properties', () => {
44
44
  const proto = { inherited: true }
45
45
  const obj = Object.create(proto)
46
46
  obj.own = 1
@@ -51,230 +51,230 @@ describe("omit", () => {
51
51
  // --------------------------------------------------------
52
52
  // pick
53
53
  // --------------------------------------------------------
54
- describe("pick", () => {
55
- it("should return object with only specified keys", () => {
54
+ describe('pick', () => {
55
+ it('should return object with only specified keys', () => {
56
56
  const obj = { a: 1, b: 2, c: 3 }
57
- expect(pick(obj, ["a", "c"])).toEqual({ a: 1, c: 3 })
57
+ expect(pick(obj, ['a', 'c'])).toEqual({ a: 1, c: 3 })
58
58
  })
59
59
 
60
- it("should return shallow copy when no keys specified", () => {
60
+ it('should return shallow copy when no keys specified', () => {
61
61
  const obj = { a: 1, b: 2 }
62
62
  const result = pick(obj)
63
63
  expect(result).toEqual({ a: 1, b: 2 })
64
64
  expect(result).not.toBe(obj)
65
65
  })
66
66
 
67
- it("should return shallow copy when keys is empty array", () => {
67
+ it('should return shallow copy when keys is empty array', () => {
68
68
  const obj = { a: 1 }
69
69
  expect(pick(obj, [])).toEqual({ a: 1 })
70
70
  })
71
71
 
72
- it("should return empty object for null", () => {
72
+ it('should return empty object for null', () => {
73
73
  expect(pick(null)).toEqual({})
74
74
  })
75
75
 
76
- it("should return empty object for undefined", () => {
76
+ it('should return empty object for undefined', () => {
77
77
  expect(pick(undefined)).toEqual({})
78
78
  })
79
79
 
80
- it("should ignore keys not present in object", () => {
80
+ it('should ignore keys not present in object', () => {
81
81
  const obj = { a: 1 }
82
- expect(pick(obj, ["a", "b"])).toEqual({ a: 1 })
82
+ expect(pick(obj, ['a', 'b'])).toEqual({ a: 1 })
83
83
  })
84
84
 
85
- it("should only pick own properties", () => {
85
+ it('should only pick own properties', () => {
86
86
  const proto = { inherited: true }
87
87
  const obj = Object.create(proto)
88
88
  obj.own = 1
89
- expect(pick(obj, ["own", "inherited"])).toEqual({ own: 1 })
89
+ expect(pick(obj, ['own', 'inherited'])).toEqual({ own: 1 })
90
90
  })
91
91
  })
92
92
 
93
93
  // --------------------------------------------------------
94
94
  // get
95
95
  // --------------------------------------------------------
96
- describe("get", () => {
97
- it("should get nested value by dot path", () => {
96
+ describe('get', () => {
97
+ it('should get nested value by dot path', () => {
98
98
  const obj = { a: { b: { c: 42 } } }
99
- expect(get(obj, "a.b.c")).toBe(42)
99
+ expect(get(obj, 'a.b.c')).toBe(42)
100
100
  })
101
101
 
102
- it("should get value by array path", () => {
102
+ it('should get value by array path', () => {
103
103
  const obj = { a: { b: 10 } }
104
- expect(get(obj, ["a", "b"])).toBe(10)
104
+ expect(get(obj, ['a', 'b'])).toBe(10)
105
105
  })
106
106
 
107
- it("should get array element by bracket notation", () => {
107
+ it('should get array element by bracket notation', () => {
108
108
  const obj = { items: [10, 20, 30] }
109
- expect(get(obj, "items[1]")).toBe(20)
109
+ expect(get(obj, 'items[1]')).toBe(20)
110
110
  })
111
111
 
112
- it("should return defaultValue when path does not exist", () => {
112
+ it('should return defaultValue when path does not exist', () => {
113
113
  const obj = { a: 1 }
114
- expect(get(obj, "b.c", "default")).toBe("default")
114
+ expect(get(obj, 'b.c', 'default')).toBe('default')
115
115
  })
116
116
 
117
- it("should return defaultValue when intermediate is null", () => {
117
+ it('should return defaultValue when intermediate is null', () => {
118
118
  const obj = { a: null }
119
- expect(get(obj, "a.b", "fallback")).toBe("fallback")
119
+ expect(get(obj, 'a.b', 'fallback')).toBe('fallback')
120
120
  })
121
121
 
122
- it("should return defaultValue when intermediate is undefined", () => {
122
+ it('should return defaultValue when intermediate is undefined', () => {
123
123
  const obj = { a: undefined }
124
- expect(get(obj, "a.b", "fallback")).toBe("fallback")
124
+ expect(get(obj, 'a.b', 'fallback')).toBe('fallback')
125
125
  })
126
126
 
127
- it("should return undefined when path does not exist and no default", () => {
128
- expect(get({}, "a.b.c")).toBeUndefined()
127
+ it('should return undefined when path does not exist and no default', () => {
128
+ expect(get({}, 'a.b.c')).toBeUndefined()
129
129
  })
130
130
 
131
- it("should return the root value for empty array path", () => {
131
+ it('should return the root value for empty array path', () => {
132
132
  const obj = { a: 1 }
133
133
  expect(get(obj, [])).toEqual({ a: 1 })
134
134
  })
135
135
 
136
- it("should handle top-level key", () => {
137
- expect(get({ x: 5 }, "x")).toBe(5)
136
+ it('should handle top-level key', () => {
137
+ expect(get({ x: 5 }, 'x')).toBe(5)
138
138
  })
139
139
 
140
- it("should return actual value even if it is falsy", () => {
141
- expect(get({ a: 0 }, "a", "default")).toBe(0)
142
- expect(get({ a: false }, "a", "default")).toBe(false)
143
- expect(get({ a: "" }, "a", "default")).toBe("")
144
- expect(get({ a: null }, "a", "default")).toBeNull()
140
+ it('should return actual value even if it is falsy', () => {
141
+ expect(get({ a: 0 }, 'a', 'default')).toBe(0)
142
+ expect(get({ a: false }, 'a', 'default')).toBe(false)
143
+ expect(get({ a: '' }, 'a', 'default')).toBe('')
144
+ expect(get({ a: null }, 'a', 'default')).toBeNull()
145
145
  })
146
146
 
147
- it("should use defaultValue only when result is undefined", () => {
148
- expect(get({ a: undefined }, "a", "default")).toBe("default")
147
+ it('should use defaultValue only when result is undefined', () => {
148
+ expect(get({ a: undefined }, 'a', 'default')).toBe('default')
149
149
  })
150
150
 
151
151
  // UNSAFE_KEYS guard
152
- it("should return defaultValue for __proto__ key", () => {
152
+ it('should return defaultValue for __proto__ key', () => {
153
153
  const obj = { a: 1 }
154
- expect(get(obj, "__proto__", "safe")).toBe("safe")
154
+ expect(get(obj, '__proto__', 'safe')).toBe('safe')
155
155
  })
156
156
 
157
- it("should return defaultValue for prototype key", () => {
157
+ it('should return defaultValue for prototype key', () => {
158
158
  const obj = { a: 1 }
159
- expect(get(obj, "prototype", "safe")).toBe("safe")
159
+ expect(get(obj, 'prototype', 'safe')).toBe('safe')
160
160
  })
161
161
 
162
- it("should return defaultValue for constructor key", () => {
162
+ it('should return defaultValue for constructor key', () => {
163
163
  const obj = { a: 1 }
164
- expect(get(obj, "constructor", "safe")).toBe("safe")
164
+ expect(get(obj, 'constructor', 'safe')).toBe('safe')
165
165
  })
166
166
 
167
- it("should return defaultValue for __proto__ in nested path", () => {
167
+ it('should return defaultValue for __proto__ in nested path', () => {
168
168
  const obj = { a: { b: 1 } }
169
- expect(get(obj, "a.__proto__.c", "safe")).toBe("safe")
169
+ expect(get(obj, 'a.__proto__.c', 'safe')).toBe('safe')
170
170
  })
171
171
 
172
- it("should return obj itself for empty string path (no keys parsed)", () => {
172
+ it('should return obj itself for empty string path (no keys parsed)', () => {
173
173
  // parsePath('') returns [] — no iteration — result stays as obj
174
174
  // obj is not undefined — returned as-is
175
- expect(get({ a: 1 }, "")).toEqual({ a: 1 })
175
+ expect(get({ a: 1 }, '')).toEqual({ a: 1 })
176
176
  })
177
177
  })
178
178
 
179
179
  // --------------------------------------------------------
180
180
  // set
181
181
  // --------------------------------------------------------
182
- describe("set", () => {
183
- it("should set nested value by dot path", () => {
182
+ describe('set', () => {
183
+ it('should set nested value by dot path', () => {
184
184
  const obj: any = {}
185
- set(obj, "a.b.c", 42)
185
+ set(obj, 'a.b.c', 42)
186
186
  expect(obj.a.b.c).toBe(42)
187
187
  })
188
188
 
189
- it("should set value by array path", () => {
189
+ it('should set value by array path', () => {
190
190
  const obj: any = {}
191
- set(obj, ["x", "y"], 10)
191
+ set(obj, ['x', 'y'], 10)
192
192
  expect(obj.x.y).toBe(10)
193
193
  })
194
194
 
195
- it("should create arrays for numeric keys", () => {
195
+ it('should create arrays for numeric keys', () => {
196
196
  const obj: any = {}
197
- set(obj, "items.0", "first")
197
+ set(obj, 'items.0', 'first')
198
198
  expect(Array.isArray(obj.items)).toBe(true)
199
- expect(obj.items[0]).toBe("first")
199
+ expect(obj.items[0]).toBe('first')
200
200
  })
201
201
 
202
- it("should overwrite existing values", () => {
202
+ it('should overwrite existing values', () => {
203
203
  const obj = { a: { b: 1 } }
204
- set(obj, "a.b", 2)
204
+ set(obj, 'a.b', 2)
205
205
  expect(obj.a.b).toBe(2)
206
206
  })
207
207
 
208
- it("should return the mutated object", () => {
208
+ it('should return the mutated object', () => {
209
209
  const obj = {}
210
- const result = set(obj, "a", 1)
210
+ const result = set(obj, 'a', 1)
211
211
  expect(result).toBe(obj)
212
212
  })
213
213
 
214
- it("should handle single key path", () => {
214
+ it('should handle single key path', () => {
215
215
  const obj: any = {}
216
- set(obj, "key", "value")
217
- expect(obj.key).toBe("value")
216
+ set(obj, 'key', 'value')
217
+ expect(obj.key).toBe('value')
218
218
  })
219
219
 
220
- it("should not set anything for empty path", () => {
220
+ it('should not set anything for empty path', () => {
221
221
  const obj = { a: 1 }
222
- set(obj, "", "value")
222
+ set(obj, '', 'value')
223
223
  expect(obj).toEqual({ a: 1 })
224
224
  })
225
225
 
226
226
  // Security: prototype pollution protection
227
- it("should not pollute Object.prototype via __proto__", () => {
227
+ it('should not pollute Object.prototype via __proto__', () => {
228
228
  const obj = {}
229
- set(obj, "__proto__.polluted", true)
229
+ set(obj, '__proto__.polluted', true)
230
230
  expect(({} as any).polluted).toBeUndefined()
231
231
  })
232
232
 
233
- it("should not pollute via constructor.prototype", () => {
233
+ it('should not pollute via constructor.prototype', () => {
234
234
  const obj = {}
235
- set(obj, "constructor.prototype.polluted", true)
235
+ set(obj, 'constructor.prototype.polluted', true)
236
236
  expect(({} as any).polluted).toBeUndefined()
237
237
  })
238
238
 
239
- it("should bail out when intermediate key is __proto__", () => {
239
+ it('should bail out when intermediate key is __proto__', () => {
240
240
  const obj: any = { a: 1 }
241
- const result = set(obj, "__proto__.polluted", true)
241
+ const result = set(obj, '__proto__.polluted', true)
242
242
  expect(result).toBe(obj)
243
243
  expect(({} as any).polluted).toBeUndefined()
244
244
  })
245
245
 
246
- it("should bail out when next key in path is unsafe", () => {
246
+ it('should bail out when next key in path is unsafe', () => {
247
247
  const obj: any = {}
248
- const result = set(obj, "a.__proto__", "bad")
248
+ const result = set(obj, 'a.__proto__', 'bad')
249
249
  expect(result).toBe(obj)
250
250
  expect(obj.a).toBeUndefined()
251
251
  })
252
252
 
253
- it("should bail out when last key is unsafe", () => {
253
+ it('should bail out when last key is unsafe', () => {
254
254
  const obj: any = {}
255
- set(obj, "prototype", "bad")
255
+ set(obj, 'prototype', 'bad')
256
256
  // prototype is in UNSAFE_KEYS, so the set should be blocked
257
257
  expect(obj.prototype).toBeUndefined()
258
258
  })
259
259
 
260
- it("should handle bracket notation in paths", () => {
260
+ it('should handle bracket notation in paths', () => {
261
261
  const obj: any = {}
262
- set(obj, "items[0].name", "first")
263
- expect(obj.items[0].name).toBe("first")
262
+ set(obj, 'items[0].name', 'first')
263
+ expect(obj.items[0].name).toBe('first')
264
264
  })
265
265
 
266
- it("should not overwrite existing intermediate objects", () => {
266
+ it('should not overwrite existing intermediate objects', () => {
267
267
  const obj: any = { a: { existing: true } }
268
- set(obj, "a.b", "new")
268
+ set(obj, 'a.b', 'new')
269
269
  expect(obj.a.existing).toBe(true)
270
- expect(obj.a.b).toBe("new")
270
+ expect(obj.a.b).toBe('new')
271
271
  })
272
272
  })
273
273
 
274
274
  // --------------------------------------------------------
275
275
  // throttle
276
276
  // --------------------------------------------------------
277
- describe("throttle", () => {
277
+ describe('throttle', () => {
278
278
  beforeEach(() => {
279
279
  vi.useFakeTimers()
280
280
  })
@@ -283,15 +283,15 @@ describe("throttle", () => {
283
283
  vi.useRealTimers()
284
284
  })
285
285
 
286
- it("should call function immediately on first invocation", () => {
286
+ it('should call function immediately on first invocation', () => {
287
287
  const fn = vi.fn()
288
288
  const throttled = throttle(fn, 100)
289
- throttled("a")
290
- expect(fn).toHaveBeenCalledWith("a")
289
+ throttled('a')
290
+ expect(fn).toHaveBeenCalledWith('a')
291
291
  expect(fn).toHaveBeenCalledTimes(1)
292
292
  })
293
293
 
294
- it("should not call again within wait period", () => {
294
+ it('should not call again within wait period', () => {
295
295
  const fn = vi.fn()
296
296
  const throttled = throttle(fn, 100)
297
297
  throttled()
@@ -300,24 +300,24 @@ describe("throttle", () => {
300
300
  expect(fn).toHaveBeenCalledTimes(1)
301
301
  })
302
302
 
303
- it("should call with latest args after wait period", () => {
303
+ it('should call with latest args after wait period', () => {
304
304
  const fn = vi.fn()
305
305
  const throttled = throttle(fn, 100)
306
306
 
307
- throttled("first")
308
- throttled("second")
309
- throttled("third")
307
+ throttled('first')
308
+ throttled('second')
309
+ throttled('third')
310
310
 
311
311
  expect(fn).toHaveBeenCalledTimes(1)
312
- expect(fn).toHaveBeenCalledWith("first")
312
+ expect(fn).toHaveBeenCalledWith('first')
313
313
 
314
314
  vi.advanceTimersByTime(100)
315
315
 
316
316
  expect(fn).toHaveBeenCalledTimes(2)
317
- expect(fn).toHaveBeenLastCalledWith("third")
317
+ expect(fn).toHaveBeenLastCalledWith('third')
318
318
  })
319
319
 
320
- it("should allow immediate call after wait period elapses", () => {
320
+ it('should allow immediate call after wait period elapses', () => {
321
321
  const fn = vi.fn()
322
322
  const throttled = throttle(fn, 100)
323
323
 
@@ -327,88 +327,88 @@ describe("throttle", () => {
327
327
  expect(fn).toHaveBeenCalledTimes(2)
328
328
  })
329
329
 
330
- it("should cancel pending invocations", () => {
330
+ it('should cancel pending invocations', () => {
331
331
  const fn = vi.fn()
332
332
  const throttled = throttle(fn, 100)
333
333
 
334
- throttled("first")
335
- throttled("second")
334
+ throttled('first')
335
+ throttled('second')
336
336
  throttled.cancel()
337
337
 
338
338
  vi.advanceTimersByTime(200)
339
339
  expect(fn).toHaveBeenCalledTimes(1)
340
340
  })
341
341
 
342
- it("should work with default wait of 0", () => {
342
+ it('should work with default wait of 0', () => {
343
343
  const fn = vi.fn()
344
344
  const throttled = throttle(fn)
345
345
  throttled()
346
346
  expect(fn).toHaveBeenCalledTimes(1)
347
347
  })
348
348
 
349
- it("should skip trailing call when trailing: false", () => {
349
+ it('should skip trailing call when trailing: false', () => {
350
350
  const fn = vi.fn()
351
351
  const throttled = throttle(fn, 100, { trailing: false })
352
352
 
353
- throttled("first")
354
- throttled("second")
355
- throttled("third")
353
+ throttled('first')
354
+ throttled('second')
355
+ throttled('third')
356
356
 
357
357
  expect(fn).toHaveBeenCalledTimes(1)
358
- expect(fn).toHaveBeenCalledWith("first")
358
+ expect(fn).toHaveBeenCalledWith('first')
359
359
 
360
360
  vi.advanceTimersByTime(200)
361
361
  expect(fn).toHaveBeenCalledTimes(1)
362
362
  })
363
363
 
364
- it("should skip leading call when leading: false", () => {
364
+ it('should skip leading call when leading: false', () => {
365
365
  const fn = vi.fn()
366
366
  const throttled = throttle(fn, 100, { leading: false })
367
367
 
368
- throttled("first")
368
+ throttled('first')
369
369
  expect(fn).toHaveBeenCalledTimes(0)
370
370
 
371
371
  vi.advanceTimersByTime(100)
372
372
  expect(fn).toHaveBeenCalledTimes(1)
373
- expect(fn).toHaveBeenCalledWith("first")
373
+ expect(fn).toHaveBeenCalledWith('first')
374
374
  })
375
375
 
376
- it("should support leading: false with trailing: true (default)", () => {
376
+ it('should support leading: false with trailing: true (default)', () => {
377
377
  const fn = vi.fn()
378
378
  const throttled = throttle(fn, 100, { leading: false })
379
379
 
380
- throttled("a")
381
- throttled("b")
380
+ throttled('a')
381
+ throttled('b')
382
382
  expect(fn).toHaveBeenCalledTimes(0)
383
383
 
384
384
  vi.advanceTimersByTime(100)
385
385
  expect(fn).toHaveBeenCalledTimes(1)
386
- expect(fn).toHaveBeenCalledWith("b")
386
+ expect(fn).toHaveBeenCalledWith('b')
387
387
  })
388
388
 
389
- it("should still fire leading call after cooldown with trailing: false", () => {
389
+ it('should still fire leading call after cooldown with trailing: false', () => {
390
390
  const fn = vi.fn()
391
391
  const throttled = throttle(fn, 100, { trailing: false })
392
392
 
393
- throttled("first")
393
+ throttled('first')
394
394
  expect(fn).toHaveBeenCalledTimes(1)
395
395
 
396
396
  vi.advanceTimersByTime(100)
397
- throttled("second")
397
+ throttled('second')
398
398
  expect(fn).toHaveBeenCalledTimes(2)
399
- expect(fn).toHaveBeenLastCalledWith("second")
399
+ expect(fn).toHaveBeenLastCalledWith('second')
400
400
  })
401
401
 
402
- it("should not fire trailing call when cancelled before timer fires", () => {
402
+ it('should not fire trailing call when cancelled before timer fires', () => {
403
403
  const fn = vi.fn()
404
404
  const throttled = throttle(fn, 100)
405
405
 
406
406
  // Leading call fires immediately
407
- throttled("first")
407
+ throttled('first')
408
408
  expect(fn).toHaveBeenCalledTimes(1)
409
409
 
410
410
  // Call again within wait period — queues trailing
411
- throttled("second")
411
+ throttled('second')
412
412
 
413
413
  // Cancel before trailing timer fires
414
414
  throttled.cancel()
@@ -418,11 +418,11 @@ describe("throttle", () => {
418
418
  expect(fn).toHaveBeenCalledTimes(1)
419
419
  })
420
420
 
421
- it("should handle leading: false with trailing: false (no calls fire)", () => {
421
+ it('should handle leading: false with trailing: false (no calls fire)', () => {
422
422
  const fn = vi.fn()
423
423
  const throttled = throttle(fn, 100, { leading: false, trailing: false })
424
424
 
425
- throttled("a")
425
+ throttled('a')
426
426
  expect(fn).toHaveBeenCalledTimes(0)
427
427
 
428
428
  vi.advanceTimersByTime(200)
@@ -433,51 +433,51 @@ describe("throttle", () => {
433
433
  // --------------------------------------------------------
434
434
  // merge
435
435
  // --------------------------------------------------------
436
- describe("merge", () => {
437
- it("should deep merge two objects", () => {
436
+ describe('merge', () => {
437
+ it('should deep merge two objects', () => {
438
438
  const target = { a: { b: 1, c: 2 } }
439
439
  const source = { a: { c: 3, d: 4 } }
440
440
  expect(merge({ ...target }, source)).toEqual({ a: { b: 1, c: 3, d: 4 } })
441
441
  })
442
442
 
443
- it("should replace arrays instead of merging them", () => {
443
+ it('should replace arrays instead of merging them', () => {
444
444
  const target = { items: [1, 2, 3] }
445
445
  const source = { items: [4, 5] }
446
446
  expect(merge({ ...target }, source)).toEqual({ items: [4, 5] })
447
447
  })
448
448
 
449
- it("should handle multiple sources", () => {
449
+ it('should handle multiple sources', () => {
450
450
  const result = merge({ a: 1 }, { b: 2 }, { c: 3 })
451
451
  expect(result).toEqual({ a: 1, b: 2, c: 3 })
452
452
  })
453
453
 
454
- it("should overwrite primitive values", () => {
454
+ it('should overwrite primitive values', () => {
455
455
  expect(merge({ a: 1 }, { a: 2 })).toEqual({ a: 2 })
456
456
  })
457
457
 
458
- it("should skip null sources", () => {
458
+ it('should skip null sources', () => {
459
459
  const target = { a: 1 }
460
460
  expect(merge(target, null as any)).toEqual({ a: 1 })
461
461
  })
462
462
 
463
- it("should skip undefined sources", () => {
463
+ it('should skip undefined sources', () => {
464
464
  const target = { a: 1 }
465
465
  expect(merge(target, undefined as any)).toEqual({ a: 1 })
466
466
  })
467
467
 
468
- it("should not merge non-plain objects deeply", () => {
468
+ it('should not merge non-plain objects deeply', () => {
469
469
  const date = new Date()
470
470
  const result = merge({} as any, { d: date })
471
471
  expect(result.d).toBe(date)
472
472
  })
473
473
 
474
- it("should return the target object (mutates)", () => {
474
+ it('should return the target object (mutates)', () => {
475
475
  const target = { a: 1 }
476
476
  const result = merge(target, { b: 2 })
477
477
  expect(result).toBe(target)
478
478
  })
479
479
 
480
- it("should deeply merge nested objects", () => {
480
+ it('should deeply merge nested objects', () => {
481
481
  const target = { a: { b: { c: 1 } } }
482
482
  const source = { a: { b: { d: 2 } } }
483
483
  expect(merge({ ...target }, source)).toEqual({
@@ -486,39 +486,39 @@ describe("merge", () => {
486
486
  })
487
487
 
488
488
  // Security: prototype pollution protection
489
- it("should not pollute Object.prototype via __proto__", () => {
489
+ it('should not pollute Object.prototype via __proto__', () => {
490
490
  const malicious = JSON.parse('{"__proto__": {"polluted": true}}')
491
491
  merge({}, malicious)
492
492
  expect(({} as any).polluted).toBeUndefined()
493
493
  })
494
494
 
495
- it("should not pollute via constructor.prototype", () => {
495
+ it('should not pollute via constructor.prototype', () => {
496
496
  const malicious = JSON.parse('{"constructor": {"prototype": {"polluted": true}}}')
497
497
  merge({}, malicious)
498
498
  expect(({} as any).polluted).toBeUndefined()
499
499
  })
500
500
 
501
- it("should not pollute via prototype key", () => {
501
+ it('should not pollute via prototype key', () => {
502
502
  const malicious = { prototype: { polluted: true } }
503
503
  merge({}, malicious)
504
504
  expect(({} as any).polluted).toBeUndefined()
505
505
  })
506
506
 
507
- it("should overwrite target plain object with source array", () => {
507
+ it('should overwrite target plain object with source array', () => {
508
508
  const target = { a: { b: 1 } }
509
509
  const source = { a: [1, 2, 3] }
510
510
  const result = merge({ ...target }, source as any)
511
511
  expect(result.a).toEqual([1, 2, 3])
512
512
  })
513
513
 
514
- it("should overwrite target array with source plain object", () => {
514
+ it('should overwrite target array with source plain object', () => {
515
515
  const target = { a: [1, 2] } as any
516
- const source = { a: { key: "value" } }
516
+ const source = { a: { key: 'value' } }
517
517
  const result = merge({ ...target }, source)
518
- expect(result.a).toEqual({ key: "value" })
518
+ expect(result.a).toEqual({ key: 'value' })
519
519
  })
520
520
 
521
- it("should handle source with class instances (non-plain objects)", () => {
521
+ it('should handle source with class instances (non-plain objects)', () => {
522
522
  class MyClass {
523
523
  x = 1
524
524
  }
@@ -529,7 +529,7 @@ describe("merge", () => {
529
529
  expect(result.a).toBe(instance)
530
530
  })
531
531
 
532
- it("should handle empty sources array", () => {
532
+ it('should handle empty sources array', () => {
533
533
  const target = { a: 1 }
534
534
  const result = merge(target)
535
535
  expect(result).toEqual({ a: 1 })