{"id":13657953,"url":"https://github.com/zendive/jsdiff","last_synced_at":"2025-04-24T08:30:56.863Z","repository":{"id":81380559,"uuid":"116212778","full_name":"zendive/jsdiff","owner":"zendive","description":"Chrome/Firefox extension to compare objects in memory with console.diff(old, new) devtools function","archived":false,"fork":false,"pushed_at":"2025-04-09T03:39:35.000Z","size":2202,"stargazers_count":21,"open_issues_count":0,"forks_count":4,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-04-09T04:26:46.740Z","etag":null,"topics":["chrome-devtools-extension","chrome-extension","compare-objects","console-diff","console-tool","debugging-tools","devtools","diff","firefox-addon","javascript-tools","jsondiffpatch","vue3"],"latest_commit_sha":null,"homepage":"https://chromewebstore.google.com/detail/consolediff/iefeamoljhdcpigpnpggeiiabpnpgonb","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zendive.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-01-04T04:01:43.000Z","updated_at":"2025-04-09T03:35:25.000Z","dependencies_parsed_at":"2023-11-15T01:27:23.894Z","dependency_job_id":"727b082e-9736-4dc9-a60c-ffd663122a40","html_url":"https://github.com/zendive/jsdiff","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zendive%2Fjsdiff","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zendive%2Fjsdiff/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zendive%2Fjsdiff/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zendive%2Fjsdiff/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zendive","download_url":"https://codeload.github.com/zendive/jsdiff/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250591926,"owners_count":21455463,"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":["chrome-devtools-extension","chrome-extension","compare-objects","console-diff","console-tool","debugging-tools","devtools","diff","firefox-addon","javascript-tools","jsondiffpatch","vue3"],"created_at":"2024-08-02T05:00:54.309Z","updated_at":"2025-04-24T08:30:56.852Z","avatar_url":"https://github.com/zendive.png","language":"TypeScript","readme":"### ![](public/img/panel-icon28.png) JSDiff\n\nAn extension for developers that enhances the console API by incorporating the ability to compare objects and adds a `JSDiff` tab (parallel to Elements, Network panels) within your dev-tools for viewing the results.\n\n- Available in Chrome Web Store as [console.diff()](https://chromewebstore.google.com/detail/consolediff/iefeamoljhdcpigpnpggeiiabpnpgonb)\n- Available in Firefox Add-ons as [jsdiff.diff()](https://addons.mozilla.org/firefox/addon/jsdiff-diff/)\n\n\u003cdetails\u003e\n  \u003csummary\u003e \u003cstrong\u003eScreenshots\u003c/strong\u003e \u003c/summary\u003e\n\n- Comparing two objects\n  ![screenshot](./doc/screenshot-01.png)\n\n- Tracking changes in `localStorage` (unchanged are hidden)\n  ![screenshot](./doc/screenshot-02.png)\n\n\u003c/details\u003e\n\n### Motivation\n\n- Track object mutations during runtime and/or while debugging with intention to find expected or unexpected changes.\n\n### Features\n\n- User interface:\n\n  - Hide / show unchanged properties.\n  - Copy changed properties in format of `jsondiffpatch` Delta object.\n  - Clear current result.\n  - Search input to highlight patterns.\n    - If search query contains at least one upper-case letter - the search will be case-sensitive.\n  - Indicator of the last update time.\n  - Indicator of a fatal error (out of storage memory).\n  - DevTools light/dark color scheme support.\n\n- Compare objects between multiple [sub]domains, Chrome tabs, or single page reloads.\n\n  - `JSDiff` DevTools panel reflects current state of comparison, regardless the tab[s] it was opened from.\n\n- Fail-safe serialization of objects having security issues while accessing their properties or objects having `toJSON()` function; when instead of serialization of all object properties, - only `toJSON()` return value is serialized, like `JSON.strigify()` does.\n\n- Can be used from within online code editors like: [codesandbox.io](https://codesandbox.io), [coderpad.io](https://coderpad.io), [flems.io](https://flems.io), [codepen.io](https://codepen.io), [jsfiddle.net](https://jsfiddle.net), [mdn playground](https://developer.mozilla.org/play).\n\n#### Limitations\n\n- Map keys like `0` and `'0'` would be merged due to `Map to Object` conversion.\n\n- While paused in debug mode, `JSDiff` panel won't reflect the result until runtime is resumed (see [#10](https://github.com/zendive/jsdiff/issues/10)).\n\n- Compared objects, after being serialized, stored in `chrome.storage.local` which has 10 MB limit.\n\n- In Firefox the API is under `jsdiff` object for now, cause extension API's not fully compatible.\n\n### API\n\n- **console.diff(left, right)** - compare left and right arguments\n\n```javascript\nconsole.diff({ a: 1, b: 1, c: 3 }, { a: 1, b: 2, d: 3 });\n```\n\n- **console.diffPush(next)** - shifts sides, right becomes left, next becomes right\n\n```javascript\nconsole.diffPush(Date.now());\n```\n\n- **console.diff(next)** - shorthand for `diffPush`\n\n```javascript\nconsole.diff(Date.now());\n```\n\n- **console.diffLeft(left)** - update the old value only\n\n```javascript\nconsole.diffLeft(Date.now());\n```\n\n- **console.diffRight(right)** - update the new value only\n\n```javascript\nconsole.diffRight(Date.now());\n```\n\n#### Typescript\n\nGlobal Console interface declaration for quick copy/paste when used from typescript:\n\n```typescript\ndeclare global {\n  interface Console {\n    diff(left: unknown, right?: unknown): void;\n    diffPush(next: unknown): void;\n    diffLeft(left: unknown): void;\n    diffRight(right: unknown): void;\n  }\n}\n```\n\n### Serialization by types\n\n| Input                                                                           | Output                                                                                                   |\n|---------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|\n| XMLHttpRequest\u003csup\u003e[1]\u003c/sup\u003e                                                    | ƒ XMLHttpRequest⟪native⟫                                                                                 |\n| function test(){}\u003csup\u003e[1]\u003c/sup\u003e                                                 | ƒ test⟪1374b28d22b674e53a044425556a9cd48b82fd5aba3bf19e3545d51704227b10⟫                                 |\n| document.body                                                                   | {0001}\u003csup\u003e[2,3]\u003c/sup\u003e DOM⟪BODY⟫                                                                         |\n| ±Infinity                                                                       | Number⟪±Infinity⟫                                                                                        |\n| NaN                                                                             | Number⟪NaN⟫                                                                                              |\n| 98765432109876543210n                                                           | BigInt⟪98765432109876543210⟫                                                                             |\n| void 0                                                                          | ⟪undefined⟫                                                                                              |\n| /example/i                                                                      | RegExp⟪/example/i⟫                                                                                       |\n| new URL('https:\\//example.com/')                                                | URL⟪https:\\//example.com\\/⟫                                                                              |\n| Symbol('example')                                                               | {0001}\u003csup\u003e[3]\u003c/sup\u003e Symbol(example)                                                                     |\n| Symbol.for('global')                                                            | Symbol(global)                                                                                           |\n| (obj = {key: 1}, {first: obj, second: obj})                                     | {\"first\": {\"key\": 1}, \"second\": \"[0002]\u003csup\u003e[4]\u003c/sup\u003e Object⟪♻️⟫\"}                                       |\n| (key2= {}, map = new Map(\\[['key1', 1], [key2, 2]]), {first: map, second: map}) | {\"first\": {\"[0003]\u003csup\u003e[4,5]\u003c/sup\u003e Object⟪♻️⟫\": 2, \"key1\": 1}, \"second\": \"[0002]\u003csup\u003e[4]\u003c/sup\u003e Map⟪♻️⟫\"} |\n| (arr = [1], {first: arr, second: arr})                                          | {\"first\": [1], \"second\": \"[0002]\u003csup\u003e[4]\u003c/sup\u003e Array⟪♻️⟫\"}                                               |\n| (set = new Set([1]), {first: set, second: set})                                 | {\"first: [1], \"second\": \"[0002]\u003csup\u003e[4]\u003c/sup\u003e Set⟪♻️⟫\"}                                                  |\n\n\u003csup\u003e1\u003c/sup\u003e Functions included in comparison result in order to detect possible alterations, in form of a string combined from a function name (if present) and a hash of a `function.toString()` body.\n\n\u003csup\u003e2\u003c/sup\u003e DOM element serialized by pseudo `id` and `nodeName`.\n\n\u003csup\u003e3\u003c/sup\u003e Notation `{}` denotes pseudo `id` from a Set of unique instances, which is assigned during serialization of compared sides and remains inside internal `WeakMap` lookup catalog until its garbage collected or page is reloaded.\n\n\u003csup\u003e4\u003c/sup\u003e Notation `[]` denotes pseudo `id` from a [Multiset](https://en.wikipedia.org/wiki/Multiset) of recurring instances, which is assigned in the scope of serialization of a high level argument instance, while comparing left or right side; that means - if some object, having `id` of `[0001]` on the left side, is not guarantied to have same `id` on the right side.\n\n\u003csup\u003e5\u003c/sup\u003e `Map` key, unless it's a primitive type, serialized by his pseudo `id`.\n\n### Protection\n\n- How to protect your site from this extension:\n\n  - Tests in Chrome show that even `Content-Security-Policy: default-src 'none';` header won't prevent injection of extension content-scripts...\n\n  - Avoid assigning to `window` or `globalThis` any application object.\n    See also [accidental global variables and memory leaks](https://www.tutorialspoint.com/explain-in-detail-about-memory-leaks-in-javascript).\n\n  - In general, you can incapacitate console functions:\n\n  ```js\n  for (const prop in console) {\n    if (typeof console[prop] === 'function' \u0026\u0026 prop !== 'error') {\n      console[prop] = function noop() {};\n    }\n  }\n  ```\n\n### Build instructions\n\n- Linux\n- [Deno](https://docs.deno.com/runtime/getting_started/installation/) 2.2.8\n\n```sh\nmake install      # install dependencies\nmake all          # build for prod and make extension.${browser}.zip\nmake tune2chrome  # or tune2firefox for relevant manifest.json file\nmake dev          # local development\n```\n\n#### Based on\n\n- [jsondiffpatch](https://github.com/benjamine/jsondiffpatch) by Benjamín Eidelman\n- [vuejs](https://github.com/vuejs) by Evan You\n\n\u003cdetails\u003e\n  \u003csummary\u003e \u003cstrong\u003eCommunication schemes between Content-script and DevTools panel\u003c/strong\u003e \u003c/summary\u003e\n\n- Chrome mv3\n  ![screenshot](./doc/design.chrome.png)\n- Firefox\n  ![screenshot](./doc/design.firefox.png)\n\n\u003c/details\u003e\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzendive%2Fjsdiff","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzendive%2Fjsdiff","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzendive%2Fjsdiff/lists"}