@phun-ky/defaults-deep 2.0.7 โ†’ 2.0.9

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/CHANGELOG.md ADDED
@@ -0,0 +1,225 @@
1
+ # Changelog
2
+
3
+ ## [2.0.9](https://github.com/phun-ky/defaults-deep/compare/2.0.8...2.0.9) (2026-10-25)
4
+
5
+ ### Tasks
6
+
7
+ * ๐Ÿค– lint everything, typecheck strictly, drop unused tooling ([cc50f38](https://github.com/phun-ky/defaults-deep/commit/cc50f38a388a4a9442d0754b8feb22fb2a753644)), closes [#97](https://github.com/phun-ky/defaults-deep/issues/97) [#102](https://github.com/phun-ky/defaults-deep/issues/102), references [#110](https://github.com/phun-ky/defaults-deep/issues/110) [#98](https://github.com/phun-ky/defaults-deep/issues/98) [#99](https://github.com/phun-ky/defaults-deep/issues/99) [#101](https://github.com/phun-ky/defaults-deep/issues/101)
8
+ * ๐Ÿค– raise coverage thresholds to the new floor ([c740289](https://github.com/phun-ky/defaults-deep/commit/c740289452c5915f415b59822ae35a0afa87948a))
9
+
10
+ ### Documentation
11
+
12
+ * โœ๏ธ describe mergeWith as internal in the API comment ([49db942](https://github.com/phun-ky/defaults-deep/commit/49db942ca1a2af576adec778b4cafe6c5e89cd5a))
13
+
14
+ ### Bug
15
+
16
+ * ๐Ÿ› give keys that share a source object their own merged objects ([b2de955](https://github.com/phun-ky/defaults-deep/commit/b2de955f8a6d1fedab0171b08564744736101278)), closes [#110](https://github.com/phun-ky/defaults-deep/issues/110)
17
+
18
+ ## [2.0.8](https://github.com/phun-ky/defaults-deep/compare/2.0.7...2.0.8) (2026-10-25)
19
+
20
+ ### Tasks
21
+
22
+ * ๐Ÿค– move to release-it 21 and conventional-changelog 12 ([7986d81](https://github.com/phun-ky/defaults-deep/commit/7986d818a0d9df5e027177a78124420f7c9d19e4)), references [#94](https://github.com/phun-ky/defaults-deep/issues/94)
23
+
24
+ ### Bug
25
+
26
+ * ๐Ÿ› publish correct CommonJS types, the licences and an unbundled build ([2ecd73f](https://github.com/phun-ky/defaults-deep/commit/2ecd73f0507c1ea7e3239f2b4dd055bd69322964)), closes [#88](https://github.com/phun-ky/defaults-deep/issues/88) [#89](https://github.com/phun-ky/defaults-deep/issues/89) [#90](https://github.com/phun-ky/defaults-deep/issues/90) [#92](https://github.com/phun-ky/defaults-deep/issues/92)
27
+
28
+ ## [2.0.7](https://github.com/phun-ky/defaults-deep/compare/2.0.6...2.0.7) (2026-10-13)
29
+
30
+ ### Tasks
31
+
32
+ * ๐Ÿค– bump the minor-and-patch group across 1 directory with 9 updates ([1909cb7](https://github.com/phun-ky/defaults-deep/commit/1909cb715b28051e96a99e7f0d6df67436a2cfc2))
33
+
34
+ ## [2.0.6](https://github.com/phun-ky/defaults-deep/compare/2.0.5...2.0.6) (2026-06-14)
35
+
36
+ ### Tasks
37
+
38
+ * ๐Ÿค– bump the minor-and-patch group with 2 updates ([ef680eb](https://github.com/phun-ky/defaults-deep/commit/ef680eb152bf8d9188f691cd2fe8867b50410497))
39
+
40
+ ## [2.0.5](https://github.com/phun-ky/defaults-deep/compare/2.0.4...2.0.5) (2026-06-08)
41
+
42
+ ### Tasks
43
+
44
+ * ๐Ÿค– Update dependencies ([1141f9a](https://github.com/phun-ky/defaults-deep/commit/1141f9aedab989fa1470883b929104f99f80a840))
45
+
46
+ ### Bug
47
+
48
+ * ๐Ÿ› Mitigate prototype pollution vulnerability ([807dba9](https://github.com/phun-ky/defaults-deep/commit/807dba930f8718f9126cad59d949b8fd3539b059))
49
+
50
+ ## [2.0.4](https://github.com/phun-ky/defaults-deep/compare/2.0.3...2.0.4) (2026-06-04)
51
+
52
+ ### Tasks
53
+
54
+ * ๐Ÿค– bump the minor-and-patch group across 1 directory with 8 updates ([02114fc](https://github.com/phun-ky/defaults-deep/commit/02114fcbacbfb0654c31f6b35b214909f5c4cbbd))
55
+
56
+ ## [2.0.3](https://github.com/phun-ky/defaults-deep/compare/2.0.2...2.0.3) (2026-05-22)
57
+
58
+ ### Tasks
59
+
60
+ * ๐Ÿค– bump the minor-and-patch group with 4 updates ([5f44f25](https://github.com/phun-ky/defaults-deep/commit/5f44f25d873162709d11ef1a6c50afb33aee84b5))
61
+
62
+ ## [2.0.2](https://github.com/phun-ky/defaults-deep/compare/2.0.1...2.0.2) (2026-05-15)
63
+
64
+ ### Tasks
65
+
66
+ * ๐Ÿค– bump @phun-ky/typeof ([9ad2eb5](https://github.com/phun-ky/defaults-deep/commit/9ad2eb590fb6ce48036d5250ca0e9104e9b09e93))
67
+
68
+ ## [2.0.1](https://github.com/phun-ky/defaults-deep/compare/2.0.0...2.0.1) (2026-05-15)
69
+
70
+ ### Tasks
71
+
72
+ * ๐Ÿค– bump the minor-and-patch group with 6 updates ([e8dc20c](https://github.com/phun-ky/defaults-deep/commit/e8dc20cc3a895fdddd81c4db60ea65d3ef9fecca))
73
+
74
+ ## [2.0.0](https://github.com/phun-ky/defaults-deep/compare/1.1.19...2.0.0) (2026-05-13)
75
+
76
+ ### โš  BREAKING CHANGES
77
+
78
+ * ๐Ÿงจ Drops support for Node.js v20 and older
79
+
80
+ ### Tasks
81
+
82
+ * ๐Ÿค– bump the major-updates group across 1 directory with 3 updates ([7fb305d](https://github.com/phun-ky/defaults-deep/commit/7fb305db2d5e154ec6b4493d2791acc88b74bc49))
83
+ * ๐Ÿค– Update development tooling requirements ([f535d4f](https://github.com/phun-ky/defaults-deep/commit/f535d4f1ad520b256caa0061c5823b4cd779e86c))
84
+
85
+ ## [1.1.19](https://github.com/phun-ky/defaults-deep/compare/1.1.18...1.1.19) (2026-05-13)
86
+
87
+ ### Tasks
88
+
89
+ * ๐Ÿค– bump ip-address from 10.1.0 to 10.2.0 ([0543aaf](https://github.com/phun-ky/defaults-deep/commit/0543aaf8c77b68ec05b9661f1a7f0680d203fecf))
90
+
91
+ ## [1.1.18](https://github.com/phun-ky/defaults-deep/compare/1.1.17...1.1.18) (2026-05-13)
92
+
93
+ ### Tasks
94
+
95
+ * ๐Ÿค– bump postcss from 8.5.9 to 8.5.14 ([37846d0](https://github.com/phun-ky/defaults-deep/commit/37846d0d270762c8798827642a23278d7a5c9b87))
96
+
97
+ ## [1.1.17](https://github.com/phun-ky/defaults-deep/compare/1.1.16...1.1.17) (2026-05-11)
98
+
99
+ ### Tasks
100
+
101
+ * ๐Ÿค– bump fast-uri from 3.1.0 to 3.1.2 ([434e67d](https://github.com/phun-ky/defaults-deep/commit/434e67d56eafe81db8b89589879dab866915523d))
102
+
103
+ ## [1.1.16](https://github.com/phun-ky/defaults-deep/compare/1.1.15...1.1.16) (2026-04-27)
104
+
105
+ ### Tasks
106
+
107
+ * ๐Ÿค– bump the minor-and-patch group across 1 directory with 5 updates ([0100284](https://github.com/phun-ky/defaults-deep/commit/01002847c27593f399a126892e9fd7e4c4150cb0))
108
+
109
+ ## [1.1.15](https://github.com/phun-ky/defaults-deep/compare/1.1.14...1.1.15) (2026-04-14)
110
+
111
+ ### Tasks
112
+
113
+ * ๐Ÿค– bump the major-updates group across 1 directory with 3 updates ([ca944fd](https://github.com/phun-ky/defaults-deep/commit/ca944fda9148bb85a6bb3ac5211870ee5d3e8753))
114
+ * ๐Ÿค– Update tsconfig ([fc35211](https://github.com/phun-ky/defaults-deep/commit/fc35211bd17921a0e42b2d00dcdfd3101591956f))
115
+
116
+ ## [1.1.14](https://github.com/phun-ky/defaults-deep/compare/1.1.13...1.1.14) (2026-04-14)
117
+
118
+ ### Tasks
119
+
120
+ * ๐Ÿค– bump lodash from 4.17.23 to 4.18.1 ([83e3c45](https://github.com/phun-ky/defaults-deep/commit/83e3c45cf52692194c78dbdc7808e03cd9bb8a11))
121
+
122
+ ## [1.1.13](https://github.com/phun-ky/defaults-deep/compare/1.1.12...1.1.13) (2026-03-20)
123
+
124
+ ### Tasks
125
+
126
+ * ๐Ÿค– bump the minor-and-patch group with 4 updates ([1ef4089](https://github.com/phun-ky/defaults-deep/commit/1ef4089ba36c94887663b5a200b25181f3d31a7b))
127
+
128
+ ## [1.1.12](https://github.com/phun-ky/defaults-deep/compare/1.1.11...1.1.12) (2026-03-13)
129
+
130
+ ### Tasks
131
+
132
+ * ๐Ÿค– bump the minor-and-patch group with 5 updates ([d3696ab](https://github.com/phun-ky/defaults-deep/commit/d3696ab182e0ede72add2020572ecb686c9cf0ca))
133
+
134
+ ## [1.1.11](https://github.com/phun-ky/defaults-deep/compare/1.1.10...1.1.11) (2026-03-09)
135
+
136
+ ### Tasks
137
+
138
+ * ๐Ÿค– bump @rollup/plugin-terser in the major-updates group ([9cb5e80](https://github.com/phun-ky/defaults-deep/commit/9cb5e80e02e9892229fb4e69ee11a7252b878063))
139
+
140
+ ## [1.1.10](https://github.com/phun-ky/defaults-deep/compare/1.1.9...1.1.10) (2026-03-09)
141
+
142
+ ### Tasks
143
+
144
+ * ๐Ÿค– bump the minor-and-patch group with 2 updates ([713d70e](https://github.com/phun-ky/defaults-deep/commit/713d70ee78d3a30a965e92887fb9b5785c0acd09))
145
+
146
+ ## [1.1.9](https://github.com/phun-ky/defaults-deep/compare/1.1.8...1.1.9) (2026-03-02)
147
+
148
+ ### Tasks
149
+
150
+ * ๐Ÿค– bump minimatch from 3.1.2 to 3.1.5 ([f5ab510](https://github.com/phun-ky/defaults-deep/commit/f5ab5103bdfd28c5efe29b239f2916e61fceccc1))
151
+
152
+ ## [1.1.8](https://github.com/phun-ky/defaults-deep/compare/1.1.7...1.1.8) (2026-03-02)
153
+
154
+ ### Tasks
155
+
156
+ * ๐Ÿค– bump the minor-and-patch group across 1 directory with 7 updates ([4b63d5f](https://github.com/phun-ky/defaults-deep/commit/4b63d5fd0909f345e934e61a0095dcfa107a71e5))
157
+
158
+ ## [1.1.7](https://github.com/phun-ky/defaults-deep/compare/1.1.6...1.1.7) (2026-03-02)
159
+
160
+ ### Tasks
161
+
162
+ * ๐Ÿค– bump basic-ftp from 5.1.0 to 5.2.0 ([8ae4b30](https://github.com/phun-ky/defaults-deep/commit/8ae4b30042465ba70267b785eac27e052d8e1e1f))
163
+
164
+ ## [1.1.6](https://github.com/phun-ky/defaults-deep/compare/1.1.5...1.1.6) (2026-03-02)
165
+
166
+ ### Tasks
167
+
168
+ * ๐Ÿค– bump rollup from 4.57.1 to 4.59.0 ([673f69a](https://github.com/phun-ky/defaults-deep/commit/673f69a942045946b17e99fb21dea513c448c207))
169
+
170
+ ## [1.1.5](https://github.com/phun-ky/defaults-deep/compare/1.1.4...1.1.5) (2026-02-23)
171
+
172
+ ### Tasks
173
+
174
+ * ๐Ÿค– bump putout in the major-updates group across 1 directory ([d61c776](https://github.com/phun-ky/defaults-deep/commit/d61c7764a3e2275dd68679a946babb1a67b1072c))
175
+
176
+ ## [1.1.4](https://github.com/phun-ky/defaults-deep/compare/1.1.3...1.1.4) (2026-02-12)
177
+
178
+ ### Tasks
179
+
180
+ * ๐Ÿค– bump the minor-and-patch group with 9 updates ([580f5c2](https://github.com/phun-ky/defaults-deep/commit/580f5c2db9a377345b7655e440807624525f0077))
181
+
182
+ ## [1.1.3](https://github.com/phun-ky/defaults-deep/compare/1.1.2...1.1.3) (2026-02-10)
183
+
184
+ ### Tasks
185
+
186
+ * ๐Ÿค– bump the major-updates group across 1 directory with 4 updates ([b333048](https://github.com/phun-ky/defaults-deep/commit/b333048891a51bc20e02f9396e9fe45ebb665370))
187
+
188
+ ## [1.1.2](https://github.com/phun-ky/defaults-deep/compare/1.1.1...1.1.2) (2026-01-22)
189
+
190
+ ### Bug
191
+
192
+ * Trying something ([e25157b](https://github.com/phun-ky/defaults-deep/commit/e25157b4b0fbc7d93aecf82337152b832e8e7502))
193
+
194
+ ## [1.1.1](https://github.com/phun-ky/defaults-deep/compare/1.1.0...1.1.1) (2026-01-22)
195
+
196
+ ### Tasks
197
+
198
+ * remove old tokens ref ([74f7503](https://github.com/phun-ky/defaults-deep/commit/74f75038f289d752fd5ad6c2f63b327f7a6388ca))
199
+
200
+ ## 1.1.0 (2026-01-22)
201
+
202
+ ### Tasks
203
+
204
+ * ugprade release it ([1028038](https://github.com/phun-ky/defaults-deep/commit/102803878f8e7c8ba0db3ce4858b4ee1f767edc5))
205
+
206
+ ### Documentation
207
+
208
+ * โœ๏ธ Update docs ([cc9eec8](https://github.com/phun-ky/defaults-deep/commit/cc9eec8f37f17f24875835b1788a03030b81f3c1))
209
+
210
+ ### Feature
211
+
212
+ * ๐ŸŽธ Remove lodash as a direct dependency ([f570cd6](https://github.com/phun-ky/defaults-deep/commit/f570cd6ab1827c194bce3f36f6c54d1cdebc4f83))
213
+
214
+ ### Bug
215
+
216
+ * ๐Ÿ› cleanup package.json ([a53d222](https://github.com/phun-ky/defaults-deep/commit/a53d222e3dfe4fe38f4b1461c673eb28ab0ea170))
217
+ * ๐Ÿ› Trying to fix release ([84fd12b](https://github.com/phun-ky/defaults-deep/commit/84fd12b72968652a42703cd3db014df22c152272))
218
+ * testing ([db951c4](https://github.com/phun-ky/defaults-deep/commit/db951c41a9de12109c21aa6e5db8ad28c40319a6))
219
+ * testing ([0485486](https://github.com/phun-ky/defaults-deep/commit/0485486818d5338f21b668a99a53f7afd4dfb5cb))
220
+ * testing ([daea6e5](https://github.com/phun-ky/defaults-deep/commit/daea6e5c89709742de5150d8800b2513440efa53))
221
+ * testing ([f5b6f5b](https://github.com/phun-ky/defaults-deep/commit/f5b6f5b9f6cd26ea2aa6af196b34cc111d8a043d))
222
+
223
+ ### Refactoring
224
+
225
+ * ๐Ÿ’ก Make sure we have commonjs support ([ef61f2f](https://github.com/phun-ky/defaults-deep/commit/ef61f2f0d11b64ddda05a9c103f5a0dced863069))
package/LICENSE-ISC ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright ยฉ 2016 Drew Llewellyn
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
package/LICENSE-MIT ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright ยฉ 2026 Alexander Vassbotn Rรธyne-Helgesen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -6,11 +6,11 @@
6
6
 
7
7
  Similar to lodash's defaultsDeep, but without mutating the source object, and no merging of arrays, and no lodash as dependency!
8
8
 
9
- ## Table of Contents<!-- omit from toc -->- [defaults-deep](#defaults-deep)
9
+ ## Table of Contents<!-- omit from toc -->
10
10
 
11
11
  - [@phun-ky/defaults-deep](#phun-kydefaults-deep)
12
12
  - [About](#about)
13
- - [Table of Contents- defaults-deep](#table-of-contents--defaults-deep)
13
+ - [Requirements](#requirements)
14
14
  - [Installation](#installation)
15
15
  - [Usage](#usage)
16
16
  - [ESM](#esm)
@@ -26,6 +26,10 @@ Similar to lodash's defaultsDeep, but without mutating the source object, and no
26
26
  - [Changelog](#changelog)
27
27
  - [Sponsor me](#sponsor-me)
28
28
 
29
+ ## Requirements
30
+
31
+ Node.js 22.9 or later. The package ships an ES module and a CommonJS build, each with its own TypeScript types, and works in browsers through a bundler.
32
+
29
33
  ## Installation
30
34
 
31
35
  Install the package via `npm`:
@@ -38,14 +42,14 @@ npm i --save @phun-ky/defaults-deep
38
42
 
39
43
  ### ESM
40
44
 
41
- ```bash
42
- import defaultsDeep from "@phun-ky/defaults-deep";
45
+ ```js
46
+ import defaultsDeep from '@phun-ky/defaults-deep';
43
47
  ```
44
48
 
45
49
  ### CommonJS
46
50
 
47
- ```
48
- const defaultsDeep = require("@phun-ky/defaults-deep");
51
+ ```js
52
+ const defaultsDeep = require('@phun-ky/defaults-deep');
49
53
  ```
50
54
 
51
55
  ## API
@@ -67,7 +71,7 @@ but implemented in vanilla TypeScript with a small, predictable merge core.
67
71
  values from the first argument are treated as "defaults" that should _not_ be overwritten
68
72
  by later arguments (because later arguments are merged first).
69
73
  - Only **object-like** sources participate in merging. Non-objects are skipped via `isObjectLoose`.
70
- - Uses [mergeWith](utils/merge-with.md#mergewith) for the deep merge logic.
74
+ - Uses an internal `mergeWith` for the deep merge logic.
71
75
  - **Arrays are preserved by replacement**: if a source value is an array, it replaces the
72
76
  destination value at that key (rather than being merged element-by-element).
73
77
 
@@ -102,7 +106,7 @@ defaultsDeep({ opts: { retry: 3 } }, null, { opts: { timeout: 1000 } });
102
106
 
103
107
  ## Differences from lodash (`_.defaultsDeep` / `_.mergeWith` pattern)
104
108
 
105
- This package is inspired by lodash's common `_.defaultsDeep` approach (often implemented as `_.toArray(arguments).reverse()` + `_.mergeWith(...)`), but it is **not a drop-in, byte-for-byte compatible clone**. The goal here is to cover the common "configuration defaults" use case with a much smaller, dependency-free implementation. For typical plain objects (JSON-like data) and arrays, the results should match what you expect from the lodash approach.
109
+ This package is inspired by lodash's common `_.defaultsDeep` approach (often implemented as `_.toArray(arguments).reverse()` + `_.mergeWith(...)`), but it is **not a drop-in, byte-for-byte compatible clone**. The goal here is to cover the common "configuration defaults" use case with a much smaller implementation, whose only dependency is the small type-check library `@phun-ky/typeof`. For typical plain objects (JSON-like data) and arrays, the results should match what you expect from the lodash approach.
106
110
 
107
111
  The biggest difference is the underlying merge engine. Lodash's `mergeWith` is a mature, highly general deep-merge with lots of special-case behaviour for many JavaScript value types and edge cases. This implementation intentionally keeps the default rules conservative: it only deep-merges **plain objects** and treats most other things as **replace-by-assignment**. That means values like arrays, dates, maps/sets, functions, class instances, and other non-plain objects are not recursively merged unless you explicitly handle them via a customizer.
108
112
 
@@ -2,7 +2,7 @@
2
2
  * @phun-ky/defaults-deep
3
3
  * Like lodash's _.defaultsDeep, but with array preservation
4
4
  * @author Alexander Vassbotn Rรธyne-Helgesen <alexander@phun-ky.net>
5
- * @version 2.0.7
5
+ * @version 2.0.9
6
6
  * @license
7
7
  * Copyright (c) 2026 Alexander Vassbotn Rรธyne-Helgesen
8
8
  *
@@ -40,31 +40,5 @@
40
40
  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
41
41
  * PERFORMANCE OF THIS SOFTWARE.
42
42
  */
43
- "use strict";
44
- /**
45
- * @phun-ky/typeof
46
- * A set of JavaScript helper functions to check for types
47
- * @author Alexander Vassbotn Rรธyne-Helgesen <alexander@phun-ky.net>
48
- * @version 3.0.7
49
- * @license
50
- * Copyright (c) 2024 Alexander Vassbotn Rรธyne-Helgesen
51
- *
52
- * Permission is hereby granted, free of charge, to any person obtaining a copy
53
- * of this software and associated documentation files (the "Software"), to deal
54
- * in the Software without restriction, including without limitation the rights
55
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
56
- * copies of the Software, and to permit persons to whom the Software is
57
- * furnished to do so, subject to the following conditions:
58
- *
59
- * The above copyright notice and this permission notice shall be included in all
60
- * copies or substantial portions of the Software.
61
- *
62
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
63
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
64
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
65
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
66
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
67
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
68
- * SOFTWARE.
69
- */function t(t){return!function(t){return void 0===t}(t)}function o(t){if("[object Object]"!==Object.prototype.toString.call(t))return!1;const o=Object.getPrototypeOf(t);return o===Object.prototype||null===o}function e(t){const o=typeof t;return null!==t&&("object"===o||"function"===o)}function n(t,...o){const e=o.at(-1),n="function"==typeof e?e:void 0;const c=n?o.slice(0,-1):o,i=new WeakMap;for(const o of c)r(t,o,n,i);return t}const c=new Set(["__proto__","constructor","prototype"]),r=(n,s,f,u)=>{if(e(s))for(const e of i(s)){if("string"==typeof e&&c.has(e))continue;const i=s[e],p=n[e],l=f?.(p,i,e,n,s,u);if(t(l))n[e]=l;else{if(o(p)&&o(i)){const t=u.get(i);if(t){n[e]=t;continue}u.set(i,p),r(p,i,f,u);continue}if(o(i)){const t=u.get(i);if(t){n[e]=t;continue}const c=o(p)?p:{};n[e]=c,u.set(i,c),r(c,i,f,u);continue}n[e]=i}}},i=t=>[...Object.keys(t),...Object.getOwnPropertySymbols(t).filter(o=>Object.prototype.propertyIsEnumerable.call(t,o))];module.exports=(...t)=>{const o={};for(const c of t.slice().reverse())e(c)&&n(o,c,(t,o)=>Array.isArray(o)?o:void 0);return o};
43
+ "use strict";var e=require("@phun-ky/typeof");function t(t,...o){const i=o.at(-1),s=e.isFunction(i)?i:void 0,c=s?o.slice(0,-1):o,r=new WeakMap;for(const e of c)n(t,e,s,r);return t}const o=new Set(["__proto__","constructor","prototype"]),n=(t,s,c,r)=>{if(e.isObjectLoose(s))for(const f of i(s)){if("string"==typeof f&&o.has(f))continue;const i=s[f],l=t[f],u=c?.(l,i,f,t,s,r);if(e.isNotUndefined(u))t[f]=u;else{if(e.isObjectPlain(l)&&e.isObjectPlain(i)){const e=r.get(i);if(e){t[f]=e;continue}r.set(i,l),n(l,i,c,r),r.delete(i);continue}if(e.isObjectPlain(i)){const o=r.get(i);if(o){t[f]=o;continue}const s=e.isObjectPlain(l)?l:{};t[f]=s,r.set(i,s),n(s,i,c,r),r.delete(i);continue}t[f]=i}}},i=e=>[...Object.keys(e),...Object.getOwnPropertySymbols(e).filter(t=>Object.prototype.propertyIsEnumerable.call(e,t))];module.exports=(...o)=>{const n={};for(const i of o.slice().reverse())e.isObjectLoose(i)&&t(n,i,(e,t)=>Array.isArray(t)?t:void 0);return n};
70
44
  //# sourceMappingURL=defaults-deep.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"defaults-deep.cjs","sources":["../node_modules/@phun-ky/typeof/dist/typeof.js","../src/utils/merge-with.ts","../src/main.ts"],"sourcesContent":["/**\n* @phun-ky/typeof\n* A set of JavaScript helper functions to check for types\n* @author Alexander Vassbotn Rรธyne-Helgesen <alexander@phun-ky.net>\n* @version 3.0.7\n* @license\n* Copyright (c) 2024 Alexander Vassbotn Rรธyne-Helgesen\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the \"Software\"), to deal\n* in the Software without restriction, including without limitation the rights\n* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n* copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in all\n* copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n* SOFTWARE.\n*/\nfunction t(t){return\"string\"==typeof t}function r(r){return!t(r)}function n(t){return\"number\"==typeof t}function e(t){return!n(t)}function o(t){return\"boolean\"==typeof t}function c(t){return!o(t)}function u(t){return void 0===t}function f(t){return!u(t)}function i(t){return!u(t)}function p(t){if(\"[object Object]\"!==Object.prototype.toString.call(t))return!1;const r=Object.getPrototypeOf(t);return r===Object.prototype||null===r}function y(t){if(\"object\"!=typeof t||null===t)return!1;if(\"[object Object]\"!==Object.prototype.toString.call(t))return!1;const r=Object.getPrototypeOf(t);if(null===r)return!0;const n=Object.prototype.hasOwnProperty.call(r,\"constructor\")?r.constructor:null;return\"function\"==typeof n&&n instanceof n&&Function.prototype.call(n)===Function.prototype.call(t)}function l(t){const r=typeof t;return null!==t&&(\"object\"===r||\"function\"===r)}function a(t){if(\"function\"!=typeof t)return!1;if(b(t))return!1;try{const r=Object.getOwnPropertyDescriptor(t,\"prototype\");return!!r&&!r.writable}catch{return!1}}function b(t){if(\"function\"!=typeof t)return!1;return[Object,Array,Function,String,Number,Boolean,Date,RegExp,Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError,Map,WeakMap,Set,WeakSet,Promise].includes(t)}const O=new Set([Object,Array,Function,String,Number,Boolean,Date,RegExp,Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError,Map,WeakMap,Set,WeakSet,Promise,BigInt,Symbol]);function j(t){return\"function\"==typeof t&&O.has(t)}function E(t){return\"object\"==typeof t&&null!==t&&Object.getPrototypeOf(t)!==Object.prototype&&null!==Object.getPrototypeOf(t)}function s(t){return\"function\"==typeof t}export{o as isBoolean,j as isBuiltInCallable,b as isBuiltInConstructor,a as isClass,i as isDefined,s as isFunction,E as isInstanceOfUnknownClass,c as isNotBoolean,e as isNotNumber,r as isNotString,f as isNotUndefined,n as isNumber,l as isObjectLoose,p as isObjectPlain,y as isObjectStrict,t as isString,u as isUndefined};\n//# sourceMappingURL=typeof.js.map\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n isFunction,\n isNotUndefined,\n isObjectLoose,\n isObjectPlain\n} from '@phun-ky/typeof';\n\n/* node:coverage disable */\n/**\n * Customizer function used by {@link mergeWith} to override merge behaviour.\n *\n * If the customizer returns `undefined`, {@link mergeWith} falls back to its default\n * deep-merge rules. Any other return value will be assigned to the destination key.\n *\n * @param objValue - Current value on the destination (`object`) for `key`.\n * @param srcValue - Incoming value from the current `source` for `key`.\n * @param key - Property key being merged (string or symbol).\n * @param object - Destination object being mutated/merged into.\n * @param source - Current source object being merged from.\n * @param stack - A `WeakMap` used internally to prevent infinite recursion on circular references.\n * @returns The value to assign for this key, or `undefined` to use default merge behaviour.\n *\n * @example\n * ```ts\n * import { mergeWith, type MergeWithCustomizer } from \"./mergeWith\";\n *\n * const concatArrays: MergeWithCustomizer = (objValue, srcValue) => {\n * if (Array.isArray(objValue) && Array.isArray(srcValue)) {\n * return objValue.concat(srcValue);\n * }\n * return undefined;\n * };\n *\n * const a = { list: [1] };\n * const b = { list: [2] };\n *\n * mergeWith(a, b, concatArrays);\n * // => { list: [1, 2] }\n * ```\n */\n/* node:coverage enable */\nexport type MergeWithCustomizer = (\n objValue: unknown,\n srcValue: unknown,\n key: string | symbol,\n object: Record<PropertyKey, unknown>,\n source: Record<PropertyKey, unknown>,\n stack: WeakMap<object, object>\n) => unknown;\n\ntype UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (\n k: infer I\n) => void\n ? I\n : never;\n\ntype MergeResult<T, S extends readonly any[]> = T &\n UnionToIntersection<S[number]>;\n\n/**\n * Deep-merges one or more source objects into a destination object, with optional customization.\n *\n * This is a \"vanilla TS\" alternative to lodash's `mergeWith` with a deliberately conservative\n * default strategy:\n *\n * - **Mutates** and returns the destination `object`.\n * - Enumerates **own enumerable string keys** and **own enumerable symbol keys** from each source.\n * - When both destination and source values are **plain objects**, merges recursively.\n * - When the source value is a **plain object** but the destination is not, creates `{}` and merges into it.\n * - All other value types (arrays, dates, maps, sets, functions, primitives, class instances, etc.)\n * are assigned by **replacement** unless the customizer handles them.\n * - Circular references in **sources** are prevented from causing infinite recursion via a `WeakMap` stack.\n *\n * @typeParam T - Destination object type.\n * @typeParam S - Tuple/array of source object types.\n *\n * @param object - Destination object to merge into (will be mutated).\n * @param args - One or more source objects, optionally followed by a {@link MergeWithCustomizer}.\n * @returns The same `object` reference, now merged with all sources.\n *\n * @remarks\n * - The default merge behaviour only recurses into \"plain objects\", as determined by `isObjectPlain`.\n * - \"Loose objects\" are guarded using `isObjectLoose` to avoid attempting to merge non-object sources.\n * - If you want lodash-like array merging semantics (concat or index-wise), implement it via `customizer`.\n *\n * @example\n * ```ts\n * const target = { a: { x: 1 }, b: 1 };\n * const source = { a: { y: 2 }, b: 2 };\n *\n * mergeWith(target, source);\n * // => { a: { x: 1, y: 2 }, b: 2 }\n * // target is mutated\n * ```\n *\n * @example\n * ```ts\n * const target = { list: [1] };\n * const source = { list: [2] };\n *\n * mergeWith(target, source);\n * // => { list: [2] } (arrays replace by default)\n * ```\n *\n * @example\n * ```ts\n * const concatArrays: MergeWithCustomizer = (objValue, srcValue) => {\n * if (Array.isArray(objValue) && Array.isArray(srcValue)) {\n * return objValue.concat(srcValue);\n * }\n * };\n *\n * mergeWith({ list: [1] }, { list: [2] }, concatArrays);\n * // => { list: [1, 2] }\n * ```\n */\n\nexport function mergeWith<\n T extends Record<PropertyKey, any>,\n S extends readonly Record<PropertyKey, any>[]\n>(object: T, ...sources: S): MergeResult<T, S>;\n\nexport function mergeWith<\n T extends Record<PropertyKey, any>,\n S extends readonly Record<PropertyKey, any>[]\n>(\n object: T,\n ...args: [...sources: S, customizer: MergeWithCustomizer]\n): MergeResult<T, S>;\n\nexport function mergeWith(\n object: Record<PropertyKey, unknown>,\n ...args: (Record<PropertyKey, unknown> | MergeWithCustomizer)[]\n): Record<PropertyKey, unknown> {\n const maybeCustomizer = args.at(-1);\n const customizer = isFunction(maybeCustomizer)\n ? (maybeCustomizer as MergeWithCustomizer)\n : undefined;\n const sources = (customizer ? args.slice(0, -1) : args) as Record<\n PropertyKey,\n unknown\n >[];\n const stack = new WeakMap<object, object>();\n\n for (const source of sources) {\n baseMerge(\n object as Record<PropertyKey, unknown>,\n source,\n customizer,\n stack\n );\n }\n\n return object as any;\n}\n\nconst UNSAFE_KEYS = new Set<string>(['__proto__', 'constructor', 'prototype']);\n\n/**\n * Internal recursive merge implementation used by {@link mergeWith}.\n *\n * @param target - Destination object that is being mutated.\n * @param source - Current source object being merged in.\n * @param customizer - Optional custom merge override.\n * @param stack - Tracks visited source objects to avoid infinite recursion.\n */\nconst baseMerge = (\n target: Record<PropertyKey, unknown>,\n source: Record<PropertyKey, unknown>,\n customizer: MergeWithCustomizer | undefined,\n stack: WeakMap<object, object>\n): void => {\n if (!isObjectLoose(source)) return;\n\n for (const key of ownKeys(source)) {\n if (typeof key === 'string' && UNSAFE_KEYS.has(key)) {\n continue;\n }\n const srcValue = (source as any)[key];\n const objValue = (target as any)[key];\n const customized = customizer?.(\n objValue,\n srcValue,\n key,\n target,\n source,\n stack\n );\n\n if (isNotUndefined(customized)) {\n (target as any)[key] = customized;\n continue;\n }\n\n // Both are plain objects: merge into existing destination object.\n if (isObjectPlain(objValue) && isObjectPlain(srcValue)) {\n // If we've seen this src object before, reuse the prior destination.\n const cached = stack.get(srcValue as object);\n\n if (cached) {\n (target as any)[key] = cached;\n continue;\n }\n\n // Cache before recursing to handle cycles.\n stack.set(srcValue as object, objValue as object);\n\n baseMerge(\n objValue as Record<PropertyKey, unknown>,\n srcValue as Record<PropertyKey, unknown>,\n customizer,\n stack\n );\n continue;\n }\n\n // Source is a plain object but destination isn't: create destination object and merge into it.\n if (isObjectPlain(srcValue)) {\n const cached = stack.get(srcValue as object);\n\n if (cached) {\n (target as any)[key] = cached;\n continue;\n }\n\n const next: Record<PropertyKey, unknown> = isObjectPlain(objValue)\n ? (objValue as Record<PropertyKey, unknown>)\n : {};\n\n (target as any)[key] = next;\n\n // Cache before recursing to handle cycles and repeated refs.\n stack.set(srcValue as object, next as object);\n\n baseMerge(\n next,\n srcValue as Record<PropertyKey, unknown>,\n customizer,\n stack\n );\n continue;\n }\n\n // Arrays, primitives, dates, maps, sets, functions, etc. are assigned by replacement\n (target as any)[key] = srcValue;\n }\n};\n/**\n * Returns enumerable own property keys (string + symbol) for an object.\n *\n * This mirrors lodash's behaviour of considering both string keys and symbol keys,\n * but only those that are enumerable.\n *\n * @param obj - Object to inspect.\n * @returns Array of enumerable own keys (strings and symbols).\n *\n * @example\n * ```ts\n * const sym = Symbol(\"x\");\n * const o = { a: 1, [sym]: 2 };\n * Object.defineProperty(o, \"hidden\", { value: 3, enumerable: false });\n *\n * ownKeys(o);\n * // => [\"a\", Symbol(\"x\")]\n * ```\n */\nconst ownKeys = (obj: object): (string | symbol)[] => [\n ...Object.keys(obj),\n ...Object.getOwnPropertySymbols(obj).filter((s) =>\n Object.prototype.propertyIsEnumerable.call(obj, s)\n )\n];\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { isObjectLoose } from '@phun-ky/typeof';\n\nimport { mergeWith } from './utils/merge-with';\n\n/**\n * Deeply applies \"defaults\" from right-to-left sources into a new object, while preserving arrays.\n *\n * This function mimics the common lodash pattern:\n * `_.toArray(arguments).reverse().forEach(x => _.mergeWith(output, x, customizer))`,\n * but implemented in vanilla TypeScript with a small, predictable merge core.\n *\n * **Key behaviours**\n * - **Returns a new object** (the returned object is created inside the function).\n * - Sources are processed **right-to-left** (`reverse()`), meaning **earlier arguments win**:\n * values from the first argument are treated as \"defaults\" that should *not* be overwritten\n * by later arguments (because later arguments are merged first).\n * - Only **object-like** sources participate in merging. Non-objects are skipped via `isObjectLoose`.\n * - Uses {@link mergeWith} for the deep merge logic.\n * - **Arrays are preserved by replacement**: if a source value is an array, it replaces the\n * destination value at that key (rather than being merged element-by-element).\n *\n * @param args - A list of potential default/source values. Non-object values are ignored.\n * @returns A new object containing the merged result.\n *\n *\n * @example\n * ```ts\n * defaultsDeep(\n * { a: 1, nested: { x: 1 }, list: [1] },\n * { a: 2, nested: { y: 2 }, list: [2] }\n * );\n * // => { a: 1, nested: { x: 1, y: 2 }, list: [1] }\n * // (earlier args win; arrays preserved by replacement)\n * ```\n *\n * @example\n * ```ts\n * defaultsDeep(\n * { opts: { retry: 3 } },\n * null,\n * { opts: { timeout: 1000 } }\n * );\n * // => { opts: { retry: 3, timeout: 1000 } }\n * // (non-object sources are ignored)\n * ```\n *\n * @remarks\n *\n * - The customizer only checks the *source* value for arrays. If `s` is an array, it is returned\n * and assigned as-is. Otherwise returning `undefined` delegates merging to {@link mergeWith}'s\n * default behaviour (which deep-merges plain objects and replaces other value types).\n * - The output type is `Record<PropertyKey, unknown>` to keep the API broadly usable without\n * over-promising specific shapes.\n *\n */\nconst defaultsDeep = (...args: unknown[]) => {\n const output: Record<PropertyKey, unknown> = {};\n\n for (const item of args.slice().reverse()) {\n if (!isObjectLoose(item)) continue;\n\n mergeWith(output, item, (_o: any, s: any[] | undefined) =>\n Array.isArray(s) ? s : undefined\n );\n }\n\n return output;\n};\n\nexport default defaultsDeep;\n"],"names":["f","t","u","p","Object","prototype","toString","call","r","getPrototypeOf","l","mergeWith","object","args","maybeCustomizer","at","customizer","undefined","sources","slice","stack","WeakMap","source","baseMerge","UNSAFE_KEYS","Set","target","isObjectLoose","key","ownKeys","has","srcValue","objValue","customized","isNotUndefined","isObjectPlain","cached","get","set","next","obj","keys","getOwnPropertySymbols","filter","s","propertyIsEnumerable","output","item","reverse","_o","Array","isArray"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0BoO,SAASA,EAAEC,GAAG,OAA9C,SAAWA,GAAG,YAAO,IAASA,CAAC,CAAsBC,CAAED,EAAE,CAA2B,SAASE,EAAEF,GAAG,GAAG,oBAAoBG,OAAOC,UAAUC,SAASC,KAAKN,GAAG,SAAS,MAAMO,EAAEJ,OAAOK,eAAeR,GAAG,OAAOO,IAAIJ,OAAOC,WAAW,OAAOG,CAAC,CAAqW,SAASE,EAAET,GAAG,MAAMO,SAASP,EAAE,OAAO,OAAOA,IAAI,WAAWO,GAAG,aAAaA,EAAE,UCyGj1BG,EACdC,KACGC,GAEH,MAAMC,EAAkBD,EAAKE,OACvBC,ED9GwmD,mBC8GhlDF,EACzBA,OACDG,EACJ,MAAMC,EAAWF,EAAaH,EAAKM,MAAM,GAAG,GAAMN,EAI5CO,EAAQ,IAAIC,QAElB,IAAK,MAAMC,KAAUJ,EACnBK,EACEX,EACAU,EACAN,EACAI,GAIJ,OAAOR,CACT,CAEA,MAAMY,EAAc,IAAIC,IAAY,CAAC,YAAa,cAAe,cAU3DF,EAAY,CAChBG,EACAJ,EACAN,EACAI,KAEA,GAAKO,EAAcL,GAEnB,IAAK,MAAMM,KAAOC,EAAQP,GAAS,CACjC,GAAmB,iBAARM,GAAoBJ,EAAYM,IAAIF,GAC7C,SAEF,MAAMG,EAAYT,EAAeM,GAC3BI,EAAYN,EAAeE,GAC3BK,EAAajB,IACjBgB,EACAD,EACAH,EACAF,EACAJ,EACAF,GAGF,GAAIc,EAAeD,GAChBP,EAAeE,GAAOK,MADzB,CAMA,GAAIE,EAAcH,IAAaG,EAAcJ,GAAW,CAEtD,MAAMK,EAAShB,EAAMiB,IAAIN,GAEzB,GAAIK,EAAQ,CACTV,EAAeE,GAAOQ,EACvB,QACF,CAGAhB,EAAMkB,IAAIP,EAAoBC,GAE9BT,EACES,EACAD,EACAf,EACAI,GAEF,QACF,CAGA,GAAIe,EAAcJ,GAAW,CAC3B,MAAMK,EAAShB,EAAMiB,IAAIN,GAEzB,GAAIK,EAAQ,CACTV,EAAeE,GAAOQ,EACvB,QACF,CAEA,MAAMG,EAAqCJ,EAAcH,GACpDA,EACD,CAAA,EAEHN,EAAeE,GAAOW,EAGvBnB,EAAMkB,IAAIP,EAAoBQ,GAE9BhB,EACEgB,EACAR,EACAf,EACAI,GAEF,QACF,CAGCM,EAAeE,GAAOG,CApDvB,CAqDF,GAqBIF,EAAWW,GAAqC,IACjDpC,OAAOqC,KAAKD,MACZpC,OAAOsC,sBAAsBF,GAAKG,OAAQC,GAC3CxC,OAAOC,UAAUwC,qBAAqBtC,KAAKiC,EAAKI,oBCtN/B,IAAI/B,KACvB,MAAMiC,EAAuC,CAAA,EAE7C,IAAK,MAAMC,KAAQlC,EAAKM,QAAQ6B,UACzBrB,EAAcoB,IAEnBpC,EAAUmC,EAAQC,EAAM,CAACE,EAASL,IAChCM,MAAMC,QAAQP,GAAKA,OAAI3B,GAI3B,OAAO6B","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"defaults-deep.cjs","sources":["../src/utils/merge-with.ts","../src/main.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n isFunction,\n isNotUndefined,\n isObjectLoose,\n isObjectPlain\n} from '@phun-ky/typeof';\n\n/* node:coverage disable */\n/**\n * Customizer function used by {@link mergeWith} to override merge behaviour.\n *\n * If the customizer returns `undefined`, {@link mergeWith} falls back to its default\n * deep-merge rules. Any other return value will be assigned to the destination key.\n *\n * @param objValue - Current value on the destination (`object`) for `key`.\n * @param srcValue - Incoming value from the current `source` for `key`.\n * @param key - Property key being merged (string or symbol).\n * @param object - Destination object being mutated/merged into.\n * @param source - Current source object being merged from.\n * @param stack - A `WeakMap` used internally to prevent infinite recursion on circular references.\n * @returns The value to assign for this key, or `undefined` to use default merge behaviour.\n *\n * @example\n * ```ts\n * import { mergeWith, type MergeWithCustomizer } from \"./mergeWith\";\n *\n * const concatArrays: MergeWithCustomizer = (objValue, srcValue) => {\n * if (Array.isArray(objValue) && Array.isArray(srcValue)) {\n * return objValue.concat(srcValue);\n * }\n * return undefined;\n * };\n *\n * const a = { list: [1] };\n * const b = { list: [2] };\n *\n * mergeWith(a, b, concatArrays);\n * // => { list: [1, 2] }\n * ```\n */\n/* node:coverage enable */\nexport type MergeWithCustomizer = (\n objValue: unknown,\n srcValue: unknown,\n key: string | symbol,\n object: Record<PropertyKey, unknown>,\n source: Record<PropertyKey, unknown>,\n stack: WeakMap<object, object>\n) => unknown;\n\ntype UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (\n k: infer I\n) => void\n ? I\n : never;\n\ntype MergeResult<T, S extends readonly any[]> = T &\n UnionToIntersection<S[number]>;\n\n/**\n * Deep-merges one or more source objects into a destination object, with optional customization.\n *\n * This is a \"vanilla TS\" alternative to lodash's `mergeWith` with a deliberately conservative\n * default strategy:\n *\n * - **Mutates** and returns the destination `object`.\n * - Enumerates **own enumerable string keys** and **own enumerable symbol keys** from each source.\n * - When both destination and source values are **plain objects**, merges recursively.\n * - When the source value is a **plain object** but the destination is not, creates `{}` and merges into it.\n * - All other value types (arrays, dates, maps, sets, functions, primitives, class instances, etc.)\n * are assigned by **replacement** unless the customizer handles them.\n * - Circular references in **sources** are prevented from causing infinite recursion via a `WeakMap` stack.\n *\n * @typeParam T - Destination object type.\n * @typeParam S - Tuple/array of source object types.\n *\n * @param object - Destination object to merge into (will be mutated).\n * @param args - One or more source objects, optionally followed by a {@link MergeWithCustomizer}.\n * @returns The same `object` reference, now merged with all sources.\n *\n * @remarks\n * - The default merge behaviour only recurses into \"plain objects\", as determined by `isObjectPlain`.\n * - \"Loose objects\" are guarded using `isObjectLoose` to avoid attempting to merge non-object sources.\n * - If you want lodash-like array merging semantics (concat or index-wise), implement it via `customizer`.\n *\n * @example\n * ```ts\n * const target = { a: { x: 1 }, b: 1 };\n * const source = { a: { y: 2 }, b: 2 };\n *\n * mergeWith(target, source);\n * // => { a: { x: 1, y: 2 }, b: 2 }\n * // target is mutated\n * ```\n *\n * @example\n * ```ts\n * const target = { list: [1] };\n * const source = { list: [2] };\n *\n * mergeWith(target, source);\n * // => { list: [2] } (arrays replace by default)\n * ```\n *\n * @example\n * ```ts\n * const concatArrays: MergeWithCustomizer = (objValue, srcValue) => {\n * if (Array.isArray(objValue) && Array.isArray(srcValue)) {\n * return objValue.concat(srcValue);\n * }\n * };\n *\n * mergeWith({ list: [1] }, { list: [2] }, concatArrays);\n * // => { list: [1, 2] }\n * ```\n */\n\nexport function mergeWith<\n T extends Record<PropertyKey, any>,\n S extends readonly Record<PropertyKey, any>[]\n>(object: T, ...sources: S): MergeResult<T, S>;\n\nexport function mergeWith<\n T extends Record<PropertyKey, any>,\n S extends readonly Record<PropertyKey, any>[]\n>(\n object: T,\n ...args: [...sources: S, customizer: MergeWithCustomizer]\n): MergeResult<T, S>;\n\nexport function mergeWith(\n object: Record<PropertyKey, unknown>,\n ...args: (Record<PropertyKey, unknown> | MergeWithCustomizer)[]\n): Record<PropertyKey, unknown> {\n const maybeCustomizer = args.at(-1);\n const customizer = isFunction(maybeCustomizer)\n ? (maybeCustomizer as MergeWithCustomizer)\n : undefined;\n const sources = (customizer ? args.slice(0, -1) : args) as Record<\n PropertyKey,\n unknown\n >[];\n const stack = new WeakMap<object, object>();\n\n for (const source of sources) {\n baseMerge(\n object as Record<PropertyKey, unknown>,\n source,\n customizer,\n stack\n );\n }\n\n return object as any;\n}\n\nconst UNSAFE_KEYS = new Set<string>(['__proto__', 'constructor', 'prototype']);\n/**\n * Internal recursive merge implementation used by {@link mergeWith}.\n *\n * @param target - Destination object that is being mutated.\n * @param source - Current source object being merged in.\n * @param customizer - Optional custom merge override.\n * @param stack - The source objects on the current path, each mapped to its\n * destination, to stop at cycles. Only the current path: a source object\n * reached again through a sibling key gets its own destination.\n */\nconst baseMerge = (\n target: Record<PropertyKey, unknown>,\n source: Record<PropertyKey, unknown>,\n customizer: MergeWithCustomizer | undefined,\n stack: WeakMap<object, object>\n): void => {\n if (!isObjectLoose(source)) return;\n\n for (const key of ownKeys(source)) {\n if (typeof key === 'string' && UNSAFE_KEYS.has(key)) {\n continue;\n }\n\n const srcValue = (source as any)[key];\n const objValue = (target as any)[key];\n const customized = customizer?.(\n objValue,\n srcValue,\n key,\n target,\n source,\n stack\n );\n\n if (isNotUndefined(customized)) {\n (target as any)[key] = customized;\n continue;\n }\n\n // Both are plain objects: merge into existing destination object.\n if (isObjectPlain(objValue) && isObjectPlain(srcValue)) {\n // A cycle back to a source object on the current path: point at its destination.\n const cached = stack.get(srcValue as object);\n\n if (cached) {\n (target as any)[key] = cached;\n continue;\n }\n\n stack.set(srcValue as object, objValue as object);\n\n baseMerge(\n objValue as Record<PropertyKey, unknown>,\n srcValue as Record<PropertyKey, unknown>,\n customizer,\n stack\n );\n stack.delete(srcValue as object);\n continue;\n }\n\n // Source is a plain object but destination isn't: create destination object and merge into it.\n if (isObjectPlain(srcValue)) {\n const cached = stack.get(srcValue as object);\n\n if (cached) {\n (target as any)[key] = cached;\n continue;\n }\n\n const next: Record<PropertyKey, unknown> = isObjectPlain(objValue)\n ? (objValue as Record<PropertyKey, unknown>)\n : {};\n\n (target as any)[key] = next;\n\n stack.set(srcValue as object, next as object);\n\n baseMerge(\n next,\n srcValue as Record<PropertyKey, unknown>,\n customizer,\n stack\n );\n stack.delete(srcValue as object);\n continue;\n }\n\n // Arrays, primitives, dates, maps, sets, functions, etc. are assigned by replacement\n (target as any)[key] = srcValue;\n }\n};\n/**\n * Returns enumerable own property keys (string + symbol) for an object.\n *\n * This mirrors lodash's behaviour of considering both string keys and symbol keys,\n * but only those that are enumerable.\n *\n * @param obj - Object to inspect.\n * @returns Array of enumerable own keys (strings and symbols).\n *\n * @example\n * ```ts\n * const sym = Symbol(\"x\");\n * const o = { a: 1, [sym]: 2 };\n * Object.defineProperty(o, \"hidden\", { value: 3, enumerable: false });\n *\n * ownKeys(o);\n * // => [\"a\", Symbol(\"x\")]\n * ```\n */\nconst ownKeys = (obj: object): (string | symbol)[] => [\n ...Object.keys(obj),\n ...Object.getOwnPropertySymbols(obj).filter((s) =>\n Object.prototype.propertyIsEnumerable.call(obj, s)\n )\n];\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { isObjectLoose } from '@phun-ky/typeof';\n\nimport { mergeWith } from './utils/merge-with';\n\n/**\n * Deeply applies \"defaults\" from right-to-left sources into a new object, while preserving arrays.\n *\n * This function mimics the common lodash pattern:\n * `_.toArray(arguments).reverse().forEach(x => _.mergeWith(output, x, customizer))`,\n * but implemented in vanilla TypeScript with a small, predictable merge core.\n *\n * **Key behaviours**\n * - **Returns a new object** (the returned object is created inside the function).\n * - Sources are processed **right-to-left** (`reverse()`), meaning **earlier arguments win**:\n * values from the first argument are treated as \"defaults\" that should *not* be overwritten\n * by later arguments (because later arguments are merged first).\n * - Only **object-like** sources participate in merging. Non-objects are skipped via `isObjectLoose`.\n * - Uses an internal `mergeWith` for the deep merge logic.\n * - **Arrays are preserved by replacement**: if a source value is an array, it replaces the\n * destination value at that key (rather than being merged element-by-element).\n *\n * @param args - A list of potential default/source values. Non-object values are ignored.\n * @returns A new object containing the merged result.\n *\n *\n * @example\n * ```ts\n * defaultsDeep(\n * { a: 1, nested: { x: 1 }, list: [1] },\n * { a: 2, nested: { y: 2 }, list: [2] }\n * );\n * // => { a: 1, nested: { x: 1, y: 2 }, list: [1] }\n * // (earlier args win; arrays preserved by replacement)\n * ```\n *\n * @example\n * ```ts\n * defaultsDeep(\n * { opts: { retry: 3 } },\n * null,\n * { opts: { timeout: 1000 } }\n * );\n * // => { opts: { retry: 3, timeout: 1000 } }\n * // (non-object sources are ignored)\n * ```\n *\n * @remarks\n *\n * - The customizer only checks the *source* value for arrays. If `s` is an array, it is returned\n * and assigned as-is. Otherwise returning `undefined` delegates merging to `mergeWith`'s\n * default behaviour (which deep-merges plain objects and replaces other value types).\n * - The output type is `Record<PropertyKey, unknown>` to keep the API broadly usable without\n * over-promising specific shapes.\n *\n */\nconst defaultsDeep = (...args: unknown[]) => {\n const output: Record<PropertyKey, unknown> = {};\n\n for (const item of args.slice().reverse()) {\n if (!isObjectLoose(item)) continue;\n\n mergeWith(output, item, (_o: any, s: any[] | undefined) =>\n Array.isArray(s) ? s : undefined\n );\n }\n\n return output;\n};\n\nexport default defaultsDeep;\n"],"names":["mergeWith","object","args","maybeCustomizer","at","customizer","isFunction","undefined","sources","slice","stack","WeakMap","source","baseMerge","UNSAFE_KEYS","Set","target","isObjectLoose","key","ownKeys","has","srcValue","objValue","customized","isNotUndefined","isObjectPlain","cached","get","set","delete","next","obj","Object","keys","getOwnPropertySymbols","filter","s","prototype","propertyIsEnumerable","call","output","item","reverse","_o","Array","isArray"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uDAmIgBA,EACdC,KACGC,GAEH,MAAMC,EAAkBD,EAAKE,OACvBC,EAAaC,EAAAA,WAAWH,GACzBA,OACDI,EACEC,EAAWH,EAAaH,EAAKO,MAAM,GAAG,GAAMP,EAI5CQ,EAAQ,IAAIC,QAElB,IAAK,MAAMC,KAAUJ,EACnBK,EACEZ,EACAW,EACAP,EACAK,GAIJ,OAAOT,CACT,CAEA,MAAMa,EAAc,IAAIC,IAAY,CAAC,YAAa,cAAe,cAW3DF,EAAY,CAChBG,EACAJ,EACAP,EACAK,KAEA,GAAKO,EAAAA,cAAcL,GAEnB,IAAK,MAAMM,KAAOC,EAAQP,GAAS,CACjC,GAAmB,iBAARM,GAAoBJ,EAAYM,IAAIF,GAC7C,SAGF,MAAMG,EAAYT,EAAeM,GAC3BI,EAAYN,EAAeE,GAC3BK,EAAalB,IACjBiB,EACAD,EACAH,EACAF,EACAJ,EACAF,GAGF,GAAIc,EAAAA,eAAeD,GAChBP,EAAeE,GAAOK,MADzB,CAMA,GAAIE,EAAAA,cAAcH,IAAaG,EAAAA,cAAcJ,GAAW,CAEtD,MAAMK,EAAShB,EAAMiB,IAAIN,GAEzB,GAAIK,EAAQ,CACTV,EAAeE,GAAOQ,EACvB,QACF,CAEAhB,EAAMkB,IAAIP,EAAoBC,GAE9BT,EACES,EACAD,EACAhB,EACAK,GAEFA,EAAMmB,OAAOR,GACb,QACF,CAGA,GAAII,EAAAA,cAAcJ,GAAW,CAC3B,MAAMK,EAAShB,EAAMiB,IAAIN,GAEzB,GAAIK,EAAQ,CACTV,EAAeE,GAAOQ,EACvB,QACF,CAEA,MAAMI,EAAqCL,EAAAA,cAAcH,GACpDA,EACD,CAAA,EAEHN,EAAeE,GAAOY,EAEvBpB,EAAMkB,IAAIP,EAAoBS,GAE9BjB,EACEiB,EACAT,EACAhB,EACAK,GAEFA,EAAMmB,OAAOR,GACb,QACF,CAGCL,EAAeE,GAAOG,CApDvB,CAqDF,GAqBIF,EAAWY,GAAqC,IACjDC,OAAOC,KAAKF,MACZC,OAAOE,sBAAsBH,GAAKI,OAAQC,GAC3CJ,OAAOK,UAAUC,qBAAqBC,KAAKR,EAAKK,oBCxN/B,IAAIlC,KACvB,MAAMsC,EAAuC,CAAA,EAE7C,IAAK,MAAMC,KAAQvC,EAAKO,QAAQiC,UACzBzB,EAAAA,cAAcwB,IAEnBzC,EAAUwC,EAAQC,EAAM,CAACE,EAASP,IAChCQ,MAAMC,QAAQT,GAAKA,OAAI7B,GAI3B,OAAOiC"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Deeply applies "defaults" from right-to-left sources into a new object, while preserving arrays.
3
+ *
4
+ * This function mimics the common lodash pattern:
5
+ * `_.toArray(arguments).reverse().forEach(x => _.mergeWith(output, x, customizer))`,
6
+ * but implemented in vanilla TypeScript with a small, predictable merge core.
7
+ *
8
+ * **Key behaviours**
9
+ * - **Returns a new object** (the returned object is created inside the function).
10
+ * - Sources are processed **right-to-left** (`reverse()`), meaning **earlier arguments win**:
11
+ * values from the first argument are treated as "defaults" that should *not* be overwritten
12
+ * by later arguments (because later arguments are merged first).
13
+ * - Only **object-like** sources participate in merging. Non-objects are skipped via `isObjectLoose`.
14
+ * - Uses an internal `mergeWith` for the deep merge logic.
15
+ * - **Arrays are preserved by replacement**: if a source value is an array, it replaces the
16
+ * destination value at that key (rather than being merged element-by-element).
17
+ *
18
+ * @param args - A list of potential default/source values. Non-object values are ignored.
19
+ * @returns A new object containing the merged result.
20
+ *
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * defaultsDeep(
25
+ * { a: 1, nested: { x: 1 }, list: [1] },
26
+ * { a: 2, nested: { y: 2 }, list: [2] }
27
+ * );
28
+ * // => { a: 1, nested: { x: 1, y: 2 }, list: [1] }
29
+ * // (earlier args win; arrays preserved by replacement)
30
+ * ```
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * defaultsDeep(
35
+ * { opts: { retry: 3 } },
36
+ * null,
37
+ * { opts: { timeout: 1000 } }
38
+ * );
39
+ * // => { opts: { retry: 3, timeout: 1000 } }
40
+ * // (non-object sources are ignored)
41
+ * ```
42
+ *
43
+ * @remarks
44
+ *
45
+ * - The customizer only checks the *source* value for arrays. If `s` is an array, it is returned
46
+ * and assigned as-is. Otherwise returning `undefined` delegates merging to `mergeWith`'s
47
+ * default behaviour (which deep-merges plain objects and replaces other value types).
48
+ * - The output type is `Record<PropertyKey, unknown>` to keep the API broadly usable without
49
+ * over-promising specific shapes.
50
+ *
51
+ */
52
+ declare const defaultsDeep: (...args: unknown[]) => Record<PropertyKey, unknown>;
53
+
54
+ export = defaultsDeep;
@@ -11,7 +11,7 @@
11
11
  * values from the first argument are treated as "defaults" that should *not* be overwritten
12
12
  * by later arguments (because later arguments are merged first).
13
13
  * - Only **object-like** sources participate in merging. Non-objects are skipped via `isObjectLoose`.
14
- * - Uses {@link mergeWith} for the deep merge logic.
14
+ * - Uses an internal `mergeWith` for the deep merge logic.
15
15
  * - **Arrays are preserved by replacement**: if a source value is an array, it replaces the
16
16
  * destination value at that key (rather than being merged element-by-element).
17
17
  *
@@ -43,7 +43,7 @@
43
43
  * @remarks
44
44
  *
45
45
  * - The customizer only checks the *source* value for arrays. If `s` is an array, it is returned
46
- * and assigned as-is. Otherwise returning `undefined` delegates merging to {@link mergeWith}'s
46
+ * and assigned as-is. Otherwise returning `undefined` delegates merging to `mergeWith`'s
47
47
  * default behaviour (which deep-merges plain objects and replaces other value types).
48
48
  * - The output type is `Record<PropertyKey, unknown>` to keep the API broadly usable without
49
49
  * over-promising specific shapes.
@@ -2,7 +2,7 @@
2
2
  * @phun-ky/defaults-deep
3
3
  * Like lodash's _.defaultsDeep, but with array preservation
4
4
  * @author Alexander Vassbotn Rรธyne-Helgesen <alexander@phun-ky.net>
5
- * @version 2.0.7
5
+ * @version 2.0.9
6
6
  * @license
7
7
  * Copyright (c) 2026 Alexander Vassbotn Rรธyne-Helgesen
8
8
  *
@@ -40,31 +40,5 @@
40
40
  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
41
41
  * PERFORMANCE OF THIS SOFTWARE.
42
42
  */
43
- /**
44
- * @phun-ky/typeof
45
- * A set of JavaScript helper functions to check for types
46
- * @author Alexander Vassbotn Rรธyne-Helgesen <alexander@phun-ky.net>
47
- * @version 3.0.7
48
- * @license
49
- * Copyright (c) 2024 Alexander Vassbotn Rรธyne-Helgesen
50
- *
51
- * Permission is hereby granted, free of charge, to any person obtaining a copy
52
- * of this software and associated documentation files (the "Software"), to deal
53
- * in the Software without restriction, including without limitation the rights
54
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
55
- * copies of the Software, and to permit persons to whom the Software is
56
- * furnished to do so, subject to the following conditions:
57
- *
58
- * The above copyright notice and this permission notice shall be included in all
59
- * copies or substantial portions of the Software.
60
- *
61
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
62
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
63
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
64
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
65
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
66
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
67
- * SOFTWARE.
68
- */
69
- function t(t){return!function(t){return void 0===t}(t)}function o(t){if("[object Object]"!==Object.prototype.toString.call(t))return!1;const o=Object.getPrototypeOf(t);return o===Object.prototype||null===o}function e(t){const o=typeof t;return null!==t&&("object"===o||"function"===o)}function n(t,...o){const e=o.at(-1),n="function"==typeof e?e:void 0;const c=n?o.slice(0,-1):o,i=new WeakMap;for(const o of c)r(t,o,n,i);return t}const c=new Set(["__proto__","constructor","prototype"]),r=(n,f,s,u)=>{if(e(f))for(const e of i(f)){if("string"==typeof e&&c.has(e))continue;const i=f[e],p=n[e],l=s?.(p,i,e,n,f,u);if(t(l))n[e]=l;else{if(o(p)&&o(i)){const t=u.get(i);if(t){n[e]=t;continue}u.set(i,p),r(p,i,s,u);continue}if(o(i)){const t=u.get(i);if(t){n[e]=t;continue}const c=o(p)?p:{};n[e]=c,u.set(i,c),r(c,i,s,u);continue}n[e]=i}}},i=t=>[...Object.keys(t),...Object.getOwnPropertySymbols(t).filter(o=>Object.prototype.propertyIsEnumerable.call(t,o))],f=(...t)=>{const o={};for(const c of t.slice().reverse())e(c)&&n(o,c,(t,o)=>Array.isArray(o)?o:void 0);return o};export{f as default};
43
+ import{isFunction as t,isObjectLoose as e,isNotUndefined as o,isObjectPlain as n}from"@phun-ky/typeof";function r(e,...o){const n=o.at(-1),r=t(n)?n:void 0,c=r?o.slice(0,-1):o,i=new WeakMap;for(const t of c)s(e,t,r,i);return e}const c=new Set(["__proto__","constructor","prototype"]),s=(t,r,f,p)=>{if(e(r))for(const e of i(r)){if("string"==typeof e&&c.has(e))continue;const i=r[e],u=t[e],l=f?.(u,i,e,t,r,p);if(o(l))t[e]=l;else{if(n(u)&&n(i)){const o=p.get(i);if(o){t[e]=o;continue}p.set(i,u),s(u,i,f,p),p.delete(i);continue}if(n(i)){const o=p.get(i);if(o){t[e]=o;continue}const r=n(u)?u:{};t[e]=r,p.set(i,r),s(r,i,f,p),p.delete(i);continue}t[e]=i}}},i=t=>[...Object.keys(t),...Object.getOwnPropertySymbols(t).filter(e=>Object.prototype.propertyIsEnumerable.call(t,e))],f=(...t)=>{const o={};for(const n of t.slice().reverse())e(n)&&r(o,n,(t,e)=>Array.isArray(e)?e:void 0);return o};export{f as default};
70
44
  //# sourceMappingURL=defaults-deep.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"defaults-deep.js","sources":["../node_modules/@phun-ky/typeof/dist/typeof.js","../src/utils/merge-with.ts","../src/main.ts"],"sourcesContent":["/**\n* @phun-ky/typeof\n* A set of JavaScript helper functions to check for types\n* @author Alexander Vassbotn Rรธyne-Helgesen <alexander@phun-ky.net>\n* @version 3.0.7\n* @license\n* Copyright (c) 2024 Alexander Vassbotn Rรธyne-Helgesen\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the \"Software\"), to deal\n* in the Software without restriction, including without limitation the rights\n* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n* copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in all\n* copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n* SOFTWARE.\n*/\nfunction t(t){return\"string\"==typeof t}function r(r){return!t(r)}function n(t){return\"number\"==typeof t}function e(t){return!n(t)}function o(t){return\"boolean\"==typeof t}function c(t){return!o(t)}function u(t){return void 0===t}function f(t){return!u(t)}function i(t){return!u(t)}function p(t){if(\"[object Object]\"!==Object.prototype.toString.call(t))return!1;const r=Object.getPrototypeOf(t);return r===Object.prototype||null===r}function y(t){if(\"object\"!=typeof t||null===t)return!1;if(\"[object Object]\"!==Object.prototype.toString.call(t))return!1;const r=Object.getPrototypeOf(t);if(null===r)return!0;const n=Object.prototype.hasOwnProperty.call(r,\"constructor\")?r.constructor:null;return\"function\"==typeof n&&n instanceof n&&Function.prototype.call(n)===Function.prototype.call(t)}function l(t){const r=typeof t;return null!==t&&(\"object\"===r||\"function\"===r)}function a(t){if(\"function\"!=typeof t)return!1;if(b(t))return!1;try{const r=Object.getOwnPropertyDescriptor(t,\"prototype\");return!!r&&!r.writable}catch{return!1}}function b(t){if(\"function\"!=typeof t)return!1;return[Object,Array,Function,String,Number,Boolean,Date,RegExp,Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError,Map,WeakMap,Set,WeakSet,Promise].includes(t)}const O=new Set([Object,Array,Function,String,Number,Boolean,Date,RegExp,Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError,Map,WeakMap,Set,WeakSet,Promise,BigInt,Symbol]);function j(t){return\"function\"==typeof t&&O.has(t)}function E(t){return\"object\"==typeof t&&null!==t&&Object.getPrototypeOf(t)!==Object.prototype&&null!==Object.getPrototypeOf(t)}function s(t){return\"function\"==typeof t}export{o as isBoolean,j as isBuiltInCallable,b as isBuiltInConstructor,a as isClass,i as isDefined,s as isFunction,E as isInstanceOfUnknownClass,c as isNotBoolean,e as isNotNumber,r as isNotString,f as isNotUndefined,n as isNumber,l as isObjectLoose,p as isObjectPlain,y as isObjectStrict,t as isString,u as isUndefined};\n//# sourceMappingURL=typeof.js.map\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n isFunction,\n isNotUndefined,\n isObjectLoose,\n isObjectPlain\n} from '@phun-ky/typeof';\n\n/* node:coverage disable */\n/**\n * Customizer function used by {@link mergeWith} to override merge behaviour.\n *\n * If the customizer returns `undefined`, {@link mergeWith} falls back to its default\n * deep-merge rules. Any other return value will be assigned to the destination key.\n *\n * @param objValue - Current value on the destination (`object`) for `key`.\n * @param srcValue - Incoming value from the current `source` for `key`.\n * @param key - Property key being merged (string or symbol).\n * @param object - Destination object being mutated/merged into.\n * @param source - Current source object being merged from.\n * @param stack - A `WeakMap` used internally to prevent infinite recursion on circular references.\n * @returns The value to assign for this key, or `undefined` to use default merge behaviour.\n *\n * @example\n * ```ts\n * import { mergeWith, type MergeWithCustomizer } from \"./mergeWith\";\n *\n * const concatArrays: MergeWithCustomizer = (objValue, srcValue) => {\n * if (Array.isArray(objValue) && Array.isArray(srcValue)) {\n * return objValue.concat(srcValue);\n * }\n * return undefined;\n * };\n *\n * const a = { list: [1] };\n * const b = { list: [2] };\n *\n * mergeWith(a, b, concatArrays);\n * // => { list: [1, 2] }\n * ```\n */\n/* node:coverage enable */\nexport type MergeWithCustomizer = (\n objValue: unknown,\n srcValue: unknown,\n key: string | symbol,\n object: Record<PropertyKey, unknown>,\n source: Record<PropertyKey, unknown>,\n stack: WeakMap<object, object>\n) => unknown;\n\ntype UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (\n k: infer I\n) => void\n ? I\n : never;\n\ntype MergeResult<T, S extends readonly any[]> = T &\n UnionToIntersection<S[number]>;\n\n/**\n * Deep-merges one or more source objects into a destination object, with optional customization.\n *\n * This is a \"vanilla TS\" alternative to lodash's `mergeWith` with a deliberately conservative\n * default strategy:\n *\n * - **Mutates** and returns the destination `object`.\n * - Enumerates **own enumerable string keys** and **own enumerable symbol keys** from each source.\n * - When both destination and source values are **plain objects**, merges recursively.\n * - When the source value is a **plain object** but the destination is not, creates `{}` and merges into it.\n * - All other value types (arrays, dates, maps, sets, functions, primitives, class instances, etc.)\n * are assigned by **replacement** unless the customizer handles them.\n * - Circular references in **sources** are prevented from causing infinite recursion via a `WeakMap` stack.\n *\n * @typeParam T - Destination object type.\n * @typeParam S - Tuple/array of source object types.\n *\n * @param object - Destination object to merge into (will be mutated).\n * @param args - One or more source objects, optionally followed by a {@link MergeWithCustomizer}.\n * @returns The same `object` reference, now merged with all sources.\n *\n * @remarks\n * - The default merge behaviour only recurses into \"plain objects\", as determined by `isObjectPlain`.\n * - \"Loose objects\" are guarded using `isObjectLoose` to avoid attempting to merge non-object sources.\n * - If you want lodash-like array merging semantics (concat or index-wise), implement it via `customizer`.\n *\n * @example\n * ```ts\n * const target = { a: { x: 1 }, b: 1 };\n * const source = { a: { y: 2 }, b: 2 };\n *\n * mergeWith(target, source);\n * // => { a: { x: 1, y: 2 }, b: 2 }\n * // target is mutated\n * ```\n *\n * @example\n * ```ts\n * const target = { list: [1] };\n * const source = { list: [2] };\n *\n * mergeWith(target, source);\n * // => { list: [2] } (arrays replace by default)\n * ```\n *\n * @example\n * ```ts\n * const concatArrays: MergeWithCustomizer = (objValue, srcValue) => {\n * if (Array.isArray(objValue) && Array.isArray(srcValue)) {\n * return objValue.concat(srcValue);\n * }\n * };\n *\n * mergeWith({ list: [1] }, { list: [2] }, concatArrays);\n * // => { list: [1, 2] }\n * ```\n */\n\nexport function mergeWith<\n T extends Record<PropertyKey, any>,\n S extends readonly Record<PropertyKey, any>[]\n>(object: T, ...sources: S): MergeResult<T, S>;\n\nexport function mergeWith<\n T extends Record<PropertyKey, any>,\n S extends readonly Record<PropertyKey, any>[]\n>(\n object: T,\n ...args: [...sources: S, customizer: MergeWithCustomizer]\n): MergeResult<T, S>;\n\nexport function mergeWith(\n object: Record<PropertyKey, unknown>,\n ...args: (Record<PropertyKey, unknown> | MergeWithCustomizer)[]\n): Record<PropertyKey, unknown> {\n const maybeCustomizer = args.at(-1);\n const customizer = isFunction(maybeCustomizer)\n ? (maybeCustomizer as MergeWithCustomizer)\n : undefined;\n const sources = (customizer ? args.slice(0, -1) : args) as Record<\n PropertyKey,\n unknown\n >[];\n const stack = new WeakMap<object, object>();\n\n for (const source of sources) {\n baseMerge(\n object as Record<PropertyKey, unknown>,\n source,\n customizer,\n stack\n );\n }\n\n return object as any;\n}\n\nconst UNSAFE_KEYS = new Set<string>(['__proto__', 'constructor', 'prototype']);\n\n/**\n * Internal recursive merge implementation used by {@link mergeWith}.\n *\n * @param target - Destination object that is being mutated.\n * @param source - Current source object being merged in.\n * @param customizer - Optional custom merge override.\n * @param stack - Tracks visited source objects to avoid infinite recursion.\n */\nconst baseMerge = (\n target: Record<PropertyKey, unknown>,\n source: Record<PropertyKey, unknown>,\n customizer: MergeWithCustomizer | undefined,\n stack: WeakMap<object, object>\n): void => {\n if (!isObjectLoose(source)) return;\n\n for (const key of ownKeys(source)) {\n if (typeof key === 'string' && UNSAFE_KEYS.has(key)) {\n continue;\n }\n const srcValue = (source as any)[key];\n const objValue = (target as any)[key];\n const customized = customizer?.(\n objValue,\n srcValue,\n key,\n target,\n source,\n stack\n );\n\n if (isNotUndefined(customized)) {\n (target as any)[key] = customized;\n continue;\n }\n\n // Both are plain objects: merge into existing destination object.\n if (isObjectPlain(objValue) && isObjectPlain(srcValue)) {\n // If we've seen this src object before, reuse the prior destination.\n const cached = stack.get(srcValue as object);\n\n if (cached) {\n (target as any)[key] = cached;\n continue;\n }\n\n // Cache before recursing to handle cycles.\n stack.set(srcValue as object, objValue as object);\n\n baseMerge(\n objValue as Record<PropertyKey, unknown>,\n srcValue as Record<PropertyKey, unknown>,\n customizer,\n stack\n );\n continue;\n }\n\n // Source is a plain object but destination isn't: create destination object and merge into it.\n if (isObjectPlain(srcValue)) {\n const cached = stack.get(srcValue as object);\n\n if (cached) {\n (target as any)[key] = cached;\n continue;\n }\n\n const next: Record<PropertyKey, unknown> = isObjectPlain(objValue)\n ? (objValue as Record<PropertyKey, unknown>)\n : {};\n\n (target as any)[key] = next;\n\n // Cache before recursing to handle cycles and repeated refs.\n stack.set(srcValue as object, next as object);\n\n baseMerge(\n next,\n srcValue as Record<PropertyKey, unknown>,\n customizer,\n stack\n );\n continue;\n }\n\n // Arrays, primitives, dates, maps, sets, functions, etc. are assigned by replacement\n (target as any)[key] = srcValue;\n }\n};\n/**\n * Returns enumerable own property keys (string + symbol) for an object.\n *\n * This mirrors lodash's behaviour of considering both string keys and symbol keys,\n * but only those that are enumerable.\n *\n * @param obj - Object to inspect.\n * @returns Array of enumerable own keys (strings and symbols).\n *\n * @example\n * ```ts\n * const sym = Symbol(\"x\");\n * const o = { a: 1, [sym]: 2 };\n * Object.defineProperty(o, \"hidden\", { value: 3, enumerable: false });\n *\n * ownKeys(o);\n * // => [\"a\", Symbol(\"x\")]\n * ```\n */\nconst ownKeys = (obj: object): (string | symbol)[] => [\n ...Object.keys(obj),\n ...Object.getOwnPropertySymbols(obj).filter((s) =>\n Object.prototype.propertyIsEnumerable.call(obj, s)\n )\n];\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { isObjectLoose } from '@phun-ky/typeof';\n\nimport { mergeWith } from './utils/merge-with';\n\n/**\n * Deeply applies \"defaults\" from right-to-left sources into a new object, while preserving arrays.\n *\n * This function mimics the common lodash pattern:\n * `_.toArray(arguments).reverse().forEach(x => _.mergeWith(output, x, customizer))`,\n * but implemented in vanilla TypeScript with a small, predictable merge core.\n *\n * **Key behaviours**\n * - **Returns a new object** (the returned object is created inside the function).\n * - Sources are processed **right-to-left** (`reverse()`), meaning **earlier arguments win**:\n * values from the first argument are treated as \"defaults\" that should *not* be overwritten\n * by later arguments (because later arguments are merged first).\n * - Only **object-like** sources participate in merging. Non-objects are skipped via `isObjectLoose`.\n * - Uses {@link mergeWith} for the deep merge logic.\n * - **Arrays are preserved by replacement**: if a source value is an array, it replaces the\n * destination value at that key (rather than being merged element-by-element).\n *\n * @param args - A list of potential default/source values. Non-object values are ignored.\n * @returns A new object containing the merged result.\n *\n *\n * @example\n * ```ts\n * defaultsDeep(\n * { a: 1, nested: { x: 1 }, list: [1] },\n * { a: 2, nested: { y: 2 }, list: [2] }\n * );\n * // => { a: 1, nested: { x: 1, y: 2 }, list: [1] }\n * // (earlier args win; arrays preserved by replacement)\n * ```\n *\n * @example\n * ```ts\n * defaultsDeep(\n * { opts: { retry: 3 } },\n * null,\n * { opts: { timeout: 1000 } }\n * );\n * // => { opts: { retry: 3, timeout: 1000 } }\n * // (non-object sources are ignored)\n * ```\n *\n * @remarks\n *\n * - The customizer only checks the *source* value for arrays. If `s` is an array, it is returned\n * and assigned as-is. Otherwise returning `undefined` delegates merging to {@link mergeWith}'s\n * default behaviour (which deep-merges plain objects and replaces other value types).\n * - The output type is `Record<PropertyKey, unknown>` to keep the API broadly usable without\n * over-promising specific shapes.\n *\n */\nconst defaultsDeep = (...args: unknown[]) => {\n const output: Record<PropertyKey, unknown> = {};\n\n for (const item of args.slice().reverse()) {\n if (!isObjectLoose(item)) continue;\n\n mergeWith(output, item, (_o: any, s: any[] | undefined) =>\n Array.isArray(s) ? s : undefined\n );\n }\n\n return output;\n};\n\nexport default defaultsDeep;\n"],"names":["f","t","u","p","Object","prototype","toString","call","r","getPrototypeOf","l","mergeWith","object","args","maybeCustomizer","at","customizer","undefined","sources","slice","stack","WeakMap","source","baseMerge","UNSAFE_KEYS","Set","target","isObjectLoose","key","ownKeys","has","srcValue","objValue","customized","isNotUndefined","isObjectPlain","cached","get","set","next","obj","keys","getOwnPropertySymbols","filter","s","propertyIsEnumerable","defaultsDeep","output","item","reverse","_o","Array","isArray"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BoO,SAASA,EAAEC,GAAG,OAA9C,SAAWA,GAAG,YAAO,IAASA,CAAC,CAAsBC,CAAED,EAAE,CAA2B,SAASE,EAAEF,GAAG,GAAG,oBAAoBG,OAAOC,UAAUC,SAASC,KAAKN,GAAG,SAAS,MAAMO,EAAEJ,OAAOK,eAAeR,GAAG,OAAOO,IAAIJ,OAAOC,WAAW,OAAOG,CAAC,CAAqW,SAASE,EAAET,GAAG,MAAMO,SAASP,EAAE,OAAO,OAAOA,IAAI,WAAWO,GAAG,aAAaA,EAAE,UCyGj1BG,EACdC,KACGC,GAEH,MAAMC,EAAkBD,EAAKE,OACvBC,ED9GwmD,mBC8GhlDF,EACzBA,OACDG,EACJ,MAAMC,EAAWF,EAAaH,EAAKM,MAAM,GAAG,GAAMN,EAI5CO,EAAQ,IAAIC,QAElB,IAAK,MAAMC,KAAUJ,EACnBK,EACEX,EACAU,EACAN,EACAI,GAIJ,OAAOR,CACT,CAEA,MAAMY,EAAc,IAAIC,IAAY,CAAC,YAAa,cAAe,cAU3DF,EAAY,CAChBG,EACAJ,EACAN,EACAI,KAEA,GAAKO,EAAcL,GAEnB,IAAK,MAAMM,KAAOC,EAAQP,GAAS,CACjC,GAAmB,iBAARM,GAAoBJ,EAAYM,IAAIF,GAC7C,SAEF,MAAMG,EAAYT,EAAeM,GAC3BI,EAAYN,EAAeE,GAC3BK,EAAajB,IACjBgB,EACAD,EACAH,EACAF,EACAJ,EACAF,GAGF,GAAIc,EAAeD,GAChBP,EAAeE,GAAOK,MADzB,CAMA,GAAIE,EAAcH,IAAaG,EAAcJ,GAAW,CAEtD,MAAMK,EAAShB,EAAMiB,IAAIN,GAEzB,GAAIK,EAAQ,CACTV,EAAeE,GAAOQ,EACvB,QACF,CAGAhB,EAAMkB,IAAIP,EAAoBC,GAE9BT,EACES,EACAD,EACAf,EACAI,GAEF,QACF,CAGA,GAAIe,EAAcJ,GAAW,CAC3B,MAAMK,EAAShB,EAAMiB,IAAIN,GAEzB,GAAIK,EAAQ,CACTV,EAAeE,GAAOQ,EACvB,QACF,CAEA,MAAMG,EAAqCJ,EAAcH,GACpDA,EACD,CAAA,EAEHN,EAAeE,GAAOW,EAGvBnB,EAAMkB,IAAIP,EAAoBQ,GAE9BhB,EACEgB,EACAR,EACAf,EACAI,GAEF,QACF,CAGCM,EAAeE,GAAOG,CApDvB,CAqDF,GAqBIF,EAAWW,GAAqC,IACjDpC,OAAOqC,KAAKD,MACZpC,OAAOsC,sBAAsBF,GAAKG,OAAQC,GAC3CxC,OAAOC,UAAUwC,qBAAqBtC,KAAKiC,EAAKI,KCtN9CE,EAAe,IAAIjC,KACvB,MAAMkC,EAAuC,CAAA,EAE7C,IAAK,MAAMC,KAAQnC,EAAKM,QAAQ8B,UACzBtB,EAAcqB,IAEnBrC,EAAUoC,EAAQC,EAAM,CAACE,EAASN,IAChCO,MAAMC,QAAQR,GAAKA,OAAI3B,GAI3B,OAAO8B","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"defaults-deep.js","sources":["../src/utils/merge-with.ts","../src/main.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n isFunction,\n isNotUndefined,\n isObjectLoose,\n isObjectPlain\n} from '@phun-ky/typeof';\n\n/* node:coverage disable */\n/**\n * Customizer function used by {@link mergeWith} to override merge behaviour.\n *\n * If the customizer returns `undefined`, {@link mergeWith} falls back to its default\n * deep-merge rules. Any other return value will be assigned to the destination key.\n *\n * @param objValue - Current value on the destination (`object`) for `key`.\n * @param srcValue - Incoming value from the current `source` for `key`.\n * @param key - Property key being merged (string or symbol).\n * @param object - Destination object being mutated/merged into.\n * @param source - Current source object being merged from.\n * @param stack - A `WeakMap` used internally to prevent infinite recursion on circular references.\n * @returns The value to assign for this key, or `undefined` to use default merge behaviour.\n *\n * @example\n * ```ts\n * import { mergeWith, type MergeWithCustomizer } from \"./mergeWith\";\n *\n * const concatArrays: MergeWithCustomizer = (objValue, srcValue) => {\n * if (Array.isArray(objValue) && Array.isArray(srcValue)) {\n * return objValue.concat(srcValue);\n * }\n * return undefined;\n * };\n *\n * const a = { list: [1] };\n * const b = { list: [2] };\n *\n * mergeWith(a, b, concatArrays);\n * // => { list: [1, 2] }\n * ```\n */\n/* node:coverage enable */\nexport type MergeWithCustomizer = (\n objValue: unknown,\n srcValue: unknown,\n key: string | symbol,\n object: Record<PropertyKey, unknown>,\n source: Record<PropertyKey, unknown>,\n stack: WeakMap<object, object>\n) => unknown;\n\ntype UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (\n k: infer I\n) => void\n ? I\n : never;\n\ntype MergeResult<T, S extends readonly any[]> = T &\n UnionToIntersection<S[number]>;\n\n/**\n * Deep-merges one or more source objects into a destination object, with optional customization.\n *\n * This is a \"vanilla TS\" alternative to lodash's `mergeWith` with a deliberately conservative\n * default strategy:\n *\n * - **Mutates** and returns the destination `object`.\n * - Enumerates **own enumerable string keys** and **own enumerable symbol keys** from each source.\n * - When both destination and source values are **plain objects**, merges recursively.\n * - When the source value is a **plain object** but the destination is not, creates `{}` and merges into it.\n * - All other value types (arrays, dates, maps, sets, functions, primitives, class instances, etc.)\n * are assigned by **replacement** unless the customizer handles them.\n * - Circular references in **sources** are prevented from causing infinite recursion via a `WeakMap` stack.\n *\n * @typeParam T - Destination object type.\n * @typeParam S - Tuple/array of source object types.\n *\n * @param object - Destination object to merge into (will be mutated).\n * @param args - One or more source objects, optionally followed by a {@link MergeWithCustomizer}.\n * @returns The same `object` reference, now merged with all sources.\n *\n * @remarks\n * - The default merge behaviour only recurses into \"plain objects\", as determined by `isObjectPlain`.\n * - \"Loose objects\" are guarded using `isObjectLoose` to avoid attempting to merge non-object sources.\n * - If you want lodash-like array merging semantics (concat or index-wise), implement it via `customizer`.\n *\n * @example\n * ```ts\n * const target = { a: { x: 1 }, b: 1 };\n * const source = { a: { y: 2 }, b: 2 };\n *\n * mergeWith(target, source);\n * // => { a: { x: 1, y: 2 }, b: 2 }\n * // target is mutated\n * ```\n *\n * @example\n * ```ts\n * const target = { list: [1] };\n * const source = { list: [2] };\n *\n * mergeWith(target, source);\n * // => { list: [2] } (arrays replace by default)\n * ```\n *\n * @example\n * ```ts\n * const concatArrays: MergeWithCustomizer = (objValue, srcValue) => {\n * if (Array.isArray(objValue) && Array.isArray(srcValue)) {\n * return objValue.concat(srcValue);\n * }\n * };\n *\n * mergeWith({ list: [1] }, { list: [2] }, concatArrays);\n * // => { list: [1, 2] }\n * ```\n */\n\nexport function mergeWith<\n T extends Record<PropertyKey, any>,\n S extends readonly Record<PropertyKey, any>[]\n>(object: T, ...sources: S): MergeResult<T, S>;\n\nexport function mergeWith<\n T extends Record<PropertyKey, any>,\n S extends readonly Record<PropertyKey, any>[]\n>(\n object: T,\n ...args: [...sources: S, customizer: MergeWithCustomizer]\n): MergeResult<T, S>;\n\nexport function mergeWith(\n object: Record<PropertyKey, unknown>,\n ...args: (Record<PropertyKey, unknown> | MergeWithCustomizer)[]\n): Record<PropertyKey, unknown> {\n const maybeCustomizer = args.at(-1);\n const customizer = isFunction(maybeCustomizer)\n ? (maybeCustomizer as MergeWithCustomizer)\n : undefined;\n const sources = (customizer ? args.slice(0, -1) : args) as Record<\n PropertyKey,\n unknown\n >[];\n const stack = new WeakMap<object, object>();\n\n for (const source of sources) {\n baseMerge(\n object as Record<PropertyKey, unknown>,\n source,\n customizer,\n stack\n );\n }\n\n return object as any;\n}\n\nconst UNSAFE_KEYS = new Set<string>(['__proto__', 'constructor', 'prototype']);\n/**\n * Internal recursive merge implementation used by {@link mergeWith}.\n *\n * @param target - Destination object that is being mutated.\n * @param source - Current source object being merged in.\n * @param customizer - Optional custom merge override.\n * @param stack - The source objects on the current path, each mapped to its\n * destination, to stop at cycles. Only the current path: a source object\n * reached again through a sibling key gets its own destination.\n */\nconst baseMerge = (\n target: Record<PropertyKey, unknown>,\n source: Record<PropertyKey, unknown>,\n customizer: MergeWithCustomizer | undefined,\n stack: WeakMap<object, object>\n): void => {\n if (!isObjectLoose(source)) return;\n\n for (const key of ownKeys(source)) {\n if (typeof key === 'string' && UNSAFE_KEYS.has(key)) {\n continue;\n }\n\n const srcValue = (source as any)[key];\n const objValue = (target as any)[key];\n const customized = customizer?.(\n objValue,\n srcValue,\n key,\n target,\n source,\n stack\n );\n\n if (isNotUndefined(customized)) {\n (target as any)[key] = customized;\n continue;\n }\n\n // Both are plain objects: merge into existing destination object.\n if (isObjectPlain(objValue) && isObjectPlain(srcValue)) {\n // A cycle back to a source object on the current path: point at its destination.\n const cached = stack.get(srcValue as object);\n\n if (cached) {\n (target as any)[key] = cached;\n continue;\n }\n\n stack.set(srcValue as object, objValue as object);\n\n baseMerge(\n objValue as Record<PropertyKey, unknown>,\n srcValue as Record<PropertyKey, unknown>,\n customizer,\n stack\n );\n stack.delete(srcValue as object);\n continue;\n }\n\n // Source is a plain object but destination isn't: create destination object and merge into it.\n if (isObjectPlain(srcValue)) {\n const cached = stack.get(srcValue as object);\n\n if (cached) {\n (target as any)[key] = cached;\n continue;\n }\n\n const next: Record<PropertyKey, unknown> = isObjectPlain(objValue)\n ? (objValue as Record<PropertyKey, unknown>)\n : {};\n\n (target as any)[key] = next;\n\n stack.set(srcValue as object, next as object);\n\n baseMerge(\n next,\n srcValue as Record<PropertyKey, unknown>,\n customizer,\n stack\n );\n stack.delete(srcValue as object);\n continue;\n }\n\n // Arrays, primitives, dates, maps, sets, functions, etc. are assigned by replacement\n (target as any)[key] = srcValue;\n }\n};\n/**\n * Returns enumerable own property keys (string + symbol) for an object.\n *\n * This mirrors lodash's behaviour of considering both string keys and symbol keys,\n * but only those that are enumerable.\n *\n * @param obj - Object to inspect.\n * @returns Array of enumerable own keys (strings and symbols).\n *\n * @example\n * ```ts\n * const sym = Symbol(\"x\");\n * const o = { a: 1, [sym]: 2 };\n * Object.defineProperty(o, \"hidden\", { value: 3, enumerable: false });\n *\n * ownKeys(o);\n * // => [\"a\", Symbol(\"x\")]\n * ```\n */\nconst ownKeys = (obj: object): (string | symbol)[] => [\n ...Object.keys(obj),\n ...Object.getOwnPropertySymbols(obj).filter((s) =>\n Object.prototype.propertyIsEnumerable.call(obj, s)\n )\n];\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { isObjectLoose } from '@phun-ky/typeof';\n\nimport { mergeWith } from './utils/merge-with';\n\n/**\n * Deeply applies \"defaults\" from right-to-left sources into a new object, while preserving arrays.\n *\n * This function mimics the common lodash pattern:\n * `_.toArray(arguments).reverse().forEach(x => _.mergeWith(output, x, customizer))`,\n * but implemented in vanilla TypeScript with a small, predictable merge core.\n *\n * **Key behaviours**\n * - **Returns a new object** (the returned object is created inside the function).\n * - Sources are processed **right-to-left** (`reverse()`), meaning **earlier arguments win**:\n * values from the first argument are treated as \"defaults\" that should *not* be overwritten\n * by later arguments (because later arguments are merged first).\n * - Only **object-like** sources participate in merging. Non-objects are skipped via `isObjectLoose`.\n * - Uses an internal `mergeWith` for the deep merge logic.\n * - **Arrays are preserved by replacement**: if a source value is an array, it replaces the\n * destination value at that key (rather than being merged element-by-element).\n *\n * @param args - A list of potential default/source values. Non-object values are ignored.\n * @returns A new object containing the merged result.\n *\n *\n * @example\n * ```ts\n * defaultsDeep(\n * { a: 1, nested: { x: 1 }, list: [1] },\n * { a: 2, nested: { y: 2 }, list: [2] }\n * );\n * // => { a: 1, nested: { x: 1, y: 2 }, list: [1] }\n * // (earlier args win; arrays preserved by replacement)\n * ```\n *\n * @example\n * ```ts\n * defaultsDeep(\n * { opts: { retry: 3 } },\n * null,\n * { opts: { timeout: 1000 } }\n * );\n * // => { opts: { retry: 3, timeout: 1000 } }\n * // (non-object sources are ignored)\n * ```\n *\n * @remarks\n *\n * - The customizer only checks the *source* value for arrays. If `s` is an array, it is returned\n * and assigned as-is. Otherwise returning `undefined` delegates merging to `mergeWith`'s\n * default behaviour (which deep-merges plain objects and replaces other value types).\n * - The output type is `Record<PropertyKey, unknown>` to keep the API broadly usable without\n * over-promising specific shapes.\n *\n */\nconst defaultsDeep = (...args: unknown[]) => {\n const output: Record<PropertyKey, unknown> = {};\n\n for (const item of args.slice().reverse()) {\n if (!isObjectLoose(item)) continue;\n\n mergeWith(output, item, (_o: any, s: any[] | undefined) =>\n Array.isArray(s) ? s : undefined\n );\n }\n\n return output;\n};\n\nexport default defaultsDeep;\n"],"names":["mergeWith","object","args","maybeCustomizer","at","customizer","isFunction","undefined","sources","slice","stack","WeakMap","source","baseMerge","UNSAFE_KEYS","Set","target","isObjectLoose","key","ownKeys","has","srcValue","objValue","customized","isNotUndefined","isObjectPlain","cached","get","set","delete","next","obj","Object","keys","getOwnPropertySymbols","filter","s","prototype","propertyIsEnumerable","call","defaultsDeep","output","item","reverse","_o","Array","isArray"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gHAmIgBA,EACdC,KACGC,GAEH,MAAMC,EAAkBD,EAAKE,OACvBC,EAAaC,EAAWH,GACzBA,OACDI,EACEC,EAAWH,EAAaH,EAAKO,MAAM,GAAG,GAAMP,EAI5CQ,EAAQ,IAAIC,QAElB,IAAK,MAAMC,KAAUJ,EACnBK,EACEZ,EACAW,EACAP,EACAK,GAIJ,OAAOT,CACT,CAEA,MAAMa,EAAc,IAAIC,IAAY,CAAC,YAAa,cAAe,cAW3DF,EAAY,CAChBG,EACAJ,EACAP,EACAK,KAEA,GAAKO,EAAcL,GAEnB,IAAK,MAAMM,KAAOC,EAAQP,GAAS,CACjC,GAAmB,iBAARM,GAAoBJ,EAAYM,IAAIF,GAC7C,SAGF,MAAMG,EAAYT,EAAeM,GAC3BI,EAAYN,EAAeE,GAC3BK,EAAalB,IACjBiB,EACAD,EACAH,EACAF,EACAJ,EACAF,GAGF,GAAIc,EAAeD,GAChBP,EAAeE,GAAOK,MADzB,CAMA,GAAIE,EAAcH,IAAaG,EAAcJ,GAAW,CAEtD,MAAMK,EAAShB,EAAMiB,IAAIN,GAEzB,GAAIK,EAAQ,CACTV,EAAeE,GAAOQ,EACvB,QACF,CAEAhB,EAAMkB,IAAIP,EAAoBC,GAE9BT,EACES,EACAD,EACAhB,EACAK,GAEFA,EAAMmB,OAAOR,GACb,QACF,CAGA,GAAII,EAAcJ,GAAW,CAC3B,MAAMK,EAAShB,EAAMiB,IAAIN,GAEzB,GAAIK,EAAQ,CACTV,EAAeE,GAAOQ,EACvB,QACF,CAEA,MAAMI,EAAqCL,EAAcH,GACpDA,EACD,CAAA,EAEHN,EAAeE,GAAOY,EAEvBpB,EAAMkB,IAAIP,EAAoBS,GAE9BjB,EACEiB,EACAT,EACAhB,EACAK,GAEFA,EAAMmB,OAAOR,GACb,QACF,CAGCL,EAAeE,GAAOG,CApDvB,CAqDF,GAqBIF,EAAWY,GAAqC,IACjDC,OAAOC,KAAKF,MACZC,OAAOE,sBAAsBH,GAAKI,OAAQC,GAC3CJ,OAAOK,UAAUC,qBAAqBC,KAAKR,EAAKK,KCxN9CI,EAAe,IAAItC,KACvB,MAAMuC,EAAuC,CAAA,EAE7C,IAAK,MAAMC,KAAQxC,EAAKO,QAAQkC,UACzB1B,EAAcyB,IAEnB1C,EAAUyC,EAAQC,EAAM,CAACE,EAASR,IAChCS,MAAMC,QAAQV,GAAKA,OAAI7B,GAI3B,OAAOkC"}
package/package.json CHANGED
@@ -1,40 +1,55 @@
1
1
  {
2
2
  "name": "@phun-ky/defaults-deep",
3
- "version": "2.0.7",
3
+ "version": "2.0.9",
4
4
  "description": "Like lodash's _.defaultsDeep, but with array preservation",
5
5
  "type": "module",
6
6
  "main": "./dist/defaults-deep.cjs",
7
7
  "module": "./dist/defaults-deep.js",
8
- "types": "./dist/defaults-deep.d.ts",
8
+ "types": "./dist/defaults-deep.d.cts",
9
9
  "files": [
10
- "/dist/defaults-deep.js",
11
- "/dist/defaults-deep.js.map",
12
- "/dist/defaults-deep.cjs",
13
- "/dist/defaults-deep.cjs.map",
14
- "/dist/defaults-deep.d.ts"
10
+ "dist",
11
+ "src",
12
+ "!src/**/__tests__",
13
+ "CHANGELOG.md",
14
+ "LICENSE-MIT",
15
+ "LICENSE-ISC"
15
16
  ],
16
17
  "exports": {
17
18
  ".": {
18
- "types": "./dist/defaults-deep.d.ts",
19
- "import": "./dist/defaults-deep.js",
20
- "require": "./dist/defaults-deep.cjs"
21
- }
19
+ "import": {
20
+ "types": "./dist/defaults-deep.d.ts",
21
+ "default": "./dist/defaults-deep.js"
22
+ },
23
+ "require": {
24
+ "types": "./dist/defaults-deep.d.cts",
25
+ "default": "./dist/defaults-deep.cjs"
26
+ }
27
+ },
28
+ "./package.json": "./package.json",
29
+ "./dist/*": "./dist/*"
22
30
  },
31
+ "sideEffects": false,
23
32
  "scripts": {
24
33
  "build": "npm run clean && npm run rollup",
34
+ "check:pack": "node scripts/check-pack.mjs",
25
35
  "clean": "rm -rf dist dts",
26
36
  "commit": "npx git-cz",
37
+ "docs:check": "typedoc --emit none --treatWarningsAsErrors",
27
38
  "docs:gen": "node ./node_modules/.bin/typedoc",
28
- "release": "npx release-it --ci",
29
- "rollup": "rollup -c",
39
+ "prepare": "git config core.hooksPath .githooks || true",
30
40
  "prerollup:dev": "npm run clean",
31
- "rollup:dev": "rollup -c -w",
32
- "style:code": "npx putout src",
33
- "style:format": "eslint -c ./eslint.config.mjs src/**/*.ts --fix --no-warn-ignored && ./node_modules/.bin/prettier --write ./eslint.config.mjs src",
34
- "style:lint": "eslint -c ./eslint.config.mjs src/**/*.ts --no-warn-ignored && ./node_modules/.bin/prettier --check src",
35
- "test": "glob-bin -c \"node --import tsx --import global-jsdom/register --test --no-warnings\" \"./src/**/__tests__/**/*.[jt]s\"",
36
41
  "pretest:ci": "rm -rf coverage && mkdir -p coverage",
37
- "test:ci": "glob-bin -c \"node --import tsx --import global-jsdom/register --test --no-warnings --experimental-test-coverage --test-reporter=cobertura --test-reporter-destination=coverage/cobertura-coverage.xml --test-reporter=spec --test-reporter-destination=stdout\" \"./src/**/__tests__/**/*.[jt]s\""
42
+ "release": "release-it",
43
+ "release:dry-run": ".github/scripts/should-release.sh && release-it --ci --dry-run --github.release=false --no-git.requireCleanWorkingDir --no-git.requireUpstream",
44
+ "rollup": "rollup -c",
45
+ "rollup:dev": "rollup -c -w",
46
+ "sbom": "node scripts/sbom.mjs",
47
+ "style:format": "eslint . --fix && prettier --write \"**/*.{json,yml,yaml}\"",
48
+ "style:format-check": "prettier --check \"**/*.{json,yml,yaml}\"",
49
+ "style:lint": "eslint .",
50
+ "test": "node --import tsx --test --enable-source-maps \"src/**/__tests__/**/*.ts\"",
51
+ "test:ci": "node --import tsx --test --enable-source-maps --experimental-test-coverage --test-coverage-include=\"src/**/*.ts\" --test-coverage-exclude=\"src/**/__tests__/**\" --test-coverage-lines=99 --test-coverage-functions=100 --test-coverage-branches=97 --test-reporter=cobertura --test-reporter-destination=coverage/cobertura-coverage.xml --test-reporter=spec --test-reporter-destination=stdout \"src/**/__tests__/**/*.ts\"",
52
+ "typecheck": "tsc -p tsconfig.json"
38
53
  },
39
54
  "funding": "https://github.com/phun-ky/defaults-deep?sponsor=1",
40
55
  "repository": {
@@ -115,46 +130,52 @@
115
130
  },
116
131
  "homepage": "https://github.com/phun-ky/defaults-deep#readme",
117
132
  "devDependencies": {
118
- "@release-it/conventional-changelog": "^11.0.0",
133
+ "@arethetypeswrong/cli": "^0.18.5",
134
+ "@release-it/conventional-changelog": "^12.0.2",
119
135
  "@rollup/plugin-node-resolve": "^16.0.1",
120
136
  "@rollup/plugin-terser": "^1.0.0",
121
- "@types/node": "^25.9.2",
137
+ "@types/node": "^24.19.2",
122
138
  "cobertura": "^1.0.1",
123
139
  "eslint": "^10.0.0",
124
- "eslint-config-phun-ky": "^2.0.0",
140
+ "eslint-config-phun-ky": "^2.0.10",
141
+ "fast-check": "^4.10.2",
125
142
  "git-cz": "^4.9.0",
126
- "glob-bin": "^1.0.0",
127
- "global-jsdom": "^29.0.0",
128
- "jsdom": "^29.0.2",
143
+ "globals": "^17.13.0",
129
144
  "prettier": "^3.8.0",
130
- "putout": "^42.0.13",
131
- "quibble": "^0.9.1",
132
- "release-it": "^20.0.1",
145
+ "publint": "^0.3.25",
146
+ "release-it": "^21.1.1",
133
147
  "remark-github": "^12.0.0",
134
148
  "remark-toc": "^9.0.0",
135
149
  "rollup": "^4.12.0",
136
150
  "rollup-plugin-dts": "^6.1.0",
137
151
  "rollup-plugin-typescript2": "^0.37.0",
138
- "tslib": "^2.3.1",
139
152
  "tsx": "^4.7.1",
140
153
  "typedoc": "^0.28.2",
141
- "typedoc-plugin-frontmatter": "^1.0.0",
142
154
  "typedoc-plugin-markdown": "^4.2.3",
143
155
  "typedoc-plugin-mdn-links": "^5.0.1",
144
- "typedoc-plugin-no-inherit": "^1.4.0",
145
156
  "typedoc-plugin-remark": "^2.0.0",
146
157
  "typedoc-plugin-rename-defaults": "^0.7.1",
147
158
  "typescript": "^6.0.2",
148
159
  "unified-prettier": "^2.0.1"
149
160
  },
150
161
  "engines": {
151
- "node": ">=22.9.0",
152
- "npm": ">=11.0.0"
153
- },
154
- "dependencies": {
155
- "@phun-ky/typeof": "^3.0.0"
162
+ "node": ">=22.9.0"
156
163
  },
157
164
  "publishConfig": {
158
165
  "access": "public"
166
+ },
167
+ "overrides": {
168
+ "get-uri": {
169
+ "basic-ftp": "^6.2.3"
170
+ },
171
+ "release-it": {
172
+ "undici": "^7.30.1"
173
+ },
174
+ "micromark-extension-math": {
175
+ "katex": "^0.18.2"
176
+ }
177
+ },
178
+ "dependencies": {
179
+ "@phun-ky/typeof": "^3.0.9"
159
180
  }
160
181
  }
package/src/main.ts ADDED
@@ -0,0 +1,71 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { isObjectLoose } from '@phun-ky/typeof';
3
+
4
+ import { mergeWith } from './utils/merge-with';
5
+
6
+ /**
7
+ * Deeply applies "defaults" from right-to-left sources into a new object, while preserving arrays.
8
+ *
9
+ * This function mimics the common lodash pattern:
10
+ * `_.toArray(arguments).reverse().forEach(x => _.mergeWith(output, x, customizer))`,
11
+ * but implemented in vanilla TypeScript with a small, predictable merge core.
12
+ *
13
+ * **Key behaviours**
14
+ * - **Returns a new object** (the returned object is created inside the function).
15
+ * - Sources are processed **right-to-left** (`reverse()`), meaning **earlier arguments win**:
16
+ * values from the first argument are treated as "defaults" that should *not* be overwritten
17
+ * by later arguments (because later arguments are merged first).
18
+ * - Only **object-like** sources participate in merging. Non-objects are skipped via `isObjectLoose`.
19
+ * - Uses an internal `mergeWith` for the deep merge logic.
20
+ * - **Arrays are preserved by replacement**: if a source value is an array, it replaces the
21
+ * destination value at that key (rather than being merged element-by-element).
22
+ *
23
+ * @param args - A list of potential default/source values. Non-object values are ignored.
24
+ * @returns A new object containing the merged result.
25
+ *
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * defaultsDeep(
30
+ * { a: 1, nested: { x: 1 }, list: [1] },
31
+ * { a: 2, nested: { y: 2 }, list: [2] }
32
+ * );
33
+ * // => { a: 1, nested: { x: 1, y: 2 }, list: [1] }
34
+ * // (earlier args win; arrays preserved by replacement)
35
+ * ```
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * defaultsDeep(
40
+ * { opts: { retry: 3 } },
41
+ * null,
42
+ * { opts: { timeout: 1000 } }
43
+ * );
44
+ * // => { opts: { retry: 3, timeout: 1000 } }
45
+ * // (non-object sources are ignored)
46
+ * ```
47
+ *
48
+ * @remarks
49
+ *
50
+ * - The customizer only checks the *source* value for arrays. If `s` is an array, it is returned
51
+ * and assigned as-is. Otherwise returning `undefined` delegates merging to `mergeWith`'s
52
+ * default behaviour (which deep-merges plain objects and replaces other value types).
53
+ * - The output type is `Record<PropertyKey, unknown>` to keep the API broadly usable without
54
+ * over-promising specific shapes.
55
+ *
56
+ */
57
+ const defaultsDeep = (...args: unknown[]) => {
58
+ const output: Record<PropertyKey, unknown> = {};
59
+
60
+ for (const item of args.slice().reverse()) {
61
+ if (!isObjectLoose(item)) continue;
62
+
63
+ mergeWith(output, item, (_o: any, s: any[] | undefined) =>
64
+ Array.isArray(s) ? s : undefined
65
+ );
66
+ }
67
+
68
+ return output;
69
+ };
70
+
71
+ export default defaultsDeep;
@@ -0,0 +1,275 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import {
3
+ isFunction,
4
+ isNotUndefined,
5
+ isObjectLoose,
6
+ isObjectPlain
7
+ } from '@phun-ky/typeof';
8
+
9
+ /* node:coverage disable */
10
+ /**
11
+ * Customizer function used by {@link mergeWith} to override merge behaviour.
12
+ *
13
+ * If the customizer returns `undefined`, {@link mergeWith} falls back to its default
14
+ * deep-merge rules. Any other return value will be assigned to the destination key.
15
+ *
16
+ * @param objValue - Current value on the destination (`object`) for `key`.
17
+ * @param srcValue - Incoming value from the current `source` for `key`.
18
+ * @param key - Property key being merged (string or symbol).
19
+ * @param object - Destination object being mutated/merged into.
20
+ * @param source - Current source object being merged from.
21
+ * @param stack - A `WeakMap` used internally to prevent infinite recursion on circular references.
22
+ * @returns The value to assign for this key, or `undefined` to use default merge behaviour.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * import { mergeWith, type MergeWithCustomizer } from "./mergeWith";
27
+ *
28
+ * const concatArrays: MergeWithCustomizer = (objValue, srcValue) => {
29
+ * if (Array.isArray(objValue) && Array.isArray(srcValue)) {
30
+ * return objValue.concat(srcValue);
31
+ * }
32
+ * return undefined;
33
+ * };
34
+ *
35
+ * const a = { list: [1] };
36
+ * const b = { list: [2] };
37
+ *
38
+ * mergeWith(a, b, concatArrays);
39
+ * // => { list: [1, 2] }
40
+ * ```
41
+ */
42
+ /* node:coverage enable */
43
+ export type MergeWithCustomizer = (
44
+ objValue: unknown,
45
+ srcValue: unknown,
46
+ key: string | symbol,
47
+ object: Record<PropertyKey, unknown>,
48
+ source: Record<PropertyKey, unknown>,
49
+ stack: WeakMap<object, object>
50
+ ) => unknown;
51
+
52
+ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
53
+ k: infer I
54
+ ) => void
55
+ ? I
56
+ : never;
57
+
58
+ type MergeResult<T, S extends readonly any[]> = T &
59
+ UnionToIntersection<S[number]>;
60
+
61
+ /**
62
+ * Deep-merges one or more source objects into a destination object, with optional customization.
63
+ *
64
+ * This is a "vanilla TS" alternative to lodash's `mergeWith` with a deliberately conservative
65
+ * default strategy:
66
+ *
67
+ * - **Mutates** and returns the destination `object`.
68
+ * - Enumerates **own enumerable string keys** and **own enumerable symbol keys** from each source.
69
+ * - When both destination and source values are **plain objects**, merges recursively.
70
+ * - When the source value is a **plain object** but the destination is not, creates `{}` and merges into it.
71
+ * - All other value types (arrays, dates, maps, sets, functions, primitives, class instances, etc.)
72
+ * are assigned by **replacement** unless the customizer handles them.
73
+ * - Circular references in **sources** are prevented from causing infinite recursion via a `WeakMap` stack.
74
+ *
75
+ * @typeParam T - Destination object type.
76
+ * @typeParam S - Tuple/array of source object types.
77
+ *
78
+ * @param object - Destination object to merge into (will be mutated).
79
+ * @param args - One or more source objects, optionally followed by a {@link MergeWithCustomizer}.
80
+ * @returns The same `object` reference, now merged with all sources.
81
+ *
82
+ * @remarks
83
+ * - The default merge behaviour only recurses into "plain objects", as determined by `isObjectPlain`.
84
+ * - "Loose objects" are guarded using `isObjectLoose` to avoid attempting to merge non-object sources.
85
+ * - If you want lodash-like array merging semantics (concat or index-wise), implement it via `customizer`.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * const target = { a: { x: 1 }, b: 1 };
90
+ * const source = { a: { y: 2 }, b: 2 };
91
+ *
92
+ * mergeWith(target, source);
93
+ * // => { a: { x: 1, y: 2 }, b: 2 }
94
+ * // target is mutated
95
+ * ```
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * const target = { list: [1] };
100
+ * const source = { list: [2] };
101
+ *
102
+ * mergeWith(target, source);
103
+ * // => { list: [2] } (arrays replace by default)
104
+ * ```
105
+ *
106
+ * @example
107
+ * ```ts
108
+ * const concatArrays: MergeWithCustomizer = (objValue, srcValue) => {
109
+ * if (Array.isArray(objValue) && Array.isArray(srcValue)) {
110
+ * return objValue.concat(srcValue);
111
+ * }
112
+ * };
113
+ *
114
+ * mergeWith({ list: [1] }, { list: [2] }, concatArrays);
115
+ * // => { list: [1, 2] }
116
+ * ```
117
+ */
118
+
119
+ export function mergeWith<
120
+ T extends Record<PropertyKey, any>,
121
+ S extends readonly Record<PropertyKey, any>[]
122
+ >(object: T, ...sources: S): MergeResult<T, S>;
123
+
124
+ export function mergeWith<
125
+ T extends Record<PropertyKey, any>,
126
+ S extends readonly Record<PropertyKey, any>[]
127
+ >(
128
+ object: T,
129
+ ...args: [...sources: S, customizer: MergeWithCustomizer]
130
+ ): MergeResult<T, S>;
131
+
132
+ export function mergeWith(
133
+ object: Record<PropertyKey, unknown>,
134
+ ...args: (Record<PropertyKey, unknown> | MergeWithCustomizer)[]
135
+ ): Record<PropertyKey, unknown> {
136
+ const maybeCustomizer = args.at(-1);
137
+ const customizer = isFunction(maybeCustomizer)
138
+ ? (maybeCustomizer as MergeWithCustomizer)
139
+ : undefined;
140
+ const sources = (customizer ? args.slice(0, -1) : args) as Record<
141
+ PropertyKey,
142
+ unknown
143
+ >[];
144
+ const stack = new WeakMap<object, object>();
145
+
146
+ for (const source of sources) {
147
+ baseMerge(
148
+ object as Record<PropertyKey, unknown>,
149
+ source,
150
+ customizer,
151
+ stack
152
+ );
153
+ }
154
+
155
+ return object as any;
156
+ }
157
+
158
+ const UNSAFE_KEYS = new Set<string>(['__proto__', 'constructor', 'prototype']);
159
+ /**
160
+ * Internal recursive merge implementation used by {@link mergeWith}.
161
+ *
162
+ * @param target - Destination object that is being mutated.
163
+ * @param source - Current source object being merged in.
164
+ * @param customizer - Optional custom merge override.
165
+ * @param stack - The source objects on the current path, each mapped to its
166
+ * destination, to stop at cycles. Only the current path: a source object
167
+ * reached again through a sibling key gets its own destination.
168
+ */
169
+ const baseMerge = (
170
+ target: Record<PropertyKey, unknown>,
171
+ source: Record<PropertyKey, unknown>,
172
+ customizer: MergeWithCustomizer | undefined,
173
+ stack: WeakMap<object, object>
174
+ ): void => {
175
+ if (!isObjectLoose(source)) return;
176
+
177
+ for (const key of ownKeys(source)) {
178
+ if (typeof key === 'string' && UNSAFE_KEYS.has(key)) {
179
+ continue;
180
+ }
181
+
182
+ const srcValue = (source as any)[key];
183
+ const objValue = (target as any)[key];
184
+ const customized = customizer?.(
185
+ objValue,
186
+ srcValue,
187
+ key,
188
+ target,
189
+ source,
190
+ stack
191
+ );
192
+
193
+ if (isNotUndefined(customized)) {
194
+ (target as any)[key] = customized;
195
+ continue;
196
+ }
197
+
198
+ // Both are plain objects: merge into existing destination object.
199
+ if (isObjectPlain(objValue) && isObjectPlain(srcValue)) {
200
+ // A cycle back to a source object on the current path: point at its destination.
201
+ const cached = stack.get(srcValue as object);
202
+
203
+ if (cached) {
204
+ (target as any)[key] = cached;
205
+ continue;
206
+ }
207
+
208
+ stack.set(srcValue as object, objValue as object);
209
+
210
+ baseMerge(
211
+ objValue as Record<PropertyKey, unknown>,
212
+ srcValue as Record<PropertyKey, unknown>,
213
+ customizer,
214
+ stack
215
+ );
216
+ stack.delete(srcValue as object);
217
+ continue;
218
+ }
219
+
220
+ // Source is a plain object but destination isn't: create destination object and merge into it.
221
+ if (isObjectPlain(srcValue)) {
222
+ const cached = stack.get(srcValue as object);
223
+
224
+ if (cached) {
225
+ (target as any)[key] = cached;
226
+ continue;
227
+ }
228
+
229
+ const next: Record<PropertyKey, unknown> = isObjectPlain(objValue)
230
+ ? (objValue as Record<PropertyKey, unknown>)
231
+ : {};
232
+
233
+ (target as any)[key] = next;
234
+
235
+ stack.set(srcValue as object, next as object);
236
+
237
+ baseMerge(
238
+ next,
239
+ srcValue as Record<PropertyKey, unknown>,
240
+ customizer,
241
+ stack
242
+ );
243
+ stack.delete(srcValue as object);
244
+ continue;
245
+ }
246
+
247
+ // Arrays, primitives, dates, maps, sets, functions, etc. are assigned by replacement
248
+ (target as any)[key] = srcValue;
249
+ }
250
+ };
251
+ /**
252
+ * Returns enumerable own property keys (string + symbol) for an object.
253
+ *
254
+ * This mirrors lodash's behaviour of considering both string keys and symbol keys,
255
+ * but only those that are enumerable.
256
+ *
257
+ * @param obj - Object to inspect.
258
+ * @returns Array of enumerable own keys (strings and symbols).
259
+ *
260
+ * @example
261
+ * ```ts
262
+ * const sym = Symbol("x");
263
+ * const o = { a: 1, [sym]: 2 };
264
+ * Object.defineProperty(o, "hidden", { value: 3, enumerable: false });
265
+ *
266
+ * ownKeys(o);
267
+ * // => ["a", Symbol("x")]
268
+ * ```
269
+ */
270
+ const ownKeys = (obj: object): (string | symbol)[] => [
271
+ ...Object.keys(obj),
272
+ ...Object.getOwnPropertySymbols(obj).filter((s) =>
273
+ Object.prototype.propertyIsEnumerable.call(obj, s)
274
+ )
275
+ ];