@scalar/json-magic 0.1.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.
Files changed (185) hide show
  1. package/.turbo/turbo-build.log +9 -0
  2. package/CHANGELOG.md +7 -0
  3. package/LICENSE +21 -0
  4. package/README.md +356 -0
  5. package/dist/bundle/bundle.d.ts +292 -0
  6. package/dist/bundle/bundle.d.ts.map +1 -0
  7. package/dist/bundle/bundle.js +259 -0
  8. package/dist/bundle/bundle.js.map +7 -0
  9. package/dist/bundle/create-limiter.d.ts +21 -0
  10. package/dist/bundle/create-limiter.d.ts.map +1 -0
  11. package/dist/bundle/create-limiter.js +31 -0
  12. package/dist/bundle/create-limiter.js.map +7 -0
  13. package/dist/bundle/index.d.ts +2 -0
  14. package/dist/bundle/index.d.ts.map +1 -0
  15. package/dist/bundle/index.js +5 -0
  16. package/dist/bundle/index.js.map +7 -0
  17. package/dist/bundle/plugins/browser.d.ts +4 -0
  18. package/dist/bundle/plugins/browser.d.ts.map +1 -0
  19. package/dist/bundle/plugins/browser.js +9 -0
  20. package/dist/bundle/plugins/browser.js.map +7 -0
  21. package/dist/bundle/plugins/fetch-urls/index.d.ts +39 -0
  22. package/dist/bundle/plugins/fetch-urls/index.d.ts.map +1 -0
  23. package/dist/bundle/plugins/fetch-urls/index.js +48 -0
  24. package/dist/bundle/plugins/fetch-urls/index.js.map +7 -0
  25. package/dist/bundle/plugins/node.d.ts +5 -0
  26. package/dist/bundle/plugins/node.d.ts.map +1 -0
  27. package/dist/bundle/plugins/node.js +11 -0
  28. package/dist/bundle/plugins/node.js.map +7 -0
  29. package/dist/bundle/plugins/parse-json/index.d.ts +13 -0
  30. package/dist/bundle/plugins/parse-json/index.d.ts.map +1 -0
  31. package/dist/bundle/plugins/parse-json/index.js +22 -0
  32. package/dist/bundle/plugins/parse-json/index.js.map +7 -0
  33. package/dist/bundle/plugins/parse-yaml/index.d.ts +13 -0
  34. package/dist/bundle/plugins/parse-yaml/index.d.ts.map +1 -0
  35. package/dist/bundle/plugins/parse-yaml/index.js +23 -0
  36. package/dist/bundle/plugins/parse-yaml/index.js.map +7 -0
  37. package/dist/bundle/plugins/read-files/index.d.ts +29 -0
  38. package/dist/bundle/plugins/read-files/index.d.ts.map +1 -0
  39. package/dist/bundle/plugins/read-files/index.js +30 -0
  40. package/dist/bundle/plugins/read-files/index.js.map +7 -0
  41. package/dist/bundle/value-generator.d.ts +79 -0
  42. package/dist/bundle/value-generator.d.ts.map +1 -0
  43. package/dist/bundle/value-generator.js +55 -0
  44. package/dist/bundle/value-generator.js.map +7 -0
  45. package/dist/dereference/dereference.d.ts +45 -0
  46. package/dist/dereference/dereference.d.ts.map +1 -0
  47. package/dist/dereference/dereference.js +37 -0
  48. package/dist/dereference/dereference.js.map +7 -0
  49. package/dist/dereference/index.d.ts +2 -0
  50. package/dist/dereference/index.d.ts.map +1 -0
  51. package/dist/dereference/index.js +5 -0
  52. package/dist/dereference/index.js.map +7 -0
  53. package/dist/diff/apply.d.ts +35 -0
  54. package/dist/diff/apply.d.ts.map +1 -0
  55. package/dist/diff/apply.js +40 -0
  56. package/dist/diff/apply.js.map +7 -0
  57. package/dist/diff/diff.d.ts +56 -0
  58. package/dist/diff/diff.d.ts.map +1 -0
  59. package/dist/diff/diff.js +33 -0
  60. package/dist/diff/diff.js.map +7 -0
  61. package/dist/diff/index.d.ts +5 -0
  62. package/dist/diff/index.d.ts.map +1 -0
  63. package/dist/diff/index.js +9 -0
  64. package/dist/diff/index.js.map +7 -0
  65. package/dist/diff/merge.d.ts +43 -0
  66. package/dist/diff/merge.d.ts.map +1 -0
  67. package/dist/diff/merge.js +61 -0
  68. package/dist/diff/merge.js.map +7 -0
  69. package/dist/diff/trie.d.ts +64 -0
  70. package/dist/diff/trie.d.ts.map +1 -0
  71. package/dist/diff/trie.js +82 -0
  72. package/dist/diff/trie.js.map +7 -0
  73. package/dist/diff/utils.d.ts +63 -0
  74. package/dist/diff/utils.d.ts.map +1 -0
  75. package/dist/diff/utils.js +48 -0
  76. package/dist/diff/utils.js.map +7 -0
  77. package/dist/magic-proxy/index.d.ts +2 -0
  78. package/dist/magic-proxy/index.d.ts.map +1 -0
  79. package/dist/magic-proxy/index.js +6 -0
  80. package/dist/magic-proxy/index.js.map +7 -0
  81. package/dist/magic-proxy/proxy.d.ts +63 -0
  82. package/dist/magic-proxy/proxy.d.ts.map +1 -0
  83. package/dist/magic-proxy/proxy.js +108 -0
  84. package/dist/magic-proxy/proxy.js.map +7 -0
  85. package/dist/polyfills/index.d.ts +2 -0
  86. package/dist/polyfills/index.d.ts.map +1 -0
  87. package/dist/polyfills/index.js +25 -0
  88. package/dist/polyfills/index.js.map +7 -0
  89. package/dist/polyfills/path.d.ts +24 -0
  90. package/dist/polyfills/path.d.ts.map +1 -0
  91. package/dist/polyfills/path.js +174 -0
  92. package/dist/polyfills/path.js.map +7 -0
  93. package/dist/types.d.ts +2 -0
  94. package/dist/types.d.ts.map +1 -0
  95. package/dist/types.js +1 -0
  96. package/dist/types.js.map +7 -0
  97. package/dist/utils/escape-json-pointer.d.ts +7 -0
  98. package/dist/utils/escape-json-pointer.d.ts.map +1 -0
  99. package/dist/utils/escape-json-pointer.js +7 -0
  100. package/dist/utils/escape-json-pointer.js.map +7 -0
  101. package/dist/utils/get-segments-from-path.d.ts +5 -0
  102. package/dist/utils/get-segments-from-path.d.ts.map +1 -0
  103. package/dist/utils/get-segments-from-path.js +11 -0
  104. package/dist/utils/get-segments-from-path.js.map +7 -0
  105. package/dist/utils/is-json-object.d.ts +18 -0
  106. package/dist/utils/is-json-object.d.ts.map +1 -0
  107. package/dist/utils/is-json-object.js +16 -0
  108. package/dist/utils/is-json-object.js.map +7 -0
  109. package/dist/utils/is-object.d.ts +5 -0
  110. package/dist/utils/is-object.d.ts.map +1 -0
  111. package/dist/utils/is-object.js +5 -0
  112. package/dist/utils/is-object.js.map +7 -0
  113. package/dist/utils/is-yaml.d.ts +17 -0
  114. package/dist/utils/is-yaml.d.ts.map +1 -0
  115. package/dist/utils/is-yaml.js +7 -0
  116. package/dist/utils/is-yaml.js.map +7 -0
  117. package/dist/utils/json-path-utils.d.ts +23 -0
  118. package/dist/utils/json-path-utils.d.ts.map +1 -0
  119. package/dist/utils/json-path-utils.js +16 -0
  120. package/dist/utils/json-path-utils.js.map +7 -0
  121. package/dist/utils/normalize.d.ts +5 -0
  122. package/dist/utils/normalize.d.ts.map +1 -0
  123. package/dist/utils/normalize.js +28 -0
  124. package/dist/utils/normalize.js.map +7 -0
  125. package/dist/utils/unescape-json-pointer.d.ts +8 -0
  126. package/dist/utils/unescape-json-pointer.d.ts.map +1 -0
  127. package/dist/utils/unescape-json-pointer.js +7 -0
  128. package/dist/utils/unescape-json-pointer.js.map +7 -0
  129. package/esbuild.ts +13 -0
  130. package/package.json +65 -0
  131. package/src/bundle/bundle.test.ts +1843 -0
  132. package/src/bundle/bundle.ts +758 -0
  133. package/src/bundle/create-limiter.test.ts +28 -0
  134. package/src/bundle/create-limiter.ts +52 -0
  135. package/src/bundle/index.ts +2 -0
  136. package/src/bundle/plugins/browser.ts +4 -0
  137. package/src/bundle/plugins/fetch-urls/index.test.ts +147 -0
  138. package/src/bundle/plugins/fetch-urls/index.ts +94 -0
  139. package/src/bundle/plugins/node.ts +5 -0
  140. package/src/bundle/plugins/parse-json/index.test.ts +22 -0
  141. package/src/bundle/plugins/parse-json/index.ts +30 -0
  142. package/src/bundle/plugins/parse-yaml/index.test.ts +24 -0
  143. package/src/bundle/plugins/parse-yaml/index.ts +31 -0
  144. package/src/bundle/plugins/read-files/index.test.ts +35 -0
  145. package/src/bundle/plugins/read-files/index.ts +55 -0
  146. package/src/bundle/value-generator.test.ts +166 -0
  147. package/src/bundle/value-generator.ts +147 -0
  148. package/src/dereference/dereference.test.ts +137 -0
  149. package/src/dereference/dereference.ts +84 -0
  150. package/src/dereference/index.ts +2 -0
  151. package/src/diff/apply.test.ts +262 -0
  152. package/src/diff/apply.ts +78 -0
  153. package/src/diff/diff.test.ts +328 -0
  154. package/src/diff/diff.ts +94 -0
  155. package/src/diff/index.test.ts +150 -0
  156. package/src/diff/index.ts +5 -0
  157. package/src/diff/merge.test.ts +1109 -0
  158. package/src/diff/merge.ts +136 -0
  159. package/src/diff/trie.test.ts +30 -0
  160. package/src/diff/trie.ts +113 -0
  161. package/src/diff/utils.test.ts +169 -0
  162. package/src/diff/utils.ts +113 -0
  163. package/src/magic-proxy/index.ts +2 -0
  164. package/src/magic-proxy/proxy.test.ts +145 -0
  165. package/src/magic-proxy/proxy.ts +225 -0
  166. package/src/polyfills/index.ts +12 -0
  167. package/src/polyfills/path.ts +248 -0
  168. package/src/types.ts +1 -0
  169. package/src/utils/escape-json-pointer.test.ts +13 -0
  170. package/src/utils/escape-json-pointer.ts +8 -0
  171. package/src/utils/get-segments-from-path.test.ts +17 -0
  172. package/src/utils/get-segments-from-path.ts +17 -0
  173. package/src/utils/is-json-object.ts +31 -0
  174. package/src/utils/is-object.test.ts +27 -0
  175. package/src/utils/is-object.ts +4 -0
  176. package/src/utils/is-yaml.ts +18 -0
  177. package/src/utils/json-path-utils.test.ts +13 -0
  178. package/src/utils/json-path-utils.ts +38 -0
  179. package/src/utils/normalize.test.ts +91 -0
  180. package/src/utils/normalize.ts +34 -0
  181. package/src/utils/unescape-json-pointer.test.ts +23 -0
  182. package/src/utils/unescape-json-pointer.ts +9 -0
  183. package/tsconfig.build.json +12 -0
  184. package/tsconfig.json +16 -0
  185. package/vite.config.ts +8 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/diff/diff.ts"],
