{"id":21177974,"url":"https://github.com/palindrom/jsonpatcherproxy","last_synced_at":"2025-07-09T22:31:05.025Z","repository":{"id":38237987,"uuid":"82815818","full_name":"Palindrom/JSONPatcherProxy","owner":"Palindrom","description":"ES6 proxy powered JSON Object observer that emits JSON patches when changes occur to your object tree.","archived":false,"fork":false,"pushed_at":"2022-12-30T16:52:36.000Z","size":809,"stargazers_count":94,"open_issues_count":26,"forks_count":14,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-05T08:03:18.890Z","etag":null,"topics":["json-patch","observe","proxy","rfc6902"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Palindrom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-22T14:47:42.000Z","updated_at":"2024-08-02T06:21:29.000Z","dependencies_parsed_at":"2023-01-31T12:01:40.422Z","dependency_job_id":null,"html_url":"https://github.com/Palindrom/JSONPatcherProxy","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/Palindrom/JSONPatcherProxy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Palindrom%2FJSONPatcherProxy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Palindrom%2FJSONPatcherProxy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Palindrom%2FJSONPatcherProxy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Palindrom%2FJSONPatcherProxy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Palindrom","download_url":"https://codeload.github.com/Palindrom/JSONPatcherProxy/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Palindrom%2FJSONPatcherProxy/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264504578,"owners_count":23618825,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["json-patch","observe","proxy","rfc6902"],"created_at":"2024-11-20T17:19:15.374Z","updated_at":"2025-07-09T22:31:04.669Z","avatar_url":"https://github.com/Palindrom.png","language":"JavaScript","readme":"# JSONPatcherProxy\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"JSONPatcherProxy\" src=\"https://user-images.githubusercontent.com/17054134/28315402-86a59eae-6bbe-11e7-82e9-45bacd7f8cc1.png\"\u003e\n\u003c/p\u003e\n\n---\n\n\u003e [ES6 proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) powered JSON Object observer that emits JSON patches when changes occur to your object tree.\n\n[![Build Status](https://travis-ci.org/Palindrom/JSONPatcherProxy.svg?branch=master)](https://travis-ci.org/Palindrom/JSONPatcherProxy)\n\nWith JSONPatcherProxy, you don't need polling or dirty checking. Any changes to the object are synchronously emitted.\n\n## Why you should use JSONPatcherProxy\n\nJSON-Patch [(RFC6902)](http://tools.ietf.org/html/rfc6902) is a standard format that\nallows you to update a JSON document by sending the changes rather than the whole document.\nJSONPatcherProxy plays well with the HTTP PATCH verb (method) and REST style programming.\n\nMark Nottingham has a [nice blog]( http://www.mnot.net/blog/2012/09/05/patch) about it.\n\n## Footprint\n* 1.30 KB minified and gzipped (3.25 KB minified)\n\n## Features\n* Allows you to watch an object and record all patches for later usage.\n* Allows you to watch an object and handle individual patches synchronously through a callback.\n* Tested in Edge, Firefox, Chrome, Safari and Node.js.\n\n## Install\n\nInstall the current version (and save it as a dependency):\n\n### npm\n\n```sh\n$ npm install jsonpatcherproxy --save\n```\n\n### [Download as ZIP](https://github.com/Starcounter-Jack/JSON-Patch/archive/master.zip)\n\n\n## Adding to your project\n\n### In a web browser\n\nLoad the bundled distribution script:\n\n```html\n\u003cscript src=\"dist/jsonpatcherproxy.min.js\"\u003e\u003c/script\u003e\n```\n\nIn [browsers that support ECMAScript modules](https://caniuse.com/#feat=es6-module), the below code uses this library as a module:\n\n```html\n\u003cscript type=\"module\"\u003e\n  import { JSONPatcherProxy } from 'jsonpatcherproxy/src/jsonpatcherproxy.mjs';\n\u003c/script\u003e\n```\n\n**You can use rawgit.com as a CDN**.\n\n### In Node.js\n\n```js\nimport { JSONPatcherProxy } from 'jsonpatcherproxy';\n```\n## Usage\n\n### Generating patches:\n\n```js\nvar myObj = { firstName:\"Joachim\", lastName:\"Wester\", contactDetails: { phoneNumbers: [ { number:\"555-123\" }] } };\nvar jsonPatcherProxy = new JSONPatcherProxy( myObj );\nvar observedObject = jsonPatcherProxy.observe(true);\nobservedObject.firstName = \"Albert\";\nobservedObject.contactDetails.phoneNumbers[0].number = \"123\";\nobservedObject.contactDetails.phoneNumbers.push({number:\"456\"});\nvar patches = jsonPatcherProxy.generate();\n// patches  == [\n//   { op:\"replace\", path=\"/firstName\", value:\"Albert\"},\n//   { op:\"replace\", path=\"/contactDetails/phoneNumbers/0/number\", value:\"123\"},\n//   { op:\"add\", path=\"/contactDetails/phoneNumbers/1\", value:{number:\"456\"}}];\n```\n\n### Receiving patches in a callback:\n\n```js\nvar myObj = { firstName:\"Joachim\", lastName:\"Wester\", contactDetails: { phoneNumbers: [ { number:\"555-123\" }] } };\nvar jsonPatcherProxy = new JSONPatcherProxy( myObj );\nvar observedObject = jsonPatcherProxy.observe(true, function(patch) {\n    // patch == { op:\"replace\", path=\"/firstName\", value:\"Albert\"}\n});\nobservedObject.firstName = \"Albert\";\n```\n\n## API\n\n## Object observing\n\n#### constructor: JSONPatcherProxy( `root` Object, [`showDetachedWarning` Boolean = true] ):  JSONPatcherProxy\n\nCreates an instance of `JSONPatcherProxy` around your object of interest `root`, for later `observe`, `unobserve`, `pause`, `resume` calls.\nReturns `JSONPatcherProxy`.\n\n`root`: The object tree you want to observe\n\n`showDetachedWarning`: Modifying a child object that is detached from the parent tree is not recommended, because the object will continue to be a Proxy. That's why JSONPatcherProxy warns when a detached proxy is accessed. You can set this to false to disable those warnings.\n\n\n#### JSONPatcherProxy#observe(`record` Boolean, [`callback` Function]): Proxy\n\nSets up a deep proxy observer on `root` that listens for changes in the tree. When changes are detected, the optional callback is called with the generated **single** patch as the parameter.\n\n**record**: if set to `false`, all changes are will be pass through the callback and no history will be kept. If set to `true` patches history will be kept until you call `generate`, this will return **several** patches and deletes them from history.\n\nReturns  a `Proxy` mirror of your object.\n\n- Note 1: you must either set `record` to `true` or pass a callback.\n- Note 2: you have to use the return value of this function as your object of interest. Changes to the original object will go unnoticed.\n- Note 3: please make sure to call `JSONPatcherProxy#generate` often if you choose to record. Because the patches will accumulate if you don't.\n\n🚨 Generated patches are not immutable. See \"Limitations\" below.\n\n#### JSONPatcherProxy#generate () :  Array\n\nIt returns the changes of your object since the last time it's called. You have to be recording (by setting `record` to `true`) when calling `JSONPatcherProxy#observe`.\n\nIf there are no pending changes in `root`, returns an empty array (length 0).\n\n🚨 Generated patches are not immutable. See \"Limitations\" below.\n\n#### JSONPatcherProxy#pause () : void\n\nDisables patches emitting (to both callback and patches array). However, the object will be updated if you change it.\n\n#### JSONPatcherProxy#resume () : void\n\nEnables patches emitting (to both callback and patches array). Starting from the moment you call it.\n\n#### JSONPatcherProxy#revoke () : void\n\nDe-proxifies (revokes) all the proxies that were created either in #observe call or by adding sub-objects to the tree in runtime.\n\n#### JSONPatcherProxy#disableTraps () : void\n\nTurns the proxified object into a forward-proxy object; doesn't emit any patches anymore, like a normal object.\n\n## `undefined`s (JS to JSON projection)\n\nAs `undefined` type does not exist in JSON, it's also not a valid value of JSON Patch operation. Therefore `JSONPatcherProxy` will not generate JSON Patches that sets anything to `undefined`.\n\nWhenever a value is set to `undefined` in JS, JSONPatcherProxy method `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:\n\n\u003e 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).\n\nSee the [ECMAScript spec](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-json.stringify) for details.\n\n## Limitations\n\n1. **It mutates your original object**: During proxification, JSONPatcherProxy mutates the object you pass to it. Because it doesn't deep-clone for better performance. If you want your original object to remain untouched, please deep-clone before you pass it to the constructor.\n\n2. **It does not support multi-level proxification**: During proxification, it recursively traverses the object and sets each property to its new proxified version. This means you can't proxify an already proxified object because the original proxy will record proxification as a series of changes. And the emitted patches are unpredictable. Also, when you change a property from either one of the proxies, both of the proxies will emit patches in an undefined manner.\n\n3. **Generated patches are not immutable**. The patches generated by JSONPatcherProxy contain reference to the profixied object as the patch `value`. You should either serialize the patch to JSON immediately or deep clone the value it if you want to process it later. If you don't do that, you run the risk that reading the patch will contain changes added after the patch was generated.\n\n4. **JSONPatcherProxy might not generate patches for changes of integers above `Number.MAX_SAFE_INTEGER`, \nor any other changes beyond the precision of JavaScript double-precision floating-point**. To reduce \npatch generation noise, JSONPatcherProxy compares if the new value is the same (`===`) as the previous one. If so, then no patch is generated. Therefore, a change from `Number.MAX_SAFE_INTEGER + 1` to \n`Number.MAX_SAFE_INTEGER + 2`, or from `0.02` to `0.020000000000000001` will not be effectively observed.\n\n## Specs/tests\n\n#### In browser\n\nGo to `/test`\n\n#### In Node\n\nRun:\n\n```\nnpm test\n```\n\n#### Benchmarking\n\nWhen you run `npm run bench`, few things happen:\n\n1. Five benchmark specs defined in `proxyBenchmark.js` are executed. This might take a minute.\n2. The results are appended to the `benchmark.tsv` file, including the following information for each spec:\n   - current Git commit SHA\n   - number of operations per second\n   - name of the spec\n3. At the end, in your console you will see the detailed information about the current benchmark, and the comparison of all data found in `benchmark.tsv` relatively to the first results for each given spec name, e.g.:\n\n```julia\nObserve and generate, small object (JSONPatcherProxy)\n ec7b9bf: 136720 Ops/sec\n 92da649: 136351 Ops/sec (0.3% worse)\nObserve and generate (JSONPatcherProxy)\n ec7b9bf: 2762 Ops/sec\n 92da649: 2793 Ops/sec (1.1% better)\nPrimitive mutation (JSONPatcherProxy)\n ec7b9bf: 781852 Ops/sec\n 92da649: 781270 Ops/sec (0.1% worse)\nComplex mutation (JSONPatcherProxy)\n ec7b9bf: 11808 Ops/sec\n 92da649: 10692 Ops/sec (9.5% worse)\nSerialization (JSONPatcherProxy)\n ec7b9bf: 1719 Ops/sec\n 92da649: 1719 Ops/sec (no difference)\n```\n\n## Contributing\n\n* Fork it.\n* Clone it locally.\n* Run `npm install`.\n* Run `npm test` to make sure tests pass before touching the code.\n* Modify, test again, push, and send a PR!\n\n## Bumping the version\n\nVersion bumping is automated. Just use [`npm version`](https://docs.npmjs.com/cli/version) command and all files will be updated with the new version.\n\n## License\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpalindrom%2Fjsonpatcherproxy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpalindrom%2Fjsonpatcherproxy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpalindrom%2Fjsonpatcherproxy/lists"}