@capture.dev/fast-json-patch 3.2.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/LICENSE.txt ADDED
@@ -0,0 +1,23 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2013, 2014, 2020 Joachim Wester (original maintainer)
4
+ Copyright (c) 2025 capture.dev (for modifications)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ 'Software'), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,428 @@
1
+ JSON-Patch
2
+ ===============
3
+
4
+ > A leaner and meaner implementation of JSON-Patch. Small footprint. High performance.
5
+
6
+ [![Build Status](https://travis-ci.org/Starcounter-Jack/JSON-Patch.svg?branch=master)](https://travis-ci.org/Starcounter-Jack/JSON-Patch)
7
+
8
+ With JSON-Patch, you can:
9
+ - **apply** patches (arrays) and single operations on JS object
10
+ - **validate** a sequence of patches
11
+ - **observe** for changes and **generate** patches when a change is detected
12
+ - **compare** two objects to obtain the difference
13
+
14
+ Tested in Firefox, Chrome, Edge, Safari, IE11, Deno and Node.js
15
+
16
+
17
+ ## Why you should use JSON-Patch
18
+
19
+ JSON-Patch [(RFC6902)](http://tools.ietf.org/html/rfc6902) is a standard format that
20
+ allows you to update a JSON document by sending the changes rather than the whole document.
21
+ JSON Patch plays well with the HTTP PATCH verb (method) and REST style programming.
22
+
23
+ Mark Nottingham has a [nice blog]( http://www.mnot.net/blog/2012/09/05/patch) about it.
24
+
25
+
26
+ ## Install
27
+
28
+ [Download as ZIP](https://github.com/Starcounter-Jack/JSON-Patch/archive/master.zip) or install the current version using a package manager (and save it as a dependency):
29
+
30
+ ```sh
31
+ # NPM
32
+ npm install fast-json-patch --save
33
+ ```
34
+
35
+
36
+ ## Adding to your project
37
+
38
+ ### In a web browser
39
+
40
+ Load the bundled distribution script:
41
+
42
+ ```html
43
+ <script src="dist/fast-json-patch.min.js"></script>
44
+ ```
45
+
46
+ In [browsers that support ECMAScript modules](https://caniuse.com/#feat=es6-module), the below code uses this library as a module:
47
+
48
+ ```html
49
+ <script type="module">
50
+ import * as jsonpatch from 'fast-json-patch/index.mjs';
51
+ import { applyOperation } from 'fast-json-patch/index.mjs';
52
+ </script>
53
+ ```
54
+
55
+ ### In Node.js
56
+
57
+ In Node 12+ with `--experimental-modules` flag, the below code uses this library as an ECMAScript module:
58
+
59
+ ```js
60
+ import * as jsonpatch from 'fast-json-patch/index.mjs';
61
+ import { applyOperation } from 'fast-json-patch/index.mjs';
62
+ ```
63
+
64
+ In Webpack (and most surely other bundlers based on Babel), the below code uses this library as an ECMAScript module:
65
+
66
+ ```js
67
+ import * as jsonpatch from 'fast-json-patch';
68
+ import { applyOperation } from 'fast-json-patch';
69
+ ```
70
+
71
+ In standard Node, the below code uses this library as a CommonJS module:
72
+
73
+ ```js
74
+ const { applyOperation } = require('fast-json-patch');
75
+ const applyOperation = require('fast-json-patch').applyOperation;
76
+ ```
77
+
78
+ ## Directories
79
+
80
+ Directories used in this package:
81
+
82
+ - `dist/` - contains ES5 files for a Web browser
83
+ - `commonjs/` - contains CommonJS module and typings
84
+ - `module/` - contains ECMAScript module and typings
85
+ - `src/` - contains TypeScript source files
86
+
87
+ ## API
88
+
89
+ #### `function applyPatch<T>(document: T, patch: Operation[], validateOperation?: boolean | Validator<T>, mutateDocument: boolean = true, banPrototypeModifications: boolean = true): PatchResult<T>`
90
+
91
+ Applies `patch` array on `obj`.
92
+
93
+ - `document` The document to patch
94
+ - `patch` a JSON-Patch array of operations to apply
95
+ - `validateOperation` Boolean for whether to validate each operation with our default validator, or to pass a validator callback
96
+ - `mutateDocument` Whether to mutate the original document or clone it before applying
97
+ - `banPrototypeModifications` Whether to ban modifications to `__proto__`, defaults to `true`.
98
+
99
+ An invalid patch results in throwing an error (see `jsonpatch.validate` for more information about the error object).
100
+
101
+ It modifies the `document` object and `patch` - it gets the values by reference.
102
+ If you would like to avoid touching your `patch` array values, clone them: `jsonpatch.applyPatch(document, jsonpatch.deepClone(patch))`.
103
+
104
+ Returns an array of [`OperationResult`](#operationresult-type) objects - one item for each item in `patches`, each item is an object `{newDocument: any, test?: boolean, removed?: any}`.
105
+
106
+ * `test` - boolean result of the test
107
+ * `remove`, `replace` and `move` - original object that has been removed
108
+ * `add` (only when adding to an array) - index at which item has been inserted (useful when using `-` alias)
109
+
110
+ - ** Note: It throws `TEST_OPERATION_FAILED` error if `test` operation fails. **
111
+ - ** Note II: the returned array has `newDocument` property that you can use as the final state of the patched document **.
112
+ - ** Note III: By default, when `banPrototypeModifications` is `true`, this method throws a `TypeError` when you attempt to modify an object's prototype.
113
+
114
+ - See [Validation notes](#validation-notes).
115
+
116
+ Example:
117
+
118
+ ```js
119
+ var document = { firstName: "Albert", contactDetails: { phoneNumbers: [] } };
120
+ var patch = [
121
+ { op: "replace", path: "/firstName", value: "Joachim" },
122
+ { op: "add", path: "/lastName", value: "Wester" },
123
+ { op: "add", path: "/contactDetails/phoneNumbers/0", value: { number: "555-123" } }
124
+ ];
125
+ document = jsonpatch.applyPatch(document, patch).newDocument;
126
+ // document == { firstName: "Joachim", lastName: "Wester", contactDetails: { phoneNumbers: [{number:"555-123"}] } };
127
+ ```
128
+
129
+ #### `function applyOperation<T>(document: T, operation: Operation, validateOperation: boolean | Validator<T> = false, mutateDocument: boolean = true, banPrototypeModifications: boolean = true, index: number = 0): OperationResult<T>`
130
+
131
+ Applies single operation object `operation` on `document`.
132
+
133
+ - `document` The document to patch
134
+ - `operation` The operation to apply
135
+ - `validateOperation` Whether to validate the operation, or to pass a validator callback
136
+ - `mutateDocument` Whether to mutate the original document or clone it before applying
137
+ - `banPrototypeModifications` Whether to ban modifications to `__proto__`, defaults to `true`.
138
+ - `index` The index of the operation in your patch array. Useful for better error reporting when that operation fails to apply.
139
+
140
+ It modifies the `document` object and `operation` - it gets the values by reference.
141
+ If you would like to avoid touching your values, clone them: `jsonpatch.applyOperation(document, jsonpatch.deepClone(operation))`.
142
+
143
+ Returns an [`OperationResult`](#operationresult-type) object `{newDocument: any, test?: boolean, removed?: any}`.
144
+
145
+ - ** Note: It throws `TEST_OPERATION_FAILED` error if `test` operation fails. **
146
+ - ** Note II: By default, when `banPrototypeModifications` is `true`, this method throws a `TypeError` when you attempt to modify an object's prototype.
147
+
148
+ - See [Validation notes](#validation-notes).
149
+
150
+ Example:
151
+
152
+ ```js
153
+ var document = { firstName: "Albert", contactDetails: { phoneNumbers: [] } };
154
+ var operation = { op: "replace", path: "/firstName", value: "Joachim" };
155
+ document = jsonpatch.applyOperation(document, operation).newDocument;
156
+ // document == { firstName: "Joachim", contactDetails: { phoneNumbers: [] }}
157
+ ```
158
+
159
+ #### `jsonpatch.applyReducer<T>(document: T, operation: Operation, index: number): T`
160
+
161
+ **Ideal for `patch.reduce(jsonpatch.applyReducer, document)`**.
162
+
163
+ Applies single operation object `operation` on `document`.
164
+
165
+ Returns the a modified document.
166
+
167
+ Note: It throws `TEST_OPERATION_FAILED` error if `test` operation fails.
168
+
169
+ Example:
170
+
171
+ ```js
172
+ var document = { firstName: "Albert", contactDetails: { phoneNumbers: [ ] } };
173
+ var patch = [
174
+ { op:"replace", path: "/firstName", value: "Joachim" },
175
+ { op:"add", path: "/lastName", value: "Wester" },
176
+ { op:"add", path: "/contactDetails/phoneNumbers/0", value: { number: "555-123" } }
177
+ ];
178
+ var updatedDocument = patch.reduce(applyReducer, document);
179
+ // updatedDocument == { firstName:"Joachim", lastName:"Wester", contactDetails:{ phoneNumbers[ {number:"555-123"} ] } };
180
+ ```
181
+
182
+ #### `jsonpatch.deepClone(value: any): any`
183
+
184
+ Returns deeply cloned value.
185
+
186
+ #### `jsonpatch.escapePathComponent(path: string): string`
187
+
188
+ Returns the escaped path.
189
+
190
+ #### `jsonpatch.unescapePathComponent(path: string): string`
191
+
192
+ Returns the unescaped path.
193
+
194
+ #### `jsonpatch.getValueByPointer(document: object, pointer: string)`
195
+
196
+ Retrieves a value from a JSON document by a JSON pointer.
197
+
198
+ Returns the value.
199
+
200
+ #### `jsonpatch.observe(document: any, callback?: Function): Observer`
201
+
202
+ Sets up an deep observer on `document` that listens for changes in object tree. When changes are detected, the optional
203
+ callback is called with the generated patches array as the parameter.
204
+
205
+ Returns `observer`.
206
+
207
+ #### `jsonpatch.generate(document: any, observer: Observer, invertible = false): Operation[]`
208
+
209
+ If there are pending changes in `obj`, returns them synchronously. If a `callback` was defined in `observe`
210
+ method, it will be triggered synchronously as well. If `invertible` is true, then each change will be preceded by a test operation of the value before the change.
211
+
212
+ If there are no pending changes in `obj`, returns an empty array (length 0).
213
+
214
+ Example:
215
+
216
+ ```js
217
+ var document = { firstName: "Joachim", lastName: "Wester", contactDetails: { phoneNumbers: [ { number:"555-123" }] } };
218
+ var observer = jsonpatch.observe(document);
219
+ document.firstName = "Albert";
220
+ document.contactDetails.phoneNumbers[0].number = "123";
221
+ document.contactDetails.phoneNumbers.push({ number:"456" });
222
+ var patch = jsonpatch.generate(observer);
223
+ // patch == [
224
+ // { op: "replace", path: "/firstName", value: "Albert"},
225
+ // { op: "replace", path: "/contactDetails/phoneNumbers/0/number", value: "123" },
226
+ // { op: "add", path: "/contactDetails/phoneNumbers/1", value: {number:"456"}}
227
+ // ];
228
+ ```
229
+
230
+ Example of generating patches with test operations for values in the first object:
231
+
232
+ ```js
233
+ var document = { firstName: "Joachim", lastName: "Wester", contactDetails: { phoneNumbers: [ { number:"555-123" }] } };
234
+ var observer = jsonpatch.observe(document);
235
+ document.firstName = "Albert";
236
+ document.contactDetails.phoneNumbers[0].number = "123";
237
+ document.contactDetails.phoneNumbers.push({ number:"456" });
238
+ var patch = jsonpatch.generate(observer, true);
239
+ // patch == [
240
+ // { op: "test", path: "/firstName", value: "Joachim"},
241
+ // { op: "replace", path: "/firstName", value: "Albert"},
242
+ // { op: "test", path: "/contactDetails/phoneNumbers/0/number", value: "555-123" },
243
+ // { op: "replace", path: "/contactDetails/phoneNumbers/0/number", value: "123" },
244
+ // { op: "add", path: "/contactDetails/phoneNumbers/1", value: {number:"456"}}
245
+ // ];
246
+ ```
247
+
248
+ #### `jsonpatch.unobserve(document, observer)`
249
+ ```typescript
250
+ jsonpatch.unobserve(document: any, observer: Observer): void
251
+
252
+ type JsonableObj = { [key:string]: Jsonable };
253
+ type JsonableArr = Jsonable[];
254
+ type Jsonable = JsonableArr | JsonableObj | string | number | boolean | null;
255
+ ```
256
+
257
+ Destroys the observer set up on `document`.
258
+
259
+ Any remaining changes are delivered synchronously (as in `jsonpatch.generate`). Note: this is different that ES6/7 `Object.unobserve`, which delivers remaining changes asynchronously.
260
+
261
+ #### `jsonpatch.compare(document1, document2, invertible)`
262
+
263
+ ```typescript
264
+ jsonpatch.compare(document1: Jsonable, document2: Jsonable, invertible = false): Operation[]
265
+
266
+ type JsonableObj = { [key:string]: Jsonable };
267
+ type JsonableArr = Jsonable[];
268
+ type Jsonable = JsonableArr | JsonableObj | string | number | boolean | null;
269
+ ```
270
+
271
+ Compares object trees `document1` and `document2` and returns the difference relative to `document1` as a patches array. If `invertible` is true, then each change will be preceded by a test operation of the value in `document1`.
272
+
273
+ If there are no differences, returns an empty array (length 0).
274
+
275
+ Example:
276
+
277
+ ```js
278
+ var documentA = {user: {firstName: "Albert", lastName: "Einstein"}};
279
+ var documentB = {user: {firstName: "Albert", lastName: "Collins"}};
280
+ var diff = jsonpatch.compare(documentA, documentB);
281
+ //diff == [{op: "replace", path: "/user/lastName", value: "Collins"}]
282
+ ```
283
+
284
+ Example of comparing two object trees with test operations for values in the first object:
285
+
286
+ ```js
287
+ var documentA = {user: {firstName: "Albert", lastName: "Einstein"}};
288
+ var documentB = {user: {firstName: "Albert", lastName: "Collins"}};
289
+ var diff = jsonpatch.compare(documentA, documentB, true);
290
+ //diff == [
291
+ // {op: "test", path: "/user/lastName", value: "Einstein"},
292
+ // {op: "replace", path: "/user/lastName", value: "Collins"}
293
+ // ];
294
+ ```
295
+
296
+ #### `jsonpatch.validate(patch: Operation[], document?: any, validator?: Function): JsonPatchError`
297
+
298
+ See [Validation notes](#validation-notes)
299
+
300
+ Validates a sequence of operations. If `document` parameter is provided, the sequence is additionally validated against the object tree.
301
+
302
+ If there are no errors, returns undefined. If there is an errors, returns a JsonPatchError object with the following properties:
303
+
304
+ - `name` String - short error code
305
+ - `message` String - long human readable error message
306
+ - `index` Number - index of the operation in the sequence
307
+ - `operation` Object - reference to the operation
308
+ - `tree` Object - reference to the tree
309
+
310
+ Possible errors:
311
+
312
+ Error name | Error message
313
+ ------------------------------|------------
314
+ SEQUENCE_NOT_AN_ARRAY | Patch sequence must be an array
315
+ OPERATION_NOT_AN_OBJECT | Operation is not an object
316
+ OPERATION_OP_INVALID | Operation `op` property is not one of operations defined in RFC-6902
317
+ OPERATION_PATH_INVALID | Operation `path` property is not a valid string
318
+ OPERATION_FROM_REQUIRED | Operation `from` property is not present (applicable in `move` and `copy` operations)
319
+ OPERATION_VALUE_REQUIRED | Operation `value` property is not present, or `undefined` (applicable in `add`, `replace` and `test` operations)
320
+ OPERATION_VALUE_CANNOT_CONTAIN_UNDEFINED | Operation `value` property object has at least one `undefined` value (applicable in `add`, `replace` and `test` operations)
321
+ OPERATION_PATH_CANNOT_ADD | Cannot perform an `add` operation at the desired path
322
+ OPERATION_PATH_UNRESOLVABLE | Cannot perform the operation at a path that does not exist
323
+ OPERATION_FROM_UNRESOLVABLE | Cannot perform the operation from a path that does not exist
324
+ OPERATION_PATH_ILLEGAL_ARRAY_INDEX | Expected an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index
325
+ OPERATION_VALUE_OUT_OF_BOUNDS | The specified index MUST NOT be greater than the number of elements in the array
326
+ TEST_OPERATION_FAILED | When operation is `test` and the test fails, applies to `applyReducer`.
327
+
328
+ Example:
329
+
330
+ ```js
331
+ var obj = {user: {firstName: "Albert"}};
332
+ var patches = [{op: "replace", path: "/user/firstName", value: "Albert"}, {op: "replace", path: "/user/lastName", value: "Einstein"}];
333
+ var errors = jsonpatch.validate(patches, obj);
334
+ if (errors.length == 0) {
335
+ //there are no errors!
336
+ }
337
+ else {
338
+ for (var i=0; i < errors.length; i++) {
339
+ if (!errors[i]) {
340
+ console.log("Valid patch at index", i, patches[i]);
341
+ }
342
+ else {
343
+ console.error("Invalid patch at index", i, errors[i], patches[i]);
344
+ }
345
+ }
346
+ }
347
+ ```
348
+
349
+ ## `OperationResult` Type
350
+
351
+ Functions `applyPatch` and `applyOperation` both return `OperationResult` object. This object is:
352
+
353
+ ```ts
354
+ {newDocument: any, test?: boolean, removed?: any}
355
+ ```
356
+
357
+ Where:
358
+
359
+ - `newDocument`: the new state of the document after the patch/operation is applied.
360
+ - `test`: if the operation was a `test` operation. This will be its result.
361
+ - `removed`: contains the removed, moved, or replaced values from the document after a `remove`, `move` or `replace` operation.
362
+
363
+
364
+ ## Validation Notes
365
+
366
+ Functions `applyPatch`, `applyOperation`, and `validate` accept a `validate`/ `validator` parameter:
367
+
368
+ - If the `validateOperation` parameter is set to `false`, validation will not occur.
369
+ - If set to `true`, the patch is extensively validated before applying using jsonpatch's default validation.
370
+ - If set to a `function` callback, the patch is validated using that function.
371
+
372
+ If you pass a validator, it will be called with four parameters for each operation, `function(operation, index, tree, existingPath)` and it is expected to throw `JsonPatchError` when your conditions are not met.
373
+
374
+ - `operation` The operation it self.
375
+ - `index` `operation`'s index in the patch array (if application).
376
+ - `tree` The object that is supposed to be patched.
377
+ - `existingPath` the path `operation` points to.
378
+
379
+ ## Overwriting and `move` Operation
380
+
381
+ When the target of the move operation already exists, it is cached, deep cloned and returned as `removed` in `OperationResult`.
382
+
383
+ ## `undefined`s (JS to JSON projection)
384
+
385
+ As `undefined` type does not exist in JSON, it's also not a valid value of JSON Patch operation. Therefore `jsonpatch` will not generate JSON Patches that sets anything to `undefined`.
386
+
387
+ Whenever a value is set to `undefined` in JS, JSON-Patch methods `generate` and `compare` will treat it similarly to how JavaScript method [`JSON.stringify` (MDN)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) treats them:
388
+
389
+ > If `undefined` (...) is encountered during conversion it is either omitted (when it is found in an object) or censored to `null` (when it is found in an array).
390
+
391
+ See the [ECMAScript spec](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-json.stringify) for details.
392
+
393
+ ## Specs/tests
394
+
395
+ - [Run in browser](http://starcounter-jack.github.io/JSON-Patch/test/)
396
+
397
+ ## [Contributing](CONTRIBUTING.md)
398
+
399
+ ## Changelog
400
+
401
+ To see the list of recent changes, see [Releases](https://github.com/Starcounter-Jack/JSON-Patch/releases).
402
+
403
+ ## Footprint
404
+ 4 KB minified and gzipped (12 KB minified)
405
+
406
+ ## Performance
407
+
408
+ ##### [`add` benchmark](https://run.perf.zone/view/JSON-Patch-Add-Operation-1535541298893)
409
+
410
+ ![image](https://user-images.githubusercontent.com/17054134/44784357-aa422480-ab8d-11e8-8a7e-037e692dd842.png)
411
+
412
+ ##### [`replace` benchmark](https://run.perf.zone/view/JSON-Patch-Replace-Operation-1535540952263)
413
+
414
+ ![image](https://user-images.githubusercontent.com/17054134/44784275-5fc0a800-ab8d-11e8-8a90-e87b8d5409d0.png)
415
+
416
+ Tested on 29.08.2018. Compared libraries:
417
+
418
+ - [Starcounter-Jack/JSON-Patch](https://www.npmjs.com/package/fast-json-patch) 2.0.6
419
+ - [bruth/jsonpatch-js](https://www.npmjs.com/package/json-patch) 0.7.0
420
+ - [dharmafly/jsonpatch.js](https://www.npmjs.com/package/jsonpatch) 3.0.1
421
+ - [jiff](https://www.npmjs.com/package/jiff) 0.7.3
422
+ - [RFC6902](https://www.npmjs.com/package/rfc6902) 2.4.0
423
+
424
+ We aim the tests to be fair. Our library puts performance as the #1 priority, while other libraries can have different priorities. If you'd like to update the benchmarks or add a library, please fork the [perf.zone](https://perf.zone) benchmarks linked above and open an issue to include new results.
425
+
426
+ ## License
427
+
428
+ MIT