4
+ "sourcesContent": ["/**\n * Represents the possible types of changes that can be made to a document.\n * - 'add': A new property is added\n * - 'update': An existing property's value is changed\n * - 'delete': A property is removed\n */\ntype ChangeType = 'add' | 'update' | 'delete'\n\n/**\n * Represents a single difference between two documents.\n * @property path - Array of strings representing the path to the changed property\n * @property changes - The new value for the property (for add/update) or the old value (for delete)\n * @property type - The type of change that occurred\n */\nexport type Difference = { path: string[]; changes: any; type: ChangeType }\n\n/**\n * Get the difference between two objects.\n *\n * This function performs a breadth-first comparison between two objects and returns\n * a list of operations needed to transform the first object into the second.\n *\n * @param doc1 - The source object to compare from\n * @param doc2 - The target object to compare to\n * @returns A list of operations (add/update/delete) with their paths and changes\n *\n * @example\n * // Compare two simple objects\n * const original = { name: 'John', age: 30 }\n * const updated = { name: 'John', age: 31, city: 'New York' }\n * const differences = diff(original, updated)\n * // Returns:\n * // [\n * // { path: ['age'], changes: 31, type: 'update' },\n * // { path: ['city'], changes: 'New York', type: 'add' }\n * // ]\n *\n * @example\n * // Compare nested objects\n * const original = {\n * user: { name: 'John', settings: { theme: 'light' } }\n * }\n * const updated = {\n * user: { name: 'John', settings: { theme: 'dark' } }\n * }\n * const differences = diff(original, updated)\n * // Returns:\n * // [\n * // { path: ['user', 'settings', 'theme'], changes: 'dark', type: 'update' }\n * // ]\n */\nexport const diff = (doc1: Record<string, unknown>, doc2: Record<string, unknown>): Difference[] => {\n const diff: Difference[] = []\n\n const bfs = (el1: unknown, el2: unknown, prefix = []) => {\n // If the types are different, we know that the property has been added, deleted or updated\n if (typeof el1 !== typeof el2) {\n if (typeof el1 === 'undefined') {\n diff.push({ path: prefix, changes: el2, type: 'add' })\n return\n }\n\n if (typeof el2 === 'undefined') {\n diff.push({ path: prefix, changes: el1, type: 'delete' })\n return\n }\n\n diff.push({ path: prefix, changes: el2, type: 'update' })\n return\n }\n\n // We now can assume that el1 and el2 are of the same type\n\n // For nested objects, we need to recursively check the properties\n if (typeof el1 === 'object' && typeof el2 === 'object' && el1 !== null && el2 !== null) {\n const keys = new Set([...Object.keys(el1), ...Object.keys(el2)])\n\n for (const key of keys) {\n // @ts-ignore\n bfs(el1[key], el2[key], [...prefix, key])\n }\n return\n }\n\n // For primitives, we can just compare the values\n if (el1 !== el2) {\n diff.push({ path: prefix, changes: el2, type: 'update' })\n }\n }\n\n // Run breadth-first search\n bfs(doc1, doc2)\n return diff\n}\n"],
5
+ "mappings": "AAmDO,MAAM,OAAO,CAAC,MAA+B,SAAgD;AAClG,QAAMA,QAAqB,CAAC;AAE5B,QAAM,MAAM,CAAC,KAAc,KAAc,SAAS,CAAC,MAAM;AAEvD,QAAI,OAAO,QAAQ,OAAO,KAAK;AAC7B,UAAI,OAAO,QAAQ,aAAa;AAC9B,QAAAA,MAAK,KAAK,EAAE,MAAM,QAAQ,SAAS,KAAK,MAAM,MAAM,CAAC;AACrD;AAAA,MACF;AAEA,UAAI,OAAO,QAAQ,aAAa;AAC9B,QAAAA,MAAK,KAAK,EAAE,MAAM,QAAQ,SAAS,KAAK,MAAM,SAAS,CAAC;AACxD;AAAA,MACF;AAEA,MAAAA,MAAK,KAAK,EAAE,MAAM,QAAQ,SAAS,KAAK,MAAM,SAAS,CAAC;AACxD;AAAA,IACF;AAKA,QAAI,OAAO,QAAQ,YAAY,OAAO,QAAQ,YAAY,QAAQ,QAAQ,QAAQ,MAAM;AACtF,YAAM,OAAO,oBAAI,IAAI,CAAC,GAAG,OAAO,KAAK,GAAG,GAAG,GAAG,OAAO,KAAK,GAAG,CAAC,CAAC;AAE/D,iBAAW,OAAO,MAAM;AAEtB,YAAI,IAAI,GAAG,GAAG,IAAI,GAAG,GAAG,CAAC,GAAG,QAAQ,GAAG,CAAC;AAAA,MAC1C;AACA;AAAA,IACF;AAGA,QAAI,QAAQ,KAAK;AACf,MAAAA,MAAK,KAAK,EAAE,MAAM,QAAQ,SAAS,KAAK,MAAM,SAAS,CAAC;AAAA,IAC1D;AAAA,EACF;AAGA,MAAI,MAAM,IAAI;AACd,SAAOA;AACT;",
6
+ "names": ["diff"]
7
+ }
@@ -0,0 +1,5 @@
1
+ import { diff, type Difference } from '../diff/diff.js';
2
+ import { apply } from '../diff/apply.js';
3
+ import { merge } from '../diff/merge.js';
4
+ export { diff, apply, merge, type Difference };
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/diff/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AAEpC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,CAAA"}
@@ -0,0 +1,9 @@
1
+ import { diff } from "../diff/diff.js";
2
+ import { apply } from "../diff/apply.js";
3
+ import { merge } from "../diff/merge.js";
4
+ export {
5
+ apply,
6
+ diff,
7
+ merge
8
+ };
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/diff/index.ts"],
4
+ "sourcesContent": ["import { diff, type Difference } from '@/diff/diff'\nimport { apply } from '@/diff/apply'\nimport { merge } from '@/diff/merge'\n\nexport { diff, apply, merge, type Difference }\n"],
5
+ "mappings": "AAAA,SAAS,YAA6B;AACtC,SAAS,aAAa;AACtB,SAAS,aAAa;",
6
+ "names": []
7
+ }
@@ -0,0 +1,43 @@
1
+ import type { Difference } from '../diff/diff.js';
2
+ /**
3
+ * Merges two sets of differences from the same document and resolves conflicts.
4
+ * This function combines changes from two diff lists while handling potential conflicts
5
+ * that arise when both diffs modify the same paths. It uses a trie data structure for
6
+ * efficient path matching and conflict detection.
7
+ *
8
+ * @param diff1 - First list of differences
9
+ * @param diff2 - Second list of differences
10
+ * @returns Object containing:
11
+ * - diffs: Combined list of non-conflicting differences
12
+ * - conflicts: Array of conflicting difference pairs that need manual resolution
13
+ *
14
+ * @example
15
+ * // Merge two sets of changes to a user profile
16
+ * const diff1 = [
17
+ * { path: ['name'], changes: 'John', type: 'update' },
18
+ * { path: ['age'], changes: 30, type: 'add' }
19
+ * ]
20
+ * const diff2 = [
21
+ * { path: ['name'], changes: 'Johnny', type: 'update' },
22
+ * { path: ['address'], changes: { city: 'NY' }, type: 'add' }
23
+ * ]
24
+ * const { diffs, conflicts } = merge(diff1, diff2)
25
+ * // Returns:
26
+ * // {
27
+ * // diffs: [
28
+ * // { path: ['age'], changes: 30, type: 'add' },
29
+ * // { path: ['address'], changes: { city: 'NY' }, type: 'add' }
30
+ * // ],
31
+ * // conflicts: [
32
+ * // [
33
+ * // [{ path: ['name'], changes: 'John', type: 'update' }],
34
+ * // [{ path: ['name'], changes: 'Johnny', type: 'update' }]
35
+ * // ]
36
+ * // ]
37
+ * // }
38
+ */
39
+ export declare const merge: (diff1: Difference[], diff2: Difference[]) => {
40
+ diffs: Difference[];
41
+ conflicts: [Difference[], Difference[]][];
42
+ };
43
+ //# sourceMappingURL=merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/diff/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAI7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,KAAK,UAAW,UAAU,EAAE,SAAS,UAAU,EAAE;;;CA8F7D,CAAA"}
@@ -0,0 +1,61 @@
1
+ import { Trie } from "../diff/trie.js";
2
+ import { isArrayEqual, isKeyCollisions, mergeObjects } from "../diff/utils.js";
3
+ const merge = (diff1, diff2) => {
4
+ const trie = new Trie();
5
+ for (const [index, diff] of diff1.entries()) {
6
+ trie.addPath(diff.path, { index, changes: diff });
7
+ }
8
+ const skipDiff1 = /* @__PURE__ */ new Set();
9
+ const skipDiff2 = /* @__PURE__ */ new Set();
10
+ const conflictsMap1 = /* @__PURE__ */ new Map();
11
+ const conflictsMap2 = /* @__PURE__ */ new Map();
12
+ for (const [index, diff] of diff2.entries()) {
13
+ trie.findMatch(diff.path, (value) => {
14
+ if (diff.type === "delete") {
15
+ if (value.changes.type === "delete") {
16
+ if (value.changes.path.length > diff.path.length) {
17
+ skipDiff1.add(value.index);
18
+ } else {
19
+ skipDiff2.add(value.index);
20
+ }
21
+ } else {
22
+ skipDiff1.add(value.index);
23
+ skipDiff2.add(index);
24
+ const conflictEntry = conflictsMap2.get(index);
25
+ if (conflictEntry !== void 0) {
26
+ conflictEntry[0].push(value.changes);
27
+ } else {
28
+ conflictsMap2.set(index, [[value.changes], [diff]]);
29
+ }
30
+ }
31
+ }
32
+ if (diff.type === "add" || diff.type === "update") {
33
+ if (isArrayEqual(diff.path, value.changes.path) && value.changes.type !== "delete" && !isKeyCollisions(diff.changes, value.changes.changes)) {
34
+ skipDiff1.add(value.index);
35
+ if (typeof diff.changes === "object") {
36
+ mergeObjects(diff.changes, value.changes.changes);
37
+ }
38
+ return;
39
+ }
40
+ skipDiff1.add(value.index);
41
+ skipDiff2.add(index);
42
+ const conflictEntry = conflictsMap1.get(value.index);
43
+ if (conflictEntry !== void 0) {
44
+ conflictEntry[1].push(diff);
45
+ } else {
46
+ conflictsMap1.set(value.index, [[value.changes], [diff]]);
47
+ }
48
+ }
49
+ });
50
+ }
51
+ const conflicts = [...conflictsMap1.values(), ...conflictsMap2.values()];
52
+ const diffs = [
53
+ ...diff1.filter((_, index) => !skipDiff1.has(index)),
54
+ ...diff2.filter((_, index) => !skipDiff2.has(index))
55
+ ];
56
+ return { diffs, conflicts };
57
+ };
58
+ export {
59
+ merge
60
+ };
61
+ //# sourceMappingURL=merge.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/diff/merge.ts"],
4
+ "sourcesContent": ["import type { Difference } from '@/diff/diff'\nimport { Trie } from '@/diff/trie'\nimport { isArrayEqual, isKeyCollisions, mergeObjects } from '@/diff/utils'\n\n/**\n * Merges two sets of differences from the same document and resolves conflicts.\n * This function combines changes from two diff lists while handling potential conflicts\n * that arise when both diffs modify the same paths. It uses a trie data structure for\n * efficient path matching and conflict detection.\n *\n * @param diff1 - First list of differences\n * @param diff2 - Second list of differences\n * @returns Object containing:\n * - diffs: Combined list of non-conflicting differences\n * - conflicts: Array of conflicting difference pairs that need manual resolution\n *\n * @example\n * // Merge two sets of changes to a user profile\n * const diff1 = [\n * { path: ['name'], changes: 'John', type: 'update' },\n * { path: ['age'], changes: 30, type: 'add' }\n * ]\n * const diff2 = [\n * { path: ['name'], changes: 'Johnny', type: 'update' },\n * { path: ['address'], changes: { city: 'NY' }, type: 'add' }\n * ]\n * const { diffs, conflicts } = merge(diff1, diff2)\n * // Returns:\n * // {\n * // diffs: [\n * // { path: ['age'], changes: 30, type: 'add' },\n * // { path: ['address'], changes: { city: 'NY' }, type: 'add' }\n * // ],\n * // conflicts: [\n * // [\n * // [{ path: ['name'], changes: 'John', type: 'update' }],\n * // [{ path: ['name'], changes: 'Johnny', type: 'update' }]\n * // ]\n * // ]\n * // }\n */\nexport const merge = (diff1: Difference[], diff2: Difference[]) => {\n // Here we need to use a trie to optimize searching for a prefix\n // With the naive approach time complexity of the algorithm would be\n // O(n * m)\n // ^ ^\n // n is the length off diff1 | | m length of diff2\n //\n // Assuming that the maximum depth of the nested objects would be constant lets say 0 <= D <= 100\n // we try to optimize for that using the tire data structure.\n // So the new time complexity would be O(n * D) where D is the maximum depth of the nested object\n const trie = new Trie<{ index: number; changes: Difference }>()\n\n // Create the trie\n for (const [index, diff] of diff1.entries()) {\n trie.addPath(diff.path, { index, changes: diff })\n }\n\n const skipDiff1 = new Set<number>()\n const skipDiff2 = new Set<number>()\n\n // Keep related conflicts together for easy A, B pick conflict resolution\n // map key is going to be conflicting index of first diff list where the diff will be\n // a delete operation or an add/update operation with a one to many conflicts\n const conflictsMap1 = new Map<number, [Difference[], Difference[]]>()\n // map key will be the index from the second diff list where the diff will be\n // a delete operation with one to many conflicts\n const conflictsMap2 = new Map<number, [Difference[], Difference[]]>()\n\n for (const [index, diff] of diff2.entries()) {\n trie.findMatch(diff.path, (value) => {\n if (diff.type === 'delete') {\n if (value.changes.type === 'delete') {\n // Keep the highest depth delete operation and skip the other\n if (value.changes.path.length > diff.path.length) {\n skipDiff1.add(value.index)\n } else {\n skipDiff2.add(value.index)\n }\n } else {\n // Take care of updates/add on the same path (we are sure they will be on the\n // same path since the change comes from the same document)\n skipDiff1.add(value.index)\n skipDiff2.add(index)\n\n const conflictEntry = conflictsMap2.get(index)\n\n if (conflictEntry !== undefined) {\n conflictEntry[0].push(value.changes)\n } else {\n conflictsMap2.set(index, [[value.changes], [diff]])\n }\n }\n }\n\n if (diff.type === 'add' || diff.type === 'update') {\n // For add -> add / update -> update operation we try to first see if we can merge this operations\n if (\n isArrayEqual(diff.path, value.changes.path) &&\n value.changes.type !== 'delete' &&\n !isKeyCollisions(diff.changes, value.changes.changes)\n ) {\n skipDiff1.add(value.index)\n // For non primitive values we merge object keys into diff2\n if (typeof diff.changes === 'object') {\n mergeObjects(diff.changes, value.changes.changes)\n }\n return\n }\n\n // add/update -> delete operations always resolve in conflicts\n skipDiff1.add(value.index)\n skipDiff2.add(index)\n\n const conflictEntry = conflictsMap1.get(value.index)\n\n if (conflictEntry !== undefined) {\n conflictEntry[1].push(diff)\n } else {\n conflictsMap1.set(value.index, [[value.changes], [diff]])\n }\n }\n })\n }\n\n const conflicts: [Difference[], Difference[]][] = [...conflictsMap1.values(), ...conflictsMap2.values()]\n\n // Filter all changes that should be skipped because of conflicts\n // or auto conflict resolution\n const diffs: Difference[] = [\n ...diff1.filter((_, index) => !skipDiff1.has(index)),\n ...diff2.filter((_, index) => !skipDiff2.has(index)),\n ]\n\n return { diffs, conflicts }\n}\n"],
5
+ "mappings": "AACA,SAAS,YAAY;AACrB,SAAS,cAAc,iBAAiB,oBAAoB;AAuCrD,MAAM,QAAQ,CAAC,OAAqB,UAAwB;AAUjE,QAAM,OAAO,IAAI,KAA6C;AAG9D,aAAW,CAAC,OAAO,IAAI,KAAK,MAAM,QAAQ,GAAG;AAC3C,SAAK,QAAQ,KAAK,MAAM,EAAE,OAAO,SAAS,KAAK,CAAC;AAAA,EAClD;AAEA,QAAM,YAAY,oBAAI,IAAY;AAClC,QAAM,YAAY,oBAAI,IAAY;AAKlC,QAAM,gBAAgB,oBAAI,IAA0C;AAGpE,QAAM,gBAAgB,oBAAI,IAA0C;AAEpE,aAAW,CAAC,OAAO,IAAI,KAAK,MAAM,QAAQ,GAAG;AAC3C,SAAK,UAAU,KAAK,MAAM,CAAC,UAAU;AACnC,UAAI,KAAK,SAAS,UAAU;AAC1B,YAAI,MAAM,QAAQ,SAAS,UAAU;AAEnC,cAAI,MAAM,QAAQ,KAAK,SAAS,KAAK,KAAK,QAAQ;AAChD,sBAAU,IAAI,MAAM,KAAK;AAAA,UAC3B,OAAO;AACL,sBAAU,IAAI,MAAM,KAAK;AAAA,UAC3B;AAAA,QACF,OAAO;AAGL,oBAAU,IAAI,MAAM,KAAK;AACzB,oBAAU,IAAI,KAAK;AAEnB,gBAAM,gBAAgB,cAAc,IAAI,KAAK;AAE7C,cAAI,kBAAkB,QAAW;AAC/B,0BAAc,CAAC,EAAE,KAAK,MAAM,OAAO;AAAA,UACrC,OAAO;AACL,0BAAc,IAAI,OAAO,CAAC,CAAC,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC;AAAA,UACpD;AAAA,QACF;AAAA,MACF;AAEA,UAAI,KAAK,SAAS,SAAS,KAAK,SAAS,UAAU;AAEjD,YACE,aAAa,KAAK,MAAM,MAAM,QAAQ,IAAI,KAC1C,MAAM,QAAQ,SAAS,YACvB,CAAC,gBAAgB,KAAK,SAAS,MAAM,QAAQ,OAAO,GACpD;AACA,oBAAU,IAAI,MAAM,KAAK;AAEzB,cAAI,OAAO,KAAK,YAAY,UAAU;AACpC,yBAAa,KAAK,SAAS,MAAM,QAAQ,OAAO;AAAA,UAClD;AACA;AAAA,QACF;AAGA,kBAAU,IAAI,MAAM,KAAK;AACzB,kBAAU,IAAI,KAAK;AAEnB,cAAM,gBAAgB,cAAc,IAAI,MAAM,KAAK;AAEnD,YAAI,kBAAkB,QAAW;AAC/B,wBAAc,CAAC,EAAE,KAAK,IAAI;AAAA,QAC5B,OAAO;AACL,wBAAc,IAAI,MAAM,OAAO,CAAC,CAAC,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC;AAAA,QAC1D;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,YAA4C,CAAC,GAAG,cAAc,OAAO,GAAG,GAAG,cAAc,OAAO,CAAC;AAIvG,QAAM,QAAsB;AAAA,IAC1B,GAAG,MAAM,OAAO,CAAC,GAAG,UAAU,CAAC,UAAU,IAAI,KAAK,CAAC;AAAA,IACnD,GAAG,MAAM,OAAO,CAAC,GAAG,UAAU,CAAC,UAAU,IAAI,KAAK,CAAC;AAAA,EACrD;AAEA,SAAO,EAAE,OAAO,UAAU;AAC5B;",
6
+ "names": []
7
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Trie data structure
3
+ *
4
+ * Read more: https://en.wikipedia.org/wiki/Trie
5
+ */
6
+ /**
7
+ * Represents a node in the trie data structure.
8
+ * Each node can store a value and has a map of child nodes.
9
+ *
10
+ * @template Value - The type of value that can be stored in the node
11
+ */
12
+ export declare class TrieNode<Value> {
13
+ value: Value | null;
14
+ children: Record<string, TrieNode<Value>>;
15
+ constructor(value: Value | null, children: Record<string, TrieNode<Value>>);
16
+ }
17
+ /**
18
+ * A trie (prefix tree) data structure implementation.
19
+ * This class provides efficient storage and retrieval of values associated with string paths.
20
+ *
21
+ * @template Value - The type of value to store at each node
22
+ *
23
+ * @example
24
+ * const trie = new Trie<number>()
25
+ * trie.addPath(['a', 'b', 'c'], 1)
26
+ * trie.addPath(['a', 'b', 'd'], 2)
27
+ * trie.findMatch(['a', 'b'], (value) => console.log(value)) // Logs: 1, 2
28
+ */
29
+ export declare class Trie<Value> {
30
+ private root;
31
+ constructor();
32
+ /**
33
+ * Adds a value to the trie at the specified path.
34
+ * Creates new nodes as needed to build the path.
35
+ *
36
+ * @param path - Array of strings representing the path to store the value
37
+ * @param value - The value to store at the end of the path
38
+ *
39
+ * @example
40
+ * const trie = new Trie<number>()
41
+ * trie.addPath(['users', 'john', 'age'], 30)
42
+ */
43
+ addPath(path: string[], value: Value): void;
44
+ /**
45
+ * Finds all matches along a given path in the trie.
46
+ * This method traverses both the exact path and all deeper paths,
47
+ * executing a callback for each matching value found.
48
+ *
49
+ * The search is performed in two phases:
50
+ * 1. Traverse the exact path, checking for matches at each node
51
+ * 2. Perform a depth-first search from the end of the path to find all deeper matches
52
+ *
53
+ * @param path - Array of strings representing the path to search
54
+ * @param callback - Function to execute for each matching value found
55
+ *
56
+ * @example
57
+ * const trie = new Trie<number>()
58
+ * trie.addPath(['a', 'b', 'c'], 1)
59
+ * trie.addPath(['a', 'b', 'd'], 2)
60
+ * trie.findMatch(['a', 'b'], (value) => console.log(value)) // Logs: 1, 2
61
+ */
62
+ findMatch(path: string[], callback: (value: Value) => void): void;
63
+ }
64
+ //# sourceMappingURL=trie.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trie.d.ts","sourceRoot":"","sources":["../../src/diff/trie.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;GAKG;AACH,qBAAa,QAAQ,CAAC,KAAK;IAEhB,KAAK,EAAE,KAAK,GAAG,IAAI;IACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;gBADzC,KAAK,EAAE,KAAK,GAAG,IAAI,EACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;CAEnD;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,IAAI,CAAC,KAAK;IACrB,OAAO,CAAC,IAAI,CAAiB;;IAK7B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK;IAcpC;;;;;;;;;;;;;;;;;OAiBG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI;CAgC3D"}
@@ -0,0 +1,82 @@
1
+ class TrieNode {
2
+ constructor(value, children) {
3
+ this.value = value;
4
+ this.children = children;
5
+ }
6
+ }
7
+ class Trie {
8
+ root;
9
+ constructor() {
10
+ this.root = new TrieNode(null, {});
11
+ }
12
+ /**
13
+ * Adds a value to the trie at the specified path.
14
+ * Creates new nodes as needed to build the path.
15
+ *
16
+ * @param path - Array of strings representing the path to store the value
17
+ * @param value - The value to store at the end of the path
18
+ *
19
+ * @example
20
+ * const trie = new Trie<number>()
21
+ * trie.addPath(['users', 'john', 'age'], 30)
22
+ */
23
+ addPath(path, value) {
24
+ let current = this.root;
25
+ for (const dir of path) {
26
+ if (current.children[dir]) {
27
+ current = current.children[dir];
28
+ } else {
29
+ current.children[dir] = new TrieNode(null, {});
30
+ current = current.children[dir];
31
+ }
32
+ }
33
+ current.value = value;
34
+ }
35
+ /**
36
+ * Finds all matches along a given path in the trie.
37
+ * This method traverses both the exact path and all deeper paths,
38
+ * executing a callback for each matching value found.
39
+ *
40
+ * The search is performed in two phases:
41
+ * 1. Traverse the exact path, checking for matches at each node
42
+ * 2. Perform a depth-first search from the end of the path to find all deeper matches
43
+ *
44
+ * @param path - Array of strings representing the path to search
45
+ * @param callback - Function to execute for each matching value found
46
+ *
47
+ * @example
48
+ * const trie = new Trie<number>()
49
+ * trie.addPath(['a', 'b', 'c'], 1)
50
+ * trie.addPath(['a', 'b', 'd'], 2)
51
+ * trie.findMatch(['a', 'b'], (value) => console.log(value)) // Logs: 1, 2
52
+ */
53
+ findMatch(path, callback) {
54
+ let current = this.root;
55
+ for (const dir of path) {
56
+ if (current.value !== null) {
57
+ callback(current.value);
58
+ }
59
+ const next = current.children[dir];
60
+ if (!next) {
61
+ return;
62
+ }
63
+ current = next;
64
+ }
65
+ const dfs = (current2) => {
66
+ for (const child of Object.keys(current2?.children ?? {})) {
67
+ if (current2 && Object.hasOwn(current2.children, child)) {
68
+ dfs(current2?.children[child]);
69
+ }
70
+ }
71
+ if (current2?.value) {
72
+ callback(current2.value);
73
+ }
74
+ };
75
+ dfs(current);
76
+ }
77
+ }
78
+ export {
79
+ Trie,
80
+ TrieNode
81
+ };
82
+ //# sourceMappingURL=trie.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/diff/trie.ts"],
4
+ "sourcesContent": ["/**\n * Trie data structure\n *\n * Read more: https://en.wikipedia.org/wiki/Trie\n */\n\n/**\n * Represents a node in the trie data structure.\n * Each node can store a value and has a map of child nodes.\n *\n * @template Value - The type of value that can be stored in the node\n */\nexport class TrieNode<Value> {\n constructor(\n public value: Value | null,\n public children: Record<string, TrieNode<Value>>,\n ) {}\n}\n\n/**\n * A trie (prefix tree) data structure implementation.\n * This class provides efficient storage and retrieval of values associated with string paths.\n *\n * @template Value - The type of value to store at each node\n *\n * @example\n * const trie = new Trie<number>()\n * trie.addPath(['a', 'b', 'c'], 1)\n * trie.addPath(['a', 'b', 'd'], 2)\n * trie.findMatch(['a', 'b'], (value) => console.log(value)) // Logs: 1, 2\n */\nexport class Trie<Value> {\n private root: TrieNode<Value>\n constructor() {\n this.root = new TrieNode<Value>(null, {})\n }\n\n /**\n * Adds a value to the trie at the specified path.\n * Creates new nodes as needed to build the path.\n *\n * @param path - Array of strings representing the path to store the value\n * @param value - The value to store at the end of the path\n *\n * @example\n * const trie = new Trie<number>()\n * trie.addPath(['users', 'john', 'age'], 30)\n */\n addPath(path: string[], value: Value) {\n let current = this.root\n for (const dir of path) {\n if (current.children[dir]) {\n current = current.children[dir]\n } else {\n current.children[dir] = new TrieNode<Value>(null, {})\n current = current.children[dir]\n }\n }\n\n current.value = value\n }\n\n /**\n * Finds all matches along a given path in the trie.\n * This method traverses both the exact path and all deeper paths,\n * executing a callback for each matching value found.\n *\n * The search is performed in two phases:\n * 1. Traverse the exact path, checking for matches at each node\n * 2. Perform a depth-first search from the end of the path to find all deeper matches\n *\n * @param path - Array of strings representing the path to search\n * @param callback - Function to execute for each matching value found\n *\n * @example\n * const trie = new Trie<number>()\n * trie.addPath(['a', 'b', 'c'], 1)\n * trie.addPath(['a', 'b', 'd'], 2)\n * trie.findMatch(['a', 'b'], (value) => console.log(value)) // Logs: 1, 2\n */\n findMatch(path: string[], callback: (value: Value) => void) {\n let current = this.root\n\n for (const dir of path) {\n // Note: the last callback wont fire here because it will fire on the dfs\n if (current.value !== null) {\n callback(current.value)\n }\n\n const next = current.children[dir]\n if (!next) {\n return\n }\n\n current = next\n }\n\n const dfs = (current: TrieNode<Value> | undefined) => {\n for (const child of Object.keys(current?.children ?? {})) {\n if (current && Object.hasOwn(current.children, child)) {\n dfs(current?.children[child])\n }\n }\n\n if (current?.value) {\n callback(current.value)\n }\n }\n\n // Dfs for the rest of the path\n dfs(current)\n }\n}\n"],
5
+ "mappings": "AAYO,MAAM,SAAgB;AAAA,EAC3B,YACS,OACA,UACP;AAFO;AACA;AAAA,EACN;AACL;AAcO,MAAM,KAAY;AAAA,EACf;AAAA,EACR,cAAc;AACZ,SAAK,OAAO,IAAI,SAAgB,MAAM,CAAC,CAAC;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,QAAQ,MAAgB,OAAc;AACpC,QAAI,UAAU,KAAK;AACnB,eAAW,OAAO,MAAM;AACtB,UAAI,QAAQ,SAAS,GAAG,GAAG;AACzB,kBAAU,QAAQ,SAAS,GAAG;AAAA,MAChC,OAAO;AACL,gBAAQ,SAAS,GAAG,IAAI,IAAI,SAAgB,MAAM,CAAC,CAAC;AACpD,kBAAU,QAAQ,SAAS,GAAG;AAAA,MAChC;AAAA,IACF;AAEA,YAAQ,QAAQ;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,UAAU,MAAgB,UAAkC;AAC1D,QAAI,UAAU,KAAK;AAEnB,eAAW,OAAO,MAAM;AAEtB,UAAI,QAAQ,UAAU,MAAM;AAC1B,iBAAS,QAAQ,KAAK;AAAA,MACxB;AAEA,YAAM,OAAO,QAAQ,SAAS,GAAG;AACjC,UAAI,CAAC,MAAM;AACT;AAAA,MACF;AAEA,gBAAU;AAAA,IACZ;AAEA,UAAM,MAAM,CAACA,aAAyC;AACpD,iBAAW,SAAS,OAAO,KAAKA,UAAS,YAAY,CAAC,CAAC,GAAG;AACxD,YAAIA,YAAW,OAAO,OAAOA,SAAQ,UAAU,KAAK,GAAG;AACrD,cAAIA,UAAS,SAAS,KAAK,CAAC;AAAA,QAC9B;AAAA,MACF;AAEA,UAAIA,UAAS,OAAO;AAClB,iBAASA,SAAQ,KAAK;AAAA,MACxB;AAAA,IACF;AAGA,QAAI,OAAO;AAAA,EACb;AACF;",
6
+ "names": ["current"]
7
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Deep check for objects for collisions
3
+ * Check primitives if their values are different
4
+ *
5
+ * @param a - First value to compare
6
+ * @param b - Second value to compare
7
+ * @returns true if there is a collision, false otherwise
8
+ *
9
+ * @example
10
+ * // Objects with different values for same key
11
+ * isKeyCollisions({ a: 1 }, { a: 2 }) // true
12
+ *
13
+ * // Objects with different types
14
+ * isKeyCollisions({ a: 1 }, { a: '1' }) // true
15
+ *
16
+ * // Objects with no collisions
17
+ * isKeyCollisions({ a: 1 }, { b: 2 }) // false
18
+ *
19
+ * // Nested objects with collision
20
+ * isKeyCollisions({ a: { b: 1 } }, { a: { b: 2 } }) // true
21
+ */
22
+ export declare const isKeyCollisions: (a: unknown, b: unknown) => boolean;
23
+ /**
24
+ * Deep merges two objects, combining their properties recursively.
25
+ *
26
+ * ⚠️ Note: This operation assumes there are no key collisions between the objects.
27
+ * Use isKeyCollisions() to check for collisions before merging.
28
+ *
29
+ * @param a - Target object to merge into
30
+ * @param b - Source object to merge from
31
+ * @returns The merged object (mutates and returns a)
32
+ *
33
+ * @example
34
+ * // Simple merge
35
+ * const a = { name: 'John' }
36
+ * const b = { age: 30 }
37
+ * mergeObjects(a, b) // { name: 'John', age: 30 }
38
+ *
39
+ * // Nested merge
40
+ * const a = { user: { name: 'John' } }
41
+ * const b = { user: { age: 30 } }
42
+ * mergeObjects(a, b) // { user: { name: 'John', age: 30 } }
43
+ */
44
+ export declare const mergeObjects: (a: Record<string, unknown>, b: Record<string, unknown>) => Record<string, unknown>;
45
+ /**
46
+ * Checks if two arrays have the same elements in the same order.
47
+ *
48
+ * @param a - First array to compare
49
+ * @param b - Second array to compare
50
+ * @returns True if arrays have same length and elements, false otherwise
51
+ *
52
+ * @example
53
+ * // Arrays with same elements
54
+ * isArrayEqual([1, 2, 3], [1, 2, 3]) // true
55
+ *
56
+ * // Arrays with different elements
57
+ * isArrayEqual([1, 2, 3], [1, 2, 4]) // false
58
+ *
59
+ * // Arrays with different lengths
60
+ * isArrayEqual([1, 2], [1, 2, 3]) // false
61
+ */
62
+ export declare const isArrayEqual: <T>(a: T[], b: T[]) => boolean;
63
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/diff/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,eAAe,MAAO,OAAO,KAAK,OAAO,YAsBrD,CAAA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,YAAY,MAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAe3G,CAAA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,EAAE,YAY7C,CAAA"}
@@ -0,0 +1,48 @@
1
+ const isKeyCollisions = (a, b) => {
2
+ if (typeof a !== typeof b) {
3
+ return true;
4
+ }
5
+ if (typeof a === "object" && typeof b === "object" && a !== null && b !== null) {
6
+ const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
7
+ for (const key of keys) {
8
+ if (a[key] !== void 0 && b[key] !== void 0) {
9
+ if (isKeyCollisions(a[key], b[key])) {
10
+ return true;
11
+ }
12
+ }
13
+ }
14
+ return false;
15
+ }
16
+ return a !== b;
17
+ };
18
+ const mergeObjects = (a, b) => {
19
+ for (const key in b) {
20
+ if (!(key in a)) {
21
+ a[key] = b[key];
22
+ } else {
23
+ const aValue = a[key];
24
+ const bValue = b[key];
25
+ if (typeof aValue === "object" && aValue !== null && typeof bValue === "object" && bValue !== null) {
26
+ a[key] = mergeObjects(aValue, bValue);
27
+ }
28
+ }
29
+ }
30
+ return a;
31
+ };
32
+ const isArrayEqual = (a, b) => {
33
+ if (a.length !== b.length) {
34
+ return false;
35
+ }
36
+ for (let i = 0; i <= a.length; ++i) {
37
+ if (a[i] !== b[i]) {
38
+ return false;
39
+ }
40
+ }
41
+ return true;
42
+ };
43
+ export {
44
+ isArrayEqual,
45
+ isKeyCollisions,
46
+ mergeObjects
47
+ };
48
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/diff/utils.ts"],
4
+ "sourcesContent": ["/**\n * Deep check for objects for collisions\n * Check primitives if their values are different\n *\n * @param a - First value to compare\n * @param b - Second value to compare\n * @returns true if there is a collision, false otherwise\n *\n * @example\n * // Objects with different values for same key\n * isKeyCollisions({ a: 1 }, { a: 2 }) // true\n *\n * // Objects with different types\n * isKeyCollisions({ a: 1 }, { a: '1' }) // true\n *\n * // Objects with no collisions\n * isKeyCollisions({ a: 1 }, { b: 2 }) // false\n *\n * // Nested objects with collision\n * isKeyCollisions({ a: { b: 1 } }, { a: { b: 2 } }) // true\n */\nexport const isKeyCollisions = (a: unknown, b: unknown) => {\n if (typeof a !== typeof b) {\n return true\n }\n\n if (typeof a === 'object' && typeof b === 'object' && a !== null && b !== null) {\n const keys = new Set([...Object.keys(a), ...Object.keys(b)])\n\n for (const key of keys) {\n // @ts-ignore\n if (a[key] !== undefined && b[key] !== undefined) {\n // @ts-ignore\n if (isKeyCollisions(a[key], b[key])) {\n return true\n }\n }\n }\n return false\n }\n\n // We handle all primitives here\n return a !== b\n}\n\n/**\n * Deep merges two objects, combining their properties recursively.\n *\n * \u26A0\uFE0F Note: This operation assumes there are no key collisions between the objects.\n * Use isKeyCollisions() to check for collisions before merging.\n *\n * @param a - Target object to merge into\n * @param b - Source object to merge from\n * @returns The merged object (mutates and returns a)\n *\n * @example\n * // Simple merge\n * const a = { name: 'John' }\n * const b = { age: 30 }\n * mergeObjects(a, b) // { name: 'John', age: 30 }\n *\n * // Nested merge\n * const a = { user: { name: 'John' } }\n * const b = { user: { age: 30 } }\n * mergeObjects(a, b) // { user: { name: 'John', age: 30 } }\n */\nexport const mergeObjects = (a: Record<string, unknown>, b: Record<string, unknown>): Record<string, unknown> => {\n for (const key in b) {\n if (!(key in a)) {\n a[key] = b[key]\n } else {\n const aValue = a[key]\n const bValue = b[key]\n\n if (typeof aValue === 'object' && aValue !== null && typeof bValue === 'object' && bValue !== null) {\n a[key] = mergeObjects(aValue as Record<string, unknown>, bValue as Record<string, unknown>)\n }\n }\n }\n\n return a\n}\n\n/**\n * Checks if two arrays have the same elements in the same order.\n *\n * @param a - First array to compare\n * @param b - Second array to compare\n * @returns True if arrays have same length and elements, false otherwise\n *\n * @example\n * // Arrays with same elements\n * isArrayEqual([1, 2, 3], [1, 2, 3]) // true\n *\n * // Arrays with different elements\n * isArrayEqual([1, 2, 3], [1, 2, 4]) // false\n *\n * // Arrays with different lengths\n * isArrayEqual([1, 2], [1, 2, 3]) // false\n */\nexport const isArrayEqual = <T>(a: T[], b: T[]) => {\n if (a.length !== b.length) {\n return false\n }\n\n for (let i = 0; i <= a.length; ++i) {\n if (a[i] !== b[i]) {\n return false\n }\n }\n\n return true\n}\n"],
5
+ "mappings": "AAqBO,MAAM,kBAAkB,CAAC,GAAY,MAAe;AACzD,MAAI,OAAO,MAAM,OAAO,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,MAAM,QAAQ,MAAM,MAAM;AAC9E,UAAM,OAAO,oBAAI,IAAI,CAAC,GAAG,OAAO,KAAK,CAAC,GAAG,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC;AAE3D,eAAW,OAAO,MAAM;AAEtB,UAAI,EAAE,GAAG,MAAM,UAAa,EAAE,GAAG,MAAM,QAAW;AAEhD,YAAI,gBAAgB,EAAE,GAAG,GAAG,EAAE,GAAG,CAAC,GAAG;AACnC,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAGA,SAAO,MAAM;AACf;AAuBO,MAAM,eAAe,CAAC,GAA4B,MAAwD;AAC/G,aAAW,OAAO,GAAG;AACnB,QAAI,EAAE,OAAO,IAAI;AACf,QAAE,GAAG,IAAI,EAAE,GAAG;AAAA,IAChB,OAAO;AACL,YAAM,SAAS,EAAE,GAAG;AACpB,YAAM,SAAS,EAAE,GAAG;AAEpB,UAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,OAAO,WAAW,YAAY,WAAW,MAAM;AAClG,UAAE,GAAG,IAAI,aAAa,QAAmC,MAAiC;AAAA,MAC5F;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAmBO,MAAM,eAAe,CAAI,GAAQ,MAAW;AACjD,MAAI,EAAE,WAAW,EAAE,QAAQ;AACzB,WAAO;AAAA,EACT;AAEA,WAAS,IAAI,GAAG,KAAK,EAAE,QAAQ,EAAE,GAAG;AAClC,QAAI,EAAE,CAAC,MAAM,EAAE,CAAC,GAAG;AACjB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,2 @@
1
+ export { createMagicProxy, getRaw } from './proxy.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/magic-proxy/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA"}
@@ -0,0 +1,6 @@
1
+ import { createMagicProxy, getRaw } from "./proxy.js";
2
+ export {
3
+ createMagicProxy,
4
+ getRaw
5
+ };
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/magic-proxy/index.ts"],
4
+ "sourcesContent": ["// biome-ignore lint/performance/noBarrelFile: <explanation>\nexport { createMagicProxy, getRaw } from './proxy'\n"],
5
+ "mappings": "AACA,SAAS,kBAAkB,cAAc;",
6
+ "names": []
7
+ }
@@ -0,0 +1,63 @@
1
+ import type { UnknownObject } from '../types.js';
2
+ /**
3
+ * Creates a proxy that automatically resolves JSON references ($ref) in an object.
4
+ * The proxy intercepts property access and automatically resolves any $ref references
5
+ * to their target values in the source document.
6
+ *
7
+ * @param targetObject - The object to create a proxy for
8
+ * @param sourceDocument - The source document containing the reference targets (defaults to targetObject)
9
+ * @param resolvedProxyCache - Optional cache to store resolved proxies and prevent duplicate proxies
10
+ * @returns A proxy that automatically resolves $ref references
11
+ *
12
+ * @example
13
+ * // Basic usage with local references
14
+ * const doc = {
15
+ * components: {
16
+ * schemas: {
17
+ * User: { type: 'object', properties: { name: { type: 'string' } } }
18
+ * }
19
+ * },
20
+ * paths: {
21
+ * '/users': {
22
+ * get: {
23
+ * responses: {
24
+ * 200: {
25
+ * content: {
26
+ * 'application/json': {
27
+ * schema: { $ref: '#/components/schemas/User' }
28
+ * }
29
+ * }
30
+ * }
31
+ * }
32
+ * }
33
+ * }
34
+ * }
35
+ * }
36
+ *
37
+ * const proxy = createMagicProxy(doc)
38
+ * // Accessing the schema will automatically resolve the $ref
39
+ * console.log(proxy.paths['/users'].get.responses[200].content['application/json'].schema)
40
+ * // Output: { type: 'object', properties: { name: { type: 'string' } } }
41
+ *
42
+ * @example
43
+ * // Using with a cache to prevent duplicate proxies
44
+ * const cache = new WeakMap()
45
+ * const proxy1 = createMagicProxy(doc, doc, cache)
46
+ * const proxy2 = createMagicProxy(doc, doc, cache)
47
+ * // proxy1 and proxy2 are the same instance due to caching
48
+ * console.log(proxy1 === proxy2) // true
49
+ */
50
+ export declare function createMagicProxy<T extends UnknownObject | UnknownObject[]>(targetObject: T, sourceDocument?: T, resolvedProxyCache?: WeakMap<object, T>): T;
51
+ export declare const TARGET_SYMBOL: unique symbol;
52
+ /**
53
+ * Gets the raw (non-proxied) version of an object created by createMagicProxy.
54
+ * This is useful when you need to access the original object without the magic proxy wrapper.
55
+ *
56
+ * @param obj - The magic proxy object to get the raw version of
57
+ * @returns The raw version of the object
58
+ * @example
59
+ * const proxy = createMagicProxy({ foo: { $ref: '#/bar' } })
60
+ * const raw = getRaw(proxy) // { foo: { $ref: '#/bar' } }
61
+ */
62
+ export declare function getRaw<T extends UnknownObject>(obj: T): T;
63
+ //# sourceMappingURL=proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/magic-proxy/proxy.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AA8H5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,aAAa,GAAG,aAAa,EAAE,EACxE,YAAY,EAAE,CAAC,EACf,cAAc,GAAE,CAAgB,EAChC,kBAAkB,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GACtC,CAAC,CAyBH;AAED,eAAO,MAAM,aAAa,eAA6B,CAAA;AACvD;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,aAAa,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAMzD"}