{"id":47793496,"url":"https://github.com/adametherzlab/json-diff-ts","last_synced_at":"2026-04-03T16:00:14.473Z","repository":{"id":343154912,"uuid":"1173853820","full_name":"AdametherzLab/json-diff-ts","owner":"AdametherzLab","description":"JSON diff — compare objects, list changes, apply patches","archived":false,"fork":false,"pushed_at":"2026-03-09T05:11:59.000Z","size":13,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-09T09:57:02.316Z","etag":null,"topics":["bun","compare","diff","json","patch","typescript"],"latest_commit_sha":null,"homepage":null,"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/AdametherzLab.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-05T20:25:59.000Z","updated_at":"2026-03-09T05:12:02.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/AdametherzLab/json-diff-ts","commit_stats":null,"previous_names":["adametherzlab/json-diff-ts"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/AdametherzLab/json-diff-ts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdametherzLab%2Fjson-diff-ts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdametherzLab%2Fjson-diff-ts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdametherzLab%2Fjson-diff-ts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdametherzLab%2Fjson-diff-ts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AdametherzLab","download_url":"https://codeload.github.com/AdametherzLab/json-diff-ts/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdametherzLab%2Fjson-diff-ts/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31362653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T15:19:21.178Z","status":"ssl_error","status_checked_at":"2026-04-03T15:19:20.670Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["bun","compare","diff","json","patch","typescript"],"created_at":"2026-04-03T16:00:13.326Z","updated_at":"2026-04-03T16:00:14.456Z","avatar_url":"https://github.com/AdametherzLab.png","language":"TypeScript","readme":"# json-diff-ts\n\n[![CI](https://github.com/AdametherzLab/json-diff-ts/actions/workflows/ci.yml/badge.svg)](https://github.com/AdametherzLab/json-diff-ts/actions) [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](https://www.typescriptlang.org/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\n# 🔍 json-diff-ts\n\nCompare, track, and patch JSON like a boss — TypeScript-first and zero dependencies.\n\n## One-liner\n\nA blazing-fast TypeScript utility that diffs any two JSON values, gives you a clean list of changes (added, removed, modified), and lets you apply those changes back — with full support for nested objects and arrays.\n\n## Features\n\n- ✅ **Deep diffing** — recursively compare nested objects and arrays\n- ✅ **Type-safe** — full TypeScript support with strict mode\n- ✅ **Patch application** — apply changes back to transform JSON\n- ✅ **Array support** — diff arrays by index or identity keys\n- ✅ **Zero deps** — no external packages, just pure TypeScript\n- ✅ **ESM-first** — modern module system with CJS compatibility\n\n## Installation\n\n```bash\nnpm install @adametherzlab/json-diff-ts\n```\n\nOr with Bun:\n\n```bash\nbun add @adametherzlab/json-diff-ts\n```\n\n## Quick Start\n\n```typescript\n// REMOVED external import: import { diff, patch } from \"@adametherzlab/json-diff-ts\";\n\nconst before = { name: \"Alice\", age: 30, city: \"NYC\" };\nconst after = { name: \"Alice\", age: 31, city: \"LA\", country: \"USA\" };\n\nconst result = diff(before, after);\nconsole.log(result.changes);\n// [\n//   { path: [\"age\"], type: \"changed\", oldValue: 30, newValue: 31 },\n//   { path: [\"city\"], type: \"changed\", oldValue: \"NYC\", newValue: \"LA\" },\n//   { path: [\"country\"], type: \"added\", newValue: \"USA\" }\n// ]\n\n// Apply changes to get the new state\nconst patched = patch(before, result.patches);\nconsole.log(patched);\n// { name: \"Alice\", age: 31, city: \"LA\", country: \"USA\" }\n```\n\n## API Reference\n\n### Types\n\n#### `ChangeType` (enum)\n```typescript\nenum ChangeType {\n  Added = \"added\",\n  Removed = \"removed\",\n  Changed = \"changed\"\n}\n```\n\n#### `Change` (type)\n```typescript\ntype Change = AddedChange | RemovedChange | ChangedChange;\n```\n\n#### `AddedChange`\n```typescript\ninterface AddedChange {\n  readonly type: \"added\";\n  readonly path: readonly string[];\n  readonly newValue: JsonValue;\n}\n```\n\n#### `RemovedChange`\n```typescript\ninterface RemovedChange {\n  readonly type: \"removed\";\n  readonly path: readonly string[];\n  readonly oldValue: JsonValue;\n}\n```\n\n#### `ChangedChange`\n```typescript\ninterface ChangedChange {\n  readonly type: \"changed\";\n  readonly path: readonly string[];\n  readonly oldValue: JsonValue;\n  readonly newValue: JsonValue;\n}\n```\n\n#### `JsonValue` (type)\n```typescript\ntype JsonValue = JsonPrimitive | JsonArray | JsonObject;\n```\n\n#### `JsonPrimitive` (type)\nJSON primitive types.\n\n```typescript\ntype JsonPrimitive = string | number | boolean | null;\n```\n\n#### `JsonArray` (interface)\nJSON array type — array of JsonValue.\n\n```typescript\ninterface JsonArray extends ReadonlyArray\u003cJsonValue\u003e {}\n```\n\n#### `JsonObject` (interface)\nJSON object type — record with string keys and JsonValue values.\n\n```typescript\ninterface JsonObject extends Readonly\u003cRecord\u003cstring, JsonValue\u003e\u003e {}\n```\n\n#### `DiffOptions` (interface)\n```typescript\ninterface DiffOptions {\n  /** Compare arrays by identity key instead of index. Default: false */\n  readonly arrayIdentityKey?: string;\n  /** Treat arrays as ordered (index-based). Default: true */\n  readonly orderedArrays?: boolean;\n  /** Max depth for comparison. Default: unlimited */\n  readonly maxDepth?: number;\n}\n```\n\n#### `PatchOptions` (interface)\n```typescript\ninterface PatchOptions {\n  /** Mutate the original object instead of returning a copy. Default: false */\n  readonly mutate?: boolean;\n}\n```\n\n#### `DiffResult` (interface)\n```typescript\ninterface DiffResult {\n  /** List of all changes detected */\n  readonly changes: readonly Change[];\n  /** List of patch operations derived from changes */\n  readonly patches: readonly Patch[];\n  /** Number of additions */\n  readonly added: number;\n  /** Number of removals */\n  readonly removed: number;\n  /** Number of modifications */\n  readonly changed: number;\n  /** Total number of changes */\n  readonly total: number;\n}\n```\n\n#### `Patch` (type)\nA patch operation that can be applied to transform JSON.\n\n```typescript\ntype Patch = AddPatch | RemovePatch | ReplacePatch;\n```\n\n#### `AddPatch`\n```typescript\ninterface AddPatch {\n  readonly op: \"add\";\n  readonly path: readonly string[];\n  readonly value: JsonValue;\n}\n```\n\n#### `RemovePatch`\n```typescript\ninterface RemovePatch {\n  readonly op: \"remove\";\n  readonly path: readonly string[];\n}\n```\n\n#### `ReplacePatch`\n```typescript\ninterface ReplacePatch {\n  readonly op: \"replace\";\n  readonly path: readonly string[];\n  readonly value: JsonValue;\n}\n```\n\n### Functions\n\n#### `diff(oldVal, newVal, options?)`\n```typescript\nfunction diff(oldVal: JsonValue, newVal: JsonValue, options?: DiffOptions): DiffResult\n```\n\n**Parameters:**\n- `oldVal` — The original JSON value\n- `newVal` — The new JSON value to compare against\n- `options` — Optional configuration for diff behavior\n\n**Returns:** DiffResult containing all changes and statistics\n\n**Example:**\n```typescript\nconst result = diff({ a: 1 }, { a: 2 });\nconsole.log(result.changes); // [{ path: [\"a\"], type: \"changed\", oldValue: 1, newValue: 2 }]\n```\n\n#### `diffObjects(oldObj, newObj, options?)`\nDiff two objects, detecting added, removed, and changed properties.\n\n```typescript\nfunction diffObjects(oldObj: JsonObject, newObj: JsonObject, options?: DiffOptions): DiffResult\n```\n\n**Example:**\n```typescript\nconst result = diffObjects({ x: 1 }, { x: 2, y: 3 });\n```\n\n#### `diffArrays(oldArr, newArr, options?)`\n```typescript\nfunction diffArrays(oldArr: JsonArray, newArr: JsonArray, options?: DiffOptions): DiffResult\n```\n\n**Example:**\n```typescript\nconst result = diffArrays([1, 2], [1, 3]);\n```\n\n#### `parsePath(pathString)`\n```typescript\nfunction parsePath(pathString: string): readonly string[]\n```\n\n**Example:**\n```typescript\nparsePath(\"a.b.c\") // [\"a\", \"b\", \"c\"]\nparsePath(\"a[0].b\") // [\"a\", \"0\", \"b\"]\nparsePath('a[\"0\"].b') // [\"a\", \"0\", \"b\"]\n```\n\n#### `buildPath(segments)`\n```typescript\nfunction buildPath(segments: readonly string[]): string\n```\n\n**Example:**\n```typescript\nbuildPath([\"a\", \"b\", \"c\"]) // \"a.b.c\"\nbuildPath([\"a\", \"0\", \"b\"]) // \"a[0].b\"\n```\n\n#### `deepEqual(a, b)`\n```typescript\nfunction deepEqual(a: JsonValue, b: JsonValue): boolean\n```\n\n**Example:**\n```typescript\ndeepEqual({ a: 1 }, { a: 1 }) // true\ndeepEqual([1, 2], [1, 2]) // true\ndeepEqual({ a: 1 }, { a: 2 }) // false\n```\n\n#### `patch(value, patches, options?)`\n```typescript\nfunction patch(value: JsonValue, patches: readonly Patch[], options?: PatchOptions): JsonValue\n```\n\n**Parameters:**\n- `value` — The JsonValue to patch\n- `patches` — Array of patches to apply\n- `options` — Patch options\n\n**Returns:** Patched JsonValue (new copy if mutate is false, same reference if true)\n\n**Example:**\n```typescript\nconst original = { a: 1 };\nconst patched = patch(original, [{ path: [\"a\"], op: \"replace\", value: 2 }]);\n// original is unchanged, patched is { a: 2 }\n```\n\n#### `applyChanges(value, changes, options?)`\n```typescript\nfunction applyChanges(value: JsonValue, changes: readonly Change[], options?: PatchOptions): JsonValue\n```\n\n**Example:**\n```typescript\nconst original = { a: 1 };\nconst changes = diff(original, { a: 2 });\nconst patched = applyChanges(original, changes);\n```\n\n## Advanced Usage\n\n### Array Diffing by Identity\n\n```typescript\n// REMOVED external import: import { diff } from \"@adametherzlab/json-diff-ts\";\n\nconst usersBefore = [\n  { id: 1, name: \"Alice\" },\n  { id: 2, name: \"Bob\" }\n];\n\nconst usersAfter = [\n  { id: 1, name: \"Alice Updated\" },\n  { id: 3, name: \"Charlie\" },\n  { id: 2, name: \"Bob\" }\n];\n\nconst result = diff(usersBefore, usersAfter, { arrayIdentityKey: \"id\" });\nconsole.log(result.changes);\n// Detects: name change for id=1, addition of id=3, keeps id=2 in same position\n```\n\n### Path Manipulation\n\n```typescript\n// REMOVED external import: import { parsePath, buildPath, patch } from \"@adametherzlab/json-diff-ts\";\n\nconst pathStr = \"users[0].profile.settings.notifications\";\nconst segments = parsePath(pathStr);\n// [\"users\", \"0\", \"profile\", \"settings\", \"notifications\"]\n\nconst restored = buildPath(segments);\n// \"users[0].profile.settings.notifications\"\n\n// Use segments directly in patches\nconst data = { users: [{ profile: { settings: { notifications: true } } }] };\nconst patched = patch(data, [\n  { op: \"replace\", path: segments, value: false }\n]);\n```\n\n### Mutate In-Place\n\nFor performance-critical scenarios where memory allocation matters:\n\n```typescript\n// REMOVED external import: import { diff, patch } from \"@adametherzlab/json-diff-ts\";\n\nconst largeObject = { /* big data */ };\nconst updates = { /* changes */ };\n\nconst result = diff(largeObject, updates);\npatch(largeObject, result.patches, { mutate: true });\n// Modifies largeObject directly, no copy allocation\n```\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md)\n\n## License\n\nMIT (c) [AdametherzLab](https://github.com/AdametherzLab)","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadametherzlab%2Fjson-diff-ts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadametherzlab%2Fjson-diff-ts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadametherzlab%2Fjson-diff-ts/lists"}