@stackline/deepmerge 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/CONTRIBUTING.md +51 -0
- package/LICENSE +21 -0
- package/NOTICE +14 -0
- package/README.md +311 -0
- package/SECURITY.md +50 -0
- package/dist/index.cjs +327 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.cts +86 -0
- package/dist/index.d.mts +84 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +303 -0
- package/dist/index.js.map +7 -0
- package/dist/index.min.js +4 -0
- package/dist/index.min.js.map +7 -0
- package/package.json +99 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes are documented here. This project follows Semantic
|
|
4
|
+
Versioning.
|
|
5
|
+
|
|
6
|
+
## [1.0.0] - 2026-08-16
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- Immutable deep object and array merge with zero runtime dependencies.
|
|
11
|
+
- Default API compatibility with `deepmerge` v4, including `all`,
|
|
12
|
+
`arrayMerge`, `customMerge`, `clone`, and `isMergeableObject`.
|
|
13
|
+
- Prototype-pollution protection for `__proto__`, `prototype`, and
|
|
14
|
+
`constructor` in target and source objects at every depth.
|
|
15
|
+
- Optional strict unsafe-key rejection through `onUnsafeKey: 'throw'`.
|
|
16
|
+
- Circular and shared-reference preservation.
|
|
17
|
+
- Configurable `maxDepth` and `maxKeys` traversal limits.
|
|
18
|
+
- ESM, callable CommonJS, browser-global, and source-map distributions.
|
|
19
|
+
- TypeScript declarations tested from 3.9 through 7.0.
|
|
20
|
+
- Differential compatibility tests against `deepmerge@4.3.1`.
|
|
21
|
+
- Public documentation, security policy, CI matrix, and live playground.
|
|
22
|
+
|
|
23
|
+
[1.0.0]: https://github.com/alexandroit/stackline-deepmerge/releases/tag/v1.0.0
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thank you for improving `@stackline/deepmerge`.
|
|
4
|
+
|
|
5
|
+
## Development
|
|
6
|
+
|
|
7
|
+
Use Node.js 20 or newer for the development toolchain. The published runtime
|
|
8
|
+
is separately tested on older supported Node.js versions.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm ci
|
|
12
|
+
npm test
|
|
13
|
+
npm run test:attw
|
|
14
|
+
npm run benchmark
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Pull requests
|
|
18
|
+
|
|
19
|
+
Keep each change focused. A pull request should include:
|
|
20
|
+
|
|
21
|
+
- a clear behavior statement;
|
|
22
|
+
- tests for new or changed behavior;
|
|
23
|
+
- compatibility impact;
|
|
24
|
+
- security impact when object traversal changes;
|
|
25
|
+
- documentation for public API changes.
|
|
26
|
+
|
|
27
|
+
Do not weaken dangerous-key filtering, cycle handling, or resource limits to
|
|
28
|
+
gain benchmark throughput.
|
|
29
|
+
|
|
30
|
+
## Compatibility changes
|
|
31
|
+
|
|
32
|
+
The default API intentionally tracks documented `deepmerge` v4 behavior.
|
|
33
|
+
Before changing merge semantics:
|
|
34
|
+
|
|
35
|
+
1. add a focused local test;
|
|
36
|
+
2. add or update a differential compatibility case;
|
|
37
|
+
3. document intentional differences;
|
|
38
|
+
4. validate direct and npm-alias installation shapes.
|
|
39
|
+
|
|
40
|
+
## Security changes
|
|
41
|
+
|
|
42
|
+
Security fixes should not be discussed in a public issue before a patched
|
|
43
|
+
release exists. Follow [SECURITY.md](SECURITY.md).
|
|
44
|
+
|
|
45
|
+
## Commits
|
|
46
|
+
|
|
47
|
+
Use a short imperative subject. Keep generated output and unrelated formatting
|
|
48
|
+
out of the same commit.
|
|
49
|
+
|
|
50
|
+
By contributing, you agree that your contribution is licensed under the MIT
|
|
51
|
+
License.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alexandro Paixao Marques and contributors
|
|
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/NOTICE
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
@stackline/deepmerge
|
|
2
|
+
Copyright (c) 2026 Alexandro Paixao Marques and contributors
|
|
3
|
+
|
|
4
|
+
This package is an independent implementation. Its public behavior is designed
|
|
5
|
+
to support migration from deepmerge version 4, a separate MIT-licensed project
|
|
6
|
+
by James Halliday, Josh Duff, and other contributors:
|
|
7
|
+
|
|
8
|
+
https://github.com/TehShrike/deepmerge
|
|
9
|
+
|
|
10
|
+
The upstream package is used as a development-only behavioral reference in the
|
|
11
|
+
differential compatibility tests. It is not included as a runtime dependency.
|
|
12
|
+
|
|
13
|
+
The name "deepmerge" is used descriptively for API compatibility. This project
|
|
14
|
+
is not affiliated with or endorsed by the upstream maintainers.
|
package/README.md
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# @stackline/deepmerge
|
|
2
|
+
|
|
3
|
+
> Secure, immutable, zero-dependency deep merge for modern JavaScript, with a
|
|
4
|
+
> `deepmerge`-compatible API.
|
|
5
|
+
|
|
6
|
+
[](https://www.npmjs.com/package/@stackline/deepmerge)
|
|
7
|
+
[](https://www.npmjs.com/package/@stackline/deepmerge)
|
|
8
|
+
[](https://github.com/alexandroit/stackline-deepmerge/actions/workflows/ci.yml)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
[](package.json)
|
|
11
|
+
|
|
12
|
+
**[Docs and playground](https://alexandro.net/docs/vanilla/deepmerge/)** |
|
|
13
|
+
**[npm](https://www.npmjs.com/package/@stackline/deepmerge)** |
|
|
14
|
+
**[Security](SECURITY.md)** |
|
|
15
|
+
**[Changelog](CHANGELOG.md)** |
|
|
16
|
+
**[Issues](https://github.com/alexandroit/stackline-deepmerge/issues)**
|
|
17
|
+
|
|
18
|
+
## Why this package?
|
|
19
|
+
|
|
20
|
+
Deep merge sits on a trust boundary in configuration loaders, build tools,
|
|
21
|
+
servers, CLIs, and browser applications. A useful replacement must be safe for
|
|
22
|
+
untrusted object keys without forcing existing projects to rewrite every merge.
|
|
23
|
+
|
|
24
|
+
`@stackline/deepmerge` combines:
|
|
25
|
+
|
|
26
|
+
- rejection of `__proto__`, `prototype`, and `constructor` at every depth;
|
|
27
|
+
- immutable merges with cycle and shared-reference preservation;
|
|
28
|
+
- configurable depth and key limits for hostile or malformed inputs;
|
|
29
|
+
- the familiar `deepmerge` v4 default API and extension hooks;
|
|
30
|
+
- ESM, callable CommonJS, TypeScript, and browser builds;
|
|
31
|
+
- TypeScript compatibility tested from 3.9 through 7.0;
|
|
32
|
+
- zero runtime dependencies.
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
Install under the package's public name:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install @stackline/deepmerge
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or replace `deepmerge` without changing application imports:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install deepmerge@npm:@stackline/deepmerge
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Existing code can continue to use:
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
import merge from 'deepmerge';
|
|
52
|
+
|
|
53
|
+
const config = merge(defaults, environment);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Quick start
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
import merge from '@stackline/deepmerge';
|
|
60
|
+
|
|
61
|
+
const defaults = {
|
|
62
|
+
server: { port: 3000, headers: { accept: 'application/json' } },
|
|
63
|
+
plugins: ['core']
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const production = {
|
|
67
|
+
server: { port: 8080, headers: { authorization: 'Bearer token' } },
|
|
68
|
+
plugins: ['metrics']
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const config = merge(defaults, production);
|
|
72
|
+
|
|
73
|
+
// {
|
|
74
|
+
// server: {
|
|
75
|
+
// port: 8080,
|
|
76
|
+
// headers: {
|
|
77
|
+
// accept: 'application/json',
|
|
78
|
+
// authorization: 'Bearer token'
|
|
79
|
+
// }
|
|
80
|
+
// },
|
|
81
|
+
// plugins: ['core', 'metrics']
|
|
82
|
+
// }
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Neither input is mutated.
|
|
86
|
+
|
|
87
|
+
## Secure by default
|
|
88
|
+
|
|
89
|
+
Dangerous keys are skipped from both inputs before their values are read:
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
import merge from '@stackline/deepmerge';
|
|
93
|
+
|
|
94
|
+
const payload = JSON.parse(`{
|
|
95
|
+
"profile": {
|
|
96
|
+
"name": "Ada",
|
|
97
|
+
"constructor": {
|
|
98
|
+
"prototype": { "isAdmin": true }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}`);
|
|
102
|
+
|
|
103
|
+
const result = merge({}, payload);
|
|
104
|
+
|
|
105
|
+
console.log(result); // { profile: { name: 'Ada' } }
|
|
106
|
+
console.log(Object.prototype.isAdmin); // undefined
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Use strict rejection when silent filtering is not appropriate:
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
merge({}, payload, { onUnsafeKey: 'throw' });
|
|
113
|
+
// UnsafeKeyError: Refusing to merge unsafe key constructor at
|
|
114
|
+
// <root>.profile.constructor
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Security limits are enabled by default:
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
merge(target, source, {
|
|
121
|
+
maxDepth: 1000,
|
|
122
|
+
maxKeys: 100000
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Set a smaller limit at an exposed API boundary. `Infinity` is accepted when
|
|
127
|
+
the input is already trusted.
|
|
128
|
+
|
|
129
|
+
## API compatibility
|
|
130
|
+
|
|
131
|
+
### `merge(target, source, options?)`
|
|
132
|
+
|
|
133
|
+
Returns a new merged value. Objects merge recursively. Arrays concatenate by
|
|
134
|
+
default. When an array and object occupy the same position, the source wins.
|
|
135
|
+
|
|
136
|
+
### `merge.all(objects, options?)`
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
const config = merge.all([
|
|
140
|
+
{ logging: { level: 'info' } },
|
|
141
|
+
{ logging: { format: 'json' } },
|
|
142
|
+
{ region: 'ca-central-1' }
|
|
143
|
+
]);
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Options
|
|
147
|
+
|
|
148
|
+
| Option | Default | Purpose |
|
|
149
|
+
| :--- | :--- | :--- |
|
|
150
|
+
| `arrayMerge` | concatenate | Replace or customize array behavior |
|
|
151
|
+
| `clone` | `true` | Set `false` to preserve nested input references |
|
|
152
|
+
| `customMerge` | none | Select a merge function for a property |
|
|
153
|
+
| `isMergeableObject` | built in | Decide which values can be traversed |
|
|
154
|
+
| `onUnsafeKey` | `"skip"` | Skip or throw on dangerous keys |
|
|
155
|
+
| `maxDepth` | `1000` | Bound recursive traversal |
|
|
156
|
+
| `maxKeys` | `100000` | Bound enumerable object keys per merge |
|
|
157
|
+
|
|
158
|
+
The callback options include `cloneUnlessOtherwiseSpecified`, matching the
|
|
159
|
+
extension-hook shape used by `deepmerge` v4.
|
|
160
|
+
|
|
161
|
+
### Named exports
|
|
162
|
+
|
|
163
|
+
```js
|
|
164
|
+
import merge, {
|
|
165
|
+
DeepMergeLimitError,
|
|
166
|
+
UnsafeKeyError,
|
|
167
|
+
all,
|
|
168
|
+
deepmerge,
|
|
169
|
+
isMergeableObject
|
|
170
|
+
} from '@stackline/deepmerge';
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
CommonJS remains callable:
|
|
174
|
+
|
|
175
|
+
```js
|
|
176
|
+
const merge = require('@stackline/deepmerge');
|
|
177
|
+
|
|
178
|
+
merge({ left: true }, { right: true });
|
|
179
|
+
merge.all([{ one: 1 }, { two: 2 }]);
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Array strategies
|
|
183
|
+
|
|
184
|
+
Overwrite arrays:
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
const overwrite = (_target, source) => source;
|
|
188
|
+
const result = merge([1, 2], [3], { arrayMerge: overwrite });
|
|
189
|
+
// [3]
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Merge arrays by index:
|
|
193
|
+
|
|
194
|
+
```js
|
|
195
|
+
const byIndex = (target, source, options) => {
|
|
196
|
+
const output = target.slice();
|
|
197
|
+
|
|
198
|
+
source.forEach((value, index) => {
|
|
199
|
+
output[index] = index in output
|
|
200
|
+
? merge(output[index], value, options)
|
|
201
|
+
: options.cloneUnlessOtherwiseSpecified(value, options);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
return output;
|
|
205
|
+
};
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Cycles and shared references
|
|
209
|
+
|
|
210
|
+
Circular and repeated references are preserved instead of overflowing the
|
|
211
|
+
stack or being duplicated unexpectedly:
|
|
212
|
+
|
|
213
|
+
```js
|
|
214
|
+
const shared = { enabled: true };
|
|
215
|
+
const source = { first: shared, second: shared };
|
|
216
|
+
source.self = source;
|
|
217
|
+
|
|
218
|
+
const result = merge({}, source);
|
|
219
|
+
|
|
220
|
+
result.first === result.second; // true
|
|
221
|
+
result.self === result; // true
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## TypeScript
|
|
225
|
+
|
|
226
|
+
The package ships declaration files for modern ESM, CommonJS, and older
|
|
227
|
+
TypeScript resolvers. Return types recursively combine the target and source.
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
import merge from '@stackline/deepmerge';
|
|
231
|
+
|
|
232
|
+
const result = merge(
|
|
233
|
+
{ service: { port: 3000 } },
|
|
234
|
+
{ service: { secure: true } }
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
result.service.port; // number
|
|
238
|
+
result.service.secure; // boolean
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
The release matrix tests TypeScript `3.9`, `4.7`, `4.9`, `5.9`, `6.0`, and
|
|
242
|
+
`7.0`. The JavaScript runtime supports Node.js `14.17` and newer.
|
|
243
|
+
|
|
244
|
+
## Browser
|
|
245
|
+
|
|
246
|
+
Use the ESM build with a bundler, or load the small browser global directly:
|
|
247
|
+
|
|
248
|
+
```html
|
|
249
|
+
<script src="https://unpkg.com/@stackline/deepmerge@1.0.0/dist/index.min.js"></script>
|
|
250
|
+
<script>
|
|
251
|
+
const merged = StacklineDeepmerge(
|
|
252
|
+
{ theme: { contrast: 'normal' } },
|
|
253
|
+
{ theme: { motion: 'reduced' } }
|
|
254
|
+
);
|
|
255
|
+
</script>
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Migration from `deepmerge`
|
|
259
|
+
|
|
260
|
+
The lowest-change migration uses an npm alias:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
npm uninstall deepmerge
|
|
264
|
+
npm install deepmerge@npm:@stackline/deepmerge
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The compatibility suite covers documented options and 5,000 deterministic,
|
|
268
|
+
JSON-compatible differential cases against `deepmerge@4.3.1`.
|
|
269
|
+
|
|
270
|
+
Intentional hardening differences:
|
|
271
|
+
|
|
272
|
+
- dangerous keys are always rejected or skipped;
|
|
273
|
+
- cycles are preserved;
|
|
274
|
+
- traversal limits are enabled by default;
|
|
275
|
+
- invalid option values fail early with a controlled error.
|
|
276
|
+
|
|
277
|
+
See [Compatibility](docs/COMPATIBILITY.md) for the full contract.
|
|
278
|
+
|
|
279
|
+
## Performance
|
|
280
|
+
|
|
281
|
+
Security checks, cycle tracking, and resource limits add measurable work. The
|
|
282
|
+
included benchmark compares this package with `deepmerge@4.3.1` on the same
|
|
283
|
+
process:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
npm run benchmark
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Use benchmark results as regression signals, not universal claims. Runtime,
|
|
290
|
+
CPU, input shape, and custom callbacks materially affect throughput.
|
|
291
|
+
|
|
292
|
+
## Trust and maintenance
|
|
293
|
+
|
|
294
|
+
- No runtime dependencies.
|
|
295
|
+
- Every release is built from the public repository.
|
|
296
|
+
- CI validates behavior, types, package exports, clean installs, and supported
|
|
297
|
+
runtimes.
|
|
298
|
+
- Security reports have a dedicated private process in [SECURITY.md](SECURITY.md).
|
|
299
|
+
- Release history is recorded in [CHANGELOG.md](CHANGELOG.md).
|
|
300
|
+
|
|
301
|
+
## Contributing
|
|
302
|
+
|
|
303
|
+
Read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request. Changes
|
|
304
|
+
to compatibility or security behavior require focused regression tests.
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
MIT. See [LICENSE](LICENSE) and [NOTICE](NOTICE).
|
|
309
|
+
|
|
310
|
+
`@stackline/deepmerge` is an independent project and is not affiliated with or
|
|
311
|
+
endorsed by the maintainers of the `deepmerge` package.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
| Version | Security updates |
|
|
6
|
+
| :--- | :---: |
|
|
7
|
+
| `1.x` | Yes |
|
|
8
|
+
| `< 1.0.0` | No public releases |
|
|
9
|
+
|
|
10
|
+
## Reporting a vulnerability
|
|
11
|
+
|
|
12
|
+
Do not open a public issue for a suspected vulnerability.
|
|
13
|
+
|
|
14
|
+
Use the repository's
|
|
15
|
+
[private security advisory form](https://github.com/alexandroit/stackline-deepmerge/security/advisories/new).
|
|
16
|
+
|
|
17
|
+
Include:
|
|
18
|
+
|
|
19
|
+
- the affected version;
|
|
20
|
+
- a minimal reproducer or malicious input;
|
|
21
|
+
- the observed impact;
|
|
22
|
+
- the runtime and module system;
|
|
23
|
+
- any proposed mitigation.
|
|
24
|
+
|
|
25
|
+
We aim to acknowledge complete reports within three business days. Confirmed
|
|
26
|
+
issues are fixed privately, covered by regression tests, and disclosed after a
|
|
27
|
+
patched release is available.
|
|
28
|
+
|
|
29
|
+
## Security contract
|
|
30
|
+
|
|
31
|
+
The default merge path:
|
|
32
|
+
|
|
33
|
+
- rejects `__proto__`, `prototype`, and `constructor` at every object depth;
|
|
34
|
+
- checks both target and source keys;
|
|
35
|
+
- does not read values behind rejected unsafe keys;
|
|
36
|
+
- copies only own enumerable string and symbol keys;
|
|
37
|
+
- writes accepted keys as data properties;
|
|
38
|
+
- bounds depth and enumerable key traversal;
|
|
39
|
+
- tracks circular and shared references.
|
|
40
|
+
|
|
41
|
+
Use `{ onUnsafeKey: 'throw' }` when an unsafe key must reject the entire
|
|
42
|
+
operation instead of being filtered.
|
|
43
|
+
|
|
44
|
+
## Trust boundary
|
|
45
|
+
|
|
46
|
+
Options and callbacks are executable application code. Do not accept
|
|
47
|
+
`arrayMerge`, `customMerge`, or `isMergeableObject` functions from untrusted
|
|
48
|
+
input. Safe property getters may execute when their values are merged, just as
|
|
49
|
+
normal JavaScript property access does. Convert hostile wire formats with a
|
|
50
|
+
trusted parser and use strict input limits at exposed boundaries.
|