@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,22 @@
1
+ import { isJsonObject } from "../../../utils/is-json-object.js";
2
+ function parseJson() {
3
+ return {
4
+ validate: isJsonObject,
5
+ exec: async (value) => {
6
+ try {
7
+ return {
8
+ ok: true,
9
+ data: JSON.parse(value)
10
+ };
11
+ } catch {
12
+ return {
13
+ ok: false
14
+ };
15
+ }
16
+ }
17
+ };
18
+ }
19
+ export {
20
+ parseJson
21
+ };
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/bundle/plugins/parse-json/index.ts"],
4
+ "sourcesContent": ["import { isJsonObject } from '@/utils/is-json-object'\nimport type { Plugin, ResolveResult } from '@/bundle'\n\n/**\n * Creates a plugin that parses JSON strings into JavaScript objects.\n * @returns A plugin object with validate and exec functions\n * @example\n * ```ts\n * const jsonPlugin = parseJson()\n * const result = jsonPlugin.exec('{\"name\": \"John\", \"age\": 30}')\n * // result = { name: 'John', age: 30 }\n * ```\n */\nexport function parseJson(): Plugin {\n return {\n validate: isJsonObject,\n exec: async (value): Promise<ResolveResult> => {\n try {\n return {\n ok: true,\n data: JSON.parse(value),\n }\n } catch {\n return {\n ok: false,\n }\n }\n },\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAatB,SAAS,YAAoB;AAClC,SAAO;AAAA,IACL,UAAU;AAAA,IACV,MAAM,OAAO,UAAkC;AAC7C,UAAI;AACF,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,MAAM,KAAK,MAAM,KAAK;AAAA,QACxB;AAAA,MACF,QAAQ;AACN,eAAO;AAAA,UACL,IAAI;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,13 @@
1
+ import type { Plugin } from '../../../bundle/bundle.js';
2
+ /**
3
+ * Creates a plugin that parses YAML strings into JavaScript objects.
4
+ * @returns A plugin object with validate and exec functions
5
+ * @example
6
+ * ```ts
7
+ * const yamlPlugin = parseYaml()
8
+ * const result = yamlPlugin.exec('name: John\nage: 30')
9
+ * // result = { name: 'John', age: 30 }
10
+ * ```
11
+ */
12
+ export declare function parseYaml(): Plugin;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/bundle/plugins/parse-yaml/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,iBAAiB,CAAA;AAI5D;;;;;;;;;GASG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAgBlC"}
@@ -0,0 +1,23 @@
1
+ import { isYaml } from "../../../utils/is-yaml.js";
2
+ import YAML from "yaml";
3
+ function parseYaml() {
4
+ return {
5
+ validate: isYaml,
6
+ exec: async (value) => {
7
+ try {
8
+ return {
9
+ ok: true,
10
+ data: YAML.parse(value)
11
+ };
12
+ } catch {
13
+ return {
14
+ ok: false
15
+ };
16
+ }
17
+ }
18
+ };
19
+ }
20
+ export {
21
+ parseYaml
22
+ };
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/bundle/plugins/parse-yaml/index.ts"],
4
+ "sourcesContent": ["import type { Plugin, ResolveResult } from '@/bundle/bundle'\nimport { isYaml } from '@/utils/is-yaml'\nimport YAML from 'yaml'\n\n/**\n * Creates a plugin that parses YAML strings into JavaScript objects.\n * @returns A plugin object with validate and exec functions\n * @example\n * ```ts\n * const yamlPlugin = parseYaml()\n * const result = yamlPlugin.exec('name: John\\nage: 30')\n * // result = { name: 'John', age: 30 }\n * ```\n */\nexport function parseYaml(): Plugin {\n return {\n validate: isYaml,\n exec: async (value): Promise<ResolveResult> => {\n try {\n return {\n ok: true,\n data: YAML.parse(value),\n }\n } catch {\n return {\n ok: false,\n }\n }\n },\n }\n}\n"],
5
+ "mappings": "AACA,SAAS,cAAc;AACvB,OAAO,UAAU;AAYV,SAAS,YAAoB;AAClC,SAAO;AAAA,IACL,UAAU;AAAA,IACV,MAAM,OAAO,UAAkC;AAC7C,UAAI;AACF,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,MAAM,KAAK,MAAM,KAAK;AAAA,QACxB;AAAA,MACF,QAAQ;AACN,eAAO;AAAA,UACL,IAAI;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,29 @@
1
+ import { type Plugin, type ResolveResult } from '../../../bundle/bundle.js';
2
+ /**
3
+ * Reads and normalizes data from a local file
4
+ * @param path - The file path to read from
5
+ * @returns A promise that resolves to either the normalized data or an error result
6
+ * @example
7
+ * ```ts
8
+ * const result = await readFile('./schemas/user.json')
9
+ * if (result.ok) {
10
+ * console.log(result.data) // The normalized data
11
+ * } else {
12
+ * console.log('Failed to read file')
13
+ * }
14
+ * ```
15
+ */
16
+ export declare function readFile(path: string): Promise<ResolveResult>;
17
+ /**
18
+ * Creates a plugin for handling local file references.
19
+ * This plugin validates and reads data from local filesystem paths.
20
+ *
21
+ * @returns A plugin object with validate and exec functions
22
+ * @example
23
+ * const filePlugin = readFiles()
24
+ * if (filePlugin.validate('./local-schema.json')) {
25
+ * const result = await filePlugin.exec('./local-schema.json')
26
+ * }
27
+ */
28
+ export declare function readFiles(): Plugin;
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/bundle/plugins/read-files/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,MAAM,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAE7E;;;;;;;;;;;;;GAaG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAmBnE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAKlC"}
@@ -0,0 +1,30 @@
1
+ import { normalize } from "../../../utils/normalize.js";
2
+ import { isFilePath } from "../../../bundle/bundle.js";
3
+ async function readFile(path) {
4
+ const fs = typeof window === "undefined" ? await import("node:fs/promises") : void 0;
5
+ if (fs === void 0) {
6
+ throw "Can not use readFiles plugin outside of a node environment";
7
+ }
8
+ try {
9
+ const fileContents = await fs.readFile(path, { encoding: "utf-8" });
10
+ return {
11
+ ok: true,
12
+ data: normalize(fileContents)
13
+ };
14
+ } catch {
15
+ return {
16
+ ok: false
17
+ };
18
+ }
19
+ }
20
+ function readFiles() {
21
+ return {
22
+ validate: isFilePath,
23
+ exec: readFile
24
+ };
25
+ }
26
+ export {
27
+ readFile,
28
+ readFiles
29
+ };
30
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/bundle/plugins/read-files/index.ts"],
4
+ "sourcesContent": ["import { normalize } from '@/utils/normalize'\nimport { isFilePath, type Plugin, type ResolveResult } from '@/bundle/bundle'\n\n/**\n * Reads and normalizes data from a local file\n * @param path - The file path to read from\n * @returns A promise that resolves to either the normalized data or an error result\n * @example\n * ```ts\n * const result = await readFile('./schemas/user.json')\n * if (result.ok) {\n * console.log(result.data) // The normalized data\n * } else {\n * console.log('Failed to read file')\n * }\n * ```\n */\nexport async function readFile(path: string): Promise<ResolveResult> {\n const fs = typeof window === 'undefined' ? await import('node:fs/promises') : undefined\n\n if (fs === undefined) {\n throw 'Can not use readFiles plugin outside of a node environment'\n }\n\n try {\n const fileContents = await fs.readFile(path, { encoding: 'utf-8' })\n\n return {\n ok: true,\n data: normalize(fileContents),\n }\n } catch {\n return {\n ok: false,\n }\n }\n}\n\n/**\n * Creates a plugin for handling local file references.\n * This plugin validates and reads data from local filesystem paths.\n *\n * @returns A plugin object with validate and exec functions\n * @example\n * const filePlugin = readFiles()\n * if (filePlugin.validate('./local-schema.json')) {\n * const result = await filePlugin.exec('./local-schema.json')\n * }\n */\nexport function readFiles(): Plugin {\n return {\n validate: isFilePath,\n exec: readFile,\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAC1B,SAAS,kBAAmD;AAgB5D,eAAsB,SAAS,MAAsC;AACnE,QAAM,KAAK,OAAO,WAAW,cAAc,MAAM,OAAO,kBAAkB,IAAI;AAE9E,MAAI,OAAO,QAAW;AACpB,UAAM;AAAA,EACR;AAEA,MAAI;AACF,UAAM,eAAe,MAAM,GAAG,SAAS,MAAM,EAAE,UAAU,QAAQ,CAAC;AAElE,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,MAAM,UAAU,YAAY;AAAA,IAC9B;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,MACL,IAAI;AAAA,IACN;AAAA,EACF;AACF;AAaO,SAAS,YAAoB;AAClC,SAAO;AAAA,IACL,UAAU;AAAA,IACV,MAAM;AAAA,EACR;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Generates a short SHA-1 hash from a string value.
3
+ * This function is used to create unique identifiers for external references
4
+ * while keeping the hash length manageable. It uses the Web Crypto API to
5
+ * generate a SHA-1 hash and returns the first 7 characters of the hex string.
6
+ * If the hash would be all numbers, it ensures at least one letter is included.
7
+ *
8
+ * @param value - The string to hash
9
+ * @returns A 7-character hexadecimal hash with at least one letter
10
+ * @example
11
+ * // Returns "2ae91d7"
12
+ * await getHash("https://example.com/schema.json")
13
+ */
14
+ export declare function getHash(value: string): Promise<string>;
15
+ /**
16
+ * Generates a unique compressed value for a string, handling collisions by recursively compressing
17
+ * until a unique value is found. This is used to create unique identifiers for external
18
+ * references in the bundled OpenAPI document.
19
+ *
20
+ * @param compress - Function that generates a compressed value from a string
21
+ * @param value - The original string value to compress
22
+ * @param compressedToValue - Object mapping compressed values to their original values
23
+ * @param prevCompressedValue - Optional previous compressed value to use as input for generating a new value
24
+ * @param depth - Current recursion depth to prevent infinite loops
25
+ * @returns A unique compressed value that doesn't conflict with existing values
26
+ *
27
+ * @example
28
+ * const valueMap = {}
29
+ * // First call generates compressed value for "example.com/schema.json"
30
+ * const value1 = await generateUniqueValue(compress, "example.com/schema.json", valueMap)
31
+ * // Returns something like "2ae91d7"
32
+ *
33
+ * // Second call with same value returns same compressed value
34
+ * const value2 = await generateUniqueValue(compress, "example.com/schema.json", valueMap)
35
+ * // Returns same value as value1
36
+ *
37
+ * // Call with different value generates new unique compressed value
38
+ * const value3 = await generateUniqueValue(compress, "example.com/other.json", valueMap)
39
+ * // Returns different value like "3bf82e9"
40
+ */
41
+ export declare function generateUniqueValue(compress: (value: string) => Promise<string> | string, value: string, compressedToValue: Record<string, string>, prevCompressedValue?: string, depth?: number): Promise<string>;
42
+ /**
43
+ * Factory function that creates a value generator with caching capabilities.
44
+ * The generator maintains a bidirectional mapping between original values and their compressed forms.
45
+ *
46
+ * @param compress - Function that generates a compressed value from a string
47
+ * @param compressedToValue - Initial mapping of compressed values to their original values
48
+ * @returns An object with a generate method that produces unique compressed values
49
+ *
50
+ * @example
51
+ * const compress = (value) => value.substring(0, 6) // Simple compression example
52
+ * const initialMap = { 'abc123': 'example.com/schema.json' }
53
+ * const generator = uniqueValueGeneratorFactory(compress, initialMap)
54
+ *
55
+ * // Generate compressed value for new string
56
+ * const compressed = await generator.generate('example.com/other.json')
57
+ * // Returns something like 'example'
58
+ *
59
+ * // Generate compressed value for existing string
60
+ * const cached = await generator.generate('example.com/schema.json')
61
+ * // Returns 'abc123' from cache
62
+ */
63
+ export declare const uniqueValueGeneratorFactory: (compress: (value: string) => Promise<string> | string, compressedToValue: Record<string, string>) => {
64
+ /**
65
+ * Generates a unique compressed value for the given input string.
66
+ * First checks if a compressed value already exists in the cache.
67
+ * If not, generates a new unique compressed value and stores it in the cache.
68
+ *
69
+ * @param value - The original string value to compress
70
+ * @returns A Promise that resolves to the compressed string value
71
+ *
72
+ * @example
73
+ * const generator = uniqueValueGeneratorFactory(compress, {})
74
+ * const compressed = await generator.generate('example.com/schema.json')
75
+ * // Returns a unique compressed value like 'example'
76
+ */
77
+ generate: (value: string) => Promise<string>;
78
+ };
79
+ //# sourceMappingURL=value-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"value-generator.d.ts","sourceRoot":"","sources":["../../src/bundle/value-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,mBAe1C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,EACrD,KAAK,EAAE,MAAM,EACb,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACzC,mBAAmB,CAAC,EAAE,MAAM,EAC5B,KAAK,SAAI,mBAoBV;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,2BAA2B,aAC5B,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,qBAClC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAMvC;;;;;;;;;;;;OAYG;sBACqB,MAAM;CAqBjC,CAAA"}
@@ -0,0 +1,55 @@
1
+ async function getHash(value) {
2
+ const encoder = new TextEncoder();
3
+ const data = encoder.encode(value);
4
+ const hashBuffer = await crypto.subtle.digest("SHA-1", data);
5
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
6
+ const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
7
+ const hash = hashHex.substring(0, 7);
8
+ return hash.match(/^\d+$/) ? "a" + hash.substring(1) : hash;
9
+ }
10
+ async function generateUniqueValue(compress, value, compressedToValue, prevCompressedValue, depth = 0) {
11
+ const MAX_DEPTH = 100;
12
+ if (depth >= MAX_DEPTH) {
13
+ throw "Can not generate unique compressed values";
14
+ }
15
+ const compressedValue = await compress(prevCompressedValue ?? value);
16
+ if (compressedToValue[compressedValue] !== void 0 && compressedToValue[compressedValue] !== value) {
17
+ return generateUniqueValue(compress, value, compressedToValue, compressedValue, depth + 1);
18
+ }
19
+ compressedToValue[compressedValue] = value;
20
+ return compressedValue;
21
+ }
22
+ const uniqueValueGeneratorFactory = (compress, compressedToValue) => {
23
+ const valueToCompressed = Object.fromEntries(Object.entries(compressedToValue).map(([key, value]) => [value, key]));
24
+ return {
25
+ /**
26
+ * Generates a unique compressed value for the given input string.
27
+ * First checks if a compressed value already exists in the cache.
28
+ * If not, generates a new unique compressed value and stores it in the cache.
29
+ *
30
+ * @param value - The original string value to compress
31
+ * @returns A Promise that resolves to the compressed string value
32
+ *
33
+ * @example
34
+ * const generator = uniqueValueGeneratorFactory(compress, {})
35
+ * const compressed = await generator.generate('example.com/schema.json')
36
+ * // Returns a unique compressed value like 'example'
37
+ */
38
+ generate: async (value) => {
39
+ const cache = valueToCompressed[value];
40
+ if (cache) {
41
+ return cache;
42
+ }
43
+ const generatedValue = await generateUniqueValue(compress, value, compressedToValue);
44
+ const compressedValue = generatedValue.match(/^\d+$/) ? `a${generatedValue}` : generatedValue;
45
+ valueToCompressed[value] = compressedValue;
46
+ return compressedValue;
47
+ }
48
+ };
49
+ };
50
+ export {
51
+ generateUniqueValue,
52
+ getHash,
53
+ uniqueValueGeneratorFactory
54
+ };
55
+ //# sourceMappingURL=value-generator.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/bundle/value-generator.ts"],
4
+ "sourcesContent": ["/**\n * Generates a short SHA-1 hash from a string value.\n * This function is used to create unique identifiers for external references\n * while keeping the hash length manageable. It uses the Web Crypto API to\n * generate a SHA-1 hash and returns the first 7 characters of the hex string.\n * If the hash would be all numbers, it ensures at least one letter is included.\n *\n * @param value - The string to hash\n * @returns A 7-character hexadecimal hash with at least one letter\n * @example\n * // Returns \"2ae91d7\"\n * await getHash(\"https://example.com/schema.json\")\n */\nexport async function getHash(value: string) {\n // Convert string to ArrayBuffer\n const encoder = new TextEncoder()\n const data = encoder.encode(value)\n\n // Hash the data\n const hashBuffer = await crypto.subtle.digest('SHA-1', data)\n\n // Convert buffer to hex string\n const hashArray = Array.from(new Uint8Array(hashBuffer))\n const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')\n\n // Return first 7 characters of the hash, ensuring at least one letter\n const hash = hashHex.substring(0, 7)\n return hash.match(/^\\d+$/) ? 'a' + hash.substring(1) : hash\n}\n\n/**\n * Generates a unique compressed value for a string, handling collisions by recursively compressing\n * until a unique value is found. This is used to create unique identifiers for external\n * references in the bundled OpenAPI document.\n *\n * @param compress - Function that generates a compressed value from a string\n * @param value - The original string value to compress\n * @param compressedToValue - Object mapping compressed values to their original values\n * @param prevCompressedValue - Optional previous compressed value to use as input for generating a new value\n * @param depth - Current recursion depth to prevent infinite loops\n * @returns A unique compressed value that doesn't conflict with existing values\n *\n * @example\n * const valueMap = {}\n * // First call generates compressed value for \"example.com/schema.json\"\n * const value1 = await generateUniqueValue(compress, \"example.com/schema.json\", valueMap)\n * // Returns something like \"2ae91d7\"\n *\n * // Second call with same value returns same compressed value\n * const value2 = await generateUniqueValue(compress, \"example.com/schema.json\", valueMap)\n * // Returns same value as value1\n *\n * // Call with different value generates new unique compressed value\n * const value3 = await generateUniqueValue(compress, \"example.com/other.json\", valueMap)\n * // Returns different value like \"3bf82e9\"\n */\nexport async function generateUniqueValue(\n compress: (value: string) => Promise<string> | string,\n value: string,\n compressedToValue: Record<string, string>,\n prevCompressedValue?: string,\n depth = 0,\n) {\n // Prevent infinite recursion by limiting depth\n const MAX_DEPTH = 100\n\n if (depth >= MAX_DEPTH) {\n throw 'Can not generate unique compressed values'\n }\n\n // Compress the value, using previous compressed value if provided\n const compressedValue = await compress(prevCompressedValue ?? value)\n\n // Handle collision by recursively trying with compressed value as input\n if (compressedToValue[compressedValue] !== undefined && compressedToValue[compressedValue] !== value) {\n return generateUniqueValue(compress, value, compressedToValue, compressedValue, depth + 1)\n }\n\n // Store mapping and return unique compressed value\n compressedToValue[compressedValue] = value\n return compressedValue\n}\n\n/**\n * Factory function that creates a value generator with caching capabilities.\n * The generator maintains a bidirectional mapping between original values and their compressed forms.\n *\n * @param compress - Function that generates a compressed value from a string\n * @param compressedToValue - Initial mapping of compressed values to their original values\n * @returns An object with a generate method that produces unique compressed values\n *\n * @example\n * const compress = (value) => value.substring(0, 6) // Simple compression example\n * const initialMap = { 'abc123': 'example.com/schema.json' }\n * const generator = uniqueValueGeneratorFactory(compress, initialMap)\n *\n * // Generate compressed value for new string\n * const compressed = await generator.generate('example.com/other.json')\n * // Returns something like 'example'\n *\n * // Generate compressed value for existing string\n * const cached = await generator.generate('example.com/schema.json')\n * // Returns 'abc123' from cache\n */\nexport const uniqueValueGeneratorFactory = (\n compress: (value: string) => Promise<string> | string,\n compressedToValue: Record<string, string>,\n) => {\n // Create a reverse mapping from original values to their compressed forms\n const valueToCompressed = Object.fromEntries(Object.entries(compressedToValue).map(([key, value]) => [value, key]))\n\n return {\n /**\n * Generates a unique compressed value for the given input string.\n * First checks if a compressed value already exists in the cache.\n * If not, generates a new unique compressed value and stores it in the cache.\n *\n * @param value - The original string value to compress\n * @returns A Promise that resolves to the compressed string value\n *\n * @example\n * const generator = uniqueValueGeneratorFactory(compress, {})\n * const compressed = await generator.generate('example.com/schema.json')\n * // Returns a unique compressed value like 'example'\n */\n generate: async (value: string) => {\n // Check if we already have a compressed value for this input\n const cache = valueToCompressed[value]\n if (cache) {\n return cache\n }\n\n // Generate a new unique compressed value\n const generatedValue = await generateUniqueValue(compress, value, compressedToValue)\n\n // Ensure the generated string contains at least one non-numeric character\n // This prevents the `setValueAtPath` function from interpreting the value as an array index\n // by forcing it to be treated as an object property name\n const compressedValue = generatedValue.match(/^\\d+$/) ? `a${generatedValue}` : generatedValue\n\n // Store the new mapping in our cache\n valueToCompressed[value] = compressedValue\n\n return compressedValue\n },\n }\n}\n"],
5
+ "mappings": "AAaA,eAAsB,QAAQ,OAAe;AAE3C,QAAM,UAAU,IAAI,YAAY;AAChC,QAAM,OAAO,QAAQ,OAAO,KAAK;AAGjC,QAAM,aAAa,MAAM,OAAO,OAAO,OAAO,SAAS,IAAI;AAG3D,QAAM,YAAY,MAAM,KAAK,IAAI,WAAW,UAAU,CAAC;AACvD,QAAM,UAAU,UAAU,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAG7E,QAAM,OAAO,QAAQ,UAAU,GAAG,CAAC;AACnC,SAAO,KAAK,MAAM,OAAO,IAAI,MAAM,KAAK,UAAU,CAAC,IAAI;AACzD;AA4BA,eAAsB,oBACpB,UACA,OACA,mBACA,qBACA,QAAQ,GACR;AAEA,QAAM,YAAY;AAElB,MAAI,SAAS,WAAW;AACtB,UAAM;AAAA,EACR;AAGA,QAAM,kBAAkB,MAAM,SAAS,uBAAuB,KAAK;AAGnE,MAAI,kBAAkB,eAAe,MAAM,UAAa,kBAAkB,eAAe,MAAM,OAAO;AACpG,WAAO,oBAAoB,UAAU,OAAO,mBAAmB,iBAAiB,QAAQ,CAAC;AAAA,EAC3F;AAGA,oBAAkB,eAAe,IAAI;AACrC,SAAO;AACT;AAuBO,MAAM,8BAA8B,CACzC,UACA,sBACG;AAEH,QAAM,oBAAoB,OAAO,YAAY,OAAO,QAAQ,iBAAiB,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;AAElH,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAcL,UAAU,OAAO,UAAkB;AAEjC,YAAM,QAAQ,kBAAkB,KAAK;AACrC,UAAI,OAAO;AACT,eAAO;AAAA,MACT;AAGA,YAAM,iBAAiB,MAAM,oBAAoB,UAAU,OAAO,iBAAiB;AAKnF,YAAM,kBAAkB,eAAe,MAAM,OAAO,IAAI,IAAI,cAAc,KAAK;AAG/E,wBAAkB,KAAK,IAAI;AAE3B,aAAO;AAAA,IACT;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,45 @@
1
+ import type { UnknownObject } from '../types.js';
2
+ type DereferenceResult = {
3
+ success: true;
4
+ data: UnknownObject;
5
+ } | {
6
+ success: false;
7
+ errors: string[];
8
+ };
9
+ type ReturnDereferenceResult<Opt extends {
10
+ sync?: boolean;
11
+ }> = Opt['sync'] extends true ? DereferenceResult : Promise<DereferenceResult>;
12
+ /**
13
+ * Dereferences a JSON object, resolving all $ref pointers.
14
+ *
15
+ * This function can operate synchronously (no remote refs, no async plugins) or asynchronously (with remote refs).
16
+ * If `options.sync` is true, it simply wraps the input in a magic proxy and returns it.
17
+ * Otherwise, it bundles the document, resolving all $refs (including remote ones), and returns a promise.
18
+ *
19
+ * @param input - JSON Schema object to dereference.
20
+ * @param options - Optional settings. If `sync` is true, dereferencing is synchronous.
21
+ * @returns A DereferenceResult (or Promise thereof) indicating success and the dereferenced data, or errors.
22
+ *
23
+ * @example
24
+ * // Synchronous dereference (no remote refs)
25
+ * const result = dereference({ openapi: '3.0.0', info: { title: 'My API', version: '1.0.0' } }, { sync: true });
26
+ * if (result.success) {
27
+ * console.log(result.data); // Magic proxy-wrapped document
28
+ * }
29
+ *
30
+ * @example
31
+ * // Asynchronous dereference (with remote refs)
32
+ * dereference({ $ref: 'https://example.com/api.yaml' })
33
+ * .then(result => {
34
+ * if (result.success) {
35
+ * console.log(result.data); // Fully dereferenced document
36
+ * } else {
37
+ * console.error(result.errors);
38
+ * }
39
+ * });
40
+ */
41
+ export declare const dereference: <Opts extends {
42
+ sync?: boolean;
43
+ }>(input: UnknownObject, options?: Opts) => ReturnDereferenceResult<Opts>;
44
+ export {};
45
+ //# sourceMappingURL=dereference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dereference.d.ts","sourceRoot":"","sources":["../../src/dereference/dereference.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE5C,KAAK,iBAAiB,GAClB;IACE,OAAO,EAAE,IAAI,CAAA;IACb,IAAI,EAAE,aAAa,CAAA;CACpB,GACD;IACE,OAAO,EAAE,KAAK,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB,CAAA;AAEL,KAAK,uBAAuB,CAAC,GAAG,SAAS;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,IAAI,GACnF,iBAAiB,GACjB,OAAO,CAAC,iBAAiB,CAAC,CAAA;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,WAAW,GAAI,IAAI,SAAS;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,SAClD,aAAa,YACV,IAAI,KACb,uBAAuB,CAAC,IAAI,CAgC9B,CAAA"}
@@ -0,0 +1,37 @@
1
+ import { bundle } from "../bundle/index.js";
2
+ import { fetchUrls } from "../bundle/plugins/fetch-urls/index.js";
3
+ import { createMagicProxy } from "../magic-proxy/index.js";
4
+ const dereference = (input, options) => {
5
+ if (options?.sync) {
6
+ return {
7
+ success: true,
8
+ data: createMagicProxy(input)
9
+ };
10
+ }
11
+ const errors = [];
12
+ return bundle(input, {
13
+ plugins: [fetchUrls()],
14
+ treeShake: false,
15
+ urlMap: true,
16
+ hooks: {
17
+ onResolveError(node) {
18
+ errors.push(`Failed to resolve ${node.$ref}`);
19
+ }
20
+ }
21
+ }).then((result) => {
22
+ if (errors.length > 0) {
23
+ return {
24
+ success: false,
25
+ errors
26
+ };
27
+ }
28
+ return {
29
+ success: true,
30
+ data: createMagicProxy(result)
31
+ };
32
+ });
33
+ };
34
+ export {
35
+ dereference
36
+ };
37
+ //# sourceMappingURL=dereference.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/dereference/dereference.ts"],
4
+ "sourcesContent": ["import { bundle } from '@/bundle'\nimport { fetchUrls } from '@/bundle/plugins/fetch-urls'\nimport { createMagicProxy } from '@/magic-proxy'\nimport type { UnknownObject } from '@/types'\n\ntype DereferenceResult =\n | {\n success: true\n data: UnknownObject\n }\n | {\n success: false\n errors: string[]\n }\n\ntype ReturnDereferenceResult<Opt extends { sync?: boolean }> = Opt['sync'] extends true\n ? DereferenceResult\n : Promise<DereferenceResult>\n\n/**\n * Dereferences a JSON object, resolving all $ref pointers.\n *\n * This function can operate synchronously (no remote refs, no async plugins) or asynchronously (with remote refs).\n * If `options.sync` is true, it simply wraps the input in a magic proxy and returns it.\n * Otherwise, it bundles the document, resolving all $refs (including remote ones), and returns a promise.\n *\n * @param input - JSON Schema object to dereference.\n * @param options - Optional settings. If `sync` is true, dereferencing is synchronous.\n * @returns A DereferenceResult (or Promise thereof) indicating success and the dereferenced data, or errors.\n *\n * @example\n * // Synchronous dereference (no remote refs)\n * const result = dereference({ openapi: '3.0.0', info: { title: 'My API', version: '1.0.0' } }, { sync: true });\n * if (result.success) {\n * console.log(result.data); // Magic proxy-wrapped document\n * }\n *\n * @example\n * // Asynchronous dereference (with remote refs)\n * dereference({ $ref: 'https://example.com/api.yaml' })\n * .then(result => {\n * if (result.success) {\n * console.log(result.data); // Fully dereferenced document\n * } else {\n * console.error(result.errors);\n * }\n * });\n */\nexport const dereference = <Opts extends { sync?: boolean }>(\n input: UnknownObject,\n options?: Opts,\n): ReturnDereferenceResult<Opts> => {\n if (options?.sync) {\n return {\n success: true,\n data: createMagicProxy(input),\n } as ReturnDereferenceResult<Opts>\n }\n\n const errors: string[] = []\n\n return bundle(input, {\n plugins: [fetchUrls()],\n treeShake: false,\n urlMap: true,\n hooks: {\n onResolveError(node) {\n errors.push(`Failed to resolve ${node.$ref}`)\n },\n },\n }).then((result) => {\n if (errors.length > 0) {\n return {\n success: false,\n errors,\n }\n }\n\n return {\n success: true,\n data: createMagicProxy(result as UnknownObject),\n }\n }) as ReturnDereferenceResult<Opts>\n}\n"],
5
+ "mappings": "AAAA,SAAS,cAAc;AACvB,SAAS,iBAAiB;AAC1B,SAAS,wBAAwB;AA8C1B,MAAM,cAAc,CACzB,OACA,YACkC;AAClC,MAAI,SAAS,MAAM;AACjB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM,iBAAiB,KAAK;AAAA,IAC9B;AAAA,EACF;AAEA,QAAM,SAAmB,CAAC;AAE1B,SAAO,OAAO,OAAO;AAAA,IACnB,SAAS,CAAC,UAAU,CAAC;AAAA,IACrB,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,OAAO;AAAA,MACL,eAAe,MAAM;AACnB,eAAO,KAAK,qBAAqB,KAAK,IAAI,EAAE;AAAA,MAC9C;AAAA,IACF;AAAA,EACF,CAAC,EAAE,KAAK,CAAC,WAAW;AAClB,QAAI,OAAO,SAAS,GAAG;AACrB,aAAO;AAAA,QACL,SAAS;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM,iBAAiB,MAAuB;AAAA,IAChD;AAAA,EACF,CAAC;AACH;",
6
+ "names": []
7
+ }
@@ -0,0 +1,2 @@
1
+ export { dereference } from './dereference.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dereference/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1,5 @@
1
+ import { dereference } from "./dereference.js";
2
+ export {
3
+ dereference
4
+ };
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/dereference/index.ts"],
4
+ "sourcesContent": ["// biome-ignore lint/performance/noBarrelFile: <explanation>\nexport { dereference } from './dereference'\n"],
5
+ "mappings": "AACA,SAAS,mBAAmB;",
6
+ "names": []
7
+ }
@@ -0,0 +1,35 @@
1
+ import type { Difference } from '../diff/diff.js';
2
+ export declare class InvalidChangesDetectedError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ /**
6
+ * Applies a set of differences to a document object.
7
+ * The function traverses the document structure following the paths specified in the differences
8
+ * and applies the corresponding changes (add, update, or delete) at each location.
9
+ *
10
+ * @param document - The original document to apply changes to
11
+ * @param diff - Array of differences to apply, each containing a path and change type
12
+ * @returns The modified document with all changes applied
13
+ *
14
+ * @example
15
+ * const original = {
16
+ * paths: {
17
+ * '/users': {
18
+ * get: { responses: { '200': { description: 'OK' } } }
19
+ * }
20
+ * }
21
+ * }
22
+ *
23
+ * const changes = [
24
+ * {
25
+ * path: ['paths', '/users', 'get', 'responses', '200', 'content'],
26
+ * type: 'add',
27
+ * changes: { 'application/json': { schema: { type: 'object' } } }
28
+ * }
29
+ * ]
30
+ *
31
+ * const updated = apply(original, changes)
32
+ * // Result: original document with content added to the 200 response
33
+ */
34
+ export declare const apply: (document: Record<string, unknown>, diff: Difference[]) => Record<string, unknown>;
35
+ //# sourceMappingURL=apply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/diff/apply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE7C,qBAAa,2BAA4B,SAAQ,KAAK;gBACxC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,KAAK,aAAc,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,UAAU,EAAE,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAuCnG,CAAA"}
@@ -0,0 +1,40 @@
1
+ class InvalidChangesDetectedError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = "InvalidChangesDetectedError";
5
+ }
6
+ }
7
+ const apply = (document, diff) => {
8
+ const applyChange = (current, path, d, depth = 0) => {
9
+ if (path[depth] === void 0) {
10
+ throw new InvalidChangesDetectedError(
11
+ `Process aborted. Path ${path.join(".")} at depth ${depth} is undefined, check diff object`
12
+ );
13
+ }
14
+ if (depth >= path.length - 1) {
15
+ if (d.type === "add" || d.type === "update") {
16
+ current[path[depth]] = d.changes;
17
+ } else {
18
+ if (Array.isArray(current)) {
19
+ current.splice(Number.parseInt(path[depth]), 1);
20
+ } else {
21
+ delete current[path[depth]];
22
+ }
23
+ }
24
+ return;
25
+ }
26
+ if (current[path[depth]] === void 0 || typeof current[path[depth]] !== "object") {
27
+ throw new InvalidChangesDetectedError("Process aborted, check diff object");
28
+ }
29
+ applyChange(current[path[depth]], path, d, depth + 1);
30
+ };
31
+ for (const d of diff) {
32
+ applyChange(document, d.path, d);
33
+ }
34
+ return document;
35
+ };
36
+ export {
37
+ InvalidChangesDetectedError,
38
+ apply
39
+ };
40
+ //# sourceMappingURL=apply.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/diff/apply.ts"],
4
+ "sourcesContent": ["import type { Difference } from '@/diff/diff'\n\nexport class InvalidChangesDetectedError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'InvalidChangesDetectedError'\n }\n}\n\n/**\n * Applies a set of differences to a document object.\n * The function traverses the document structure following the paths specified in the differences\n * and applies the corresponding changes (add, update, or delete) at each location.\n *\n * @param document - The original document to apply changes to\n * @param diff - Array of differences to apply, each containing a path and change type\n * @returns The modified document with all changes applied\n *\n * @example\n * const original = {\n * paths: {\n * '/users': {\n * get: { responses: { '200': { description: 'OK' } } }\n * }\n * }\n * }\n *\n * const changes = [\n * {\n * path: ['paths', '/users', 'get', 'responses', '200', 'content'],\n * type: 'add',\n * changes: { 'application/json': { schema: { type: 'object' } } }\n * }\n * ]\n *\n * const updated = apply(original, changes)\n * // Result: original document with content added to the 200 response\n */\nexport const apply = (document: Record<string, unknown>, diff: Difference[]): Record<string, unknown> => {\n // Traverse the object and apply the change\n const applyChange = (current: any, path: string[], d: Difference, depth = 0) => {\n if (path[depth] === undefined) {\n throw new InvalidChangesDetectedError(\n `Process aborted. Path ${path.join('.')} at depth ${depth} is undefined, check diff object`,\n )\n }\n\n // We reach where we want to be, now we can apply changes\n if (depth >= path.length - 1) {\n if (d.type === 'add' || d.type === 'update') {\n current[path[depth]] = d.changes\n } else {\n // For arrays we don't use delete operator since it will leave blank spots and not actually remove the element\n if (Array.isArray(current)) {\n current.splice(Number.parseInt(path[depth]), 1)\n } else {\n delete current[path[depth]]\n }\n }\n return\n }\n\n // Throw an error\n // This scenario should not happen\n // 1- if we are adding a new entry, the diff should only give us the higher level diff\n // 2- if we are updating/deleting an entry, the path to that entry should exists\n if (current[path[depth]] === undefined || typeof current[path[depth]] !== 'object') {\n throw new InvalidChangesDetectedError('Process aborted, check diff object')\n }\n applyChange(current[path[depth]], path, d, depth + 1)\n }\n\n for (const d of diff) {\n applyChange(document, d.path, d)\n }\n\n return document\n}\n"],
5
+ "mappings": "AAEO,MAAM,oCAAoC,MAAM;AAAA,EACrD,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AA+BO,MAAM,QAAQ,CAAC,UAAmC,SAAgD;AAEvG,QAAM,cAAc,CAAC,SAAc,MAAgB,GAAe,QAAQ,MAAM;AAC9E,QAAI,KAAK,KAAK,MAAM,QAAW;AAC7B,YAAM,IAAI;AAAA,QACR,yBAAyB,KAAK,KAAK,GAAG,CAAC,aAAa,KAAK;AAAA,MAC3D;AAAA,IACF;AAGA,QAAI,SAAS,KAAK,SAAS,GAAG;AAC5B,UAAI,EAAE,SAAS,SAAS,EAAE,SAAS,UAAU;AAC3C,gBAAQ,KAAK,KAAK,CAAC,IAAI,EAAE;AAAA,MAC3B,OAAO;AAEL,YAAI,MAAM,QAAQ,OAAO,GAAG;AAC1B,kBAAQ,OAAO,OAAO,SAAS,KAAK,KAAK,CAAC,GAAG,CAAC;AAAA,QAChD,OAAO;AACL,iBAAO,QAAQ,KAAK,KAAK,CAAC;AAAA,QAC5B;AAAA,MACF;AACA;AAAA,IACF;AAMA,QAAI,QAAQ,KAAK,KAAK,CAAC,MAAM,UAAa,OAAO,QAAQ,KAAK,KAAK,CAAC,MAAM,UAAU;AAClF,YAAM,IAAI,4BAA4B,oCAAoC;AAAA,IAC5E;AACA,gBAAY,QAAQ,KAAK,KAAK,CAAC,GAAG,MAAM,GAAG,QAAQ,CAAC;AAAA,EACtD;AAEA,aAAW,KAAK,MAAM;AACpB,gBAAY,UAAU,EAAE,MAAM,CAAC;AAAA,EACjC;AAEA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Represents the possible types of changes that can be made to a document.
3
+ * - 'add': A new property is added
4
+ * - 'update': An existing property's value is changed
5
+ * - 'delete': A property is removed
6
+ */
7
+ type ChangeType = 'add' | 'update' | 'delete';
8
+ /**
9
+ * Represents a single difference between two documents.
10
+ * @property path - Array of strings representing the path to the changed property
11
+ * @property changes - The new value for the property (for add/update) or the old value (for delete)
12
+ * @property type - The type of change that occurred
13
+ */
14
+ export type Difference = {
15
+ path: string[];
16
+ changes: any;
17
+ type: ChangeType;
18
+ };
19
+ /**
20
+ * Get the difference between two objects.
21
+ *
22
+ * This function performs a breadth-first comparison between two objects and returns
23
+ * a list of operations needed to transform the first object into the second.
24
+ *
25
+ * @param doc1 - The source object to compare from
26
+ * @param doc2 - The target object to compare to
27
+ * @returns A list of operations (add/update/delete) with their paths and changes
28
+ *
29
+ * @example
30
+ * // Compare two simple objects
31
+ * const original = { name: 'John', age: 30 }
32
+ * const updated = { name: 'John', age: 31, city: 'New York' }
33
+ * const differences = diff(original, updated)
34
+ * // Returns:
35
+ * // [
36
+ * // { path: ['age'], changes: 31, type: 'update' },
37
+ * // { path: ['city'], changes: 'New York', type: 'add' }
38
+ * // ]
39
+ *
40
+ * @example
41
+ * // Compare nested objects
42
+ * const original = {
43
+ * user: { name: 'John', settings: { theme: 'light' } }
44
+ * }
45
+ * const updated = {
46
+ * user: { name: 'John', settings: { theme: 'dark' } }
47
+ * }
48
+ * const differences = diff(original, updated)
49
+ * // Returns:
50
+ * // [
51
+ * // { path: ['user', 'settings', 'theme'], changes: 'dark', type: 'update' }
52
+ * // ]
53
+ */
54
+ export declare const diff: (doc1: Record<string, unknown>, doc2: Record<string, unknown>) => Difference[];
55
+ export {};
56
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/diff/diff.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,KAAK,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE7C;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAA;AAE3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,IAAI,SAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,UAAU,EA0C7F,CAAA"}
@@ -0,0 +1,33 @@
1
+ const diff = (doc1, doc2) => {
2
+ const diff2 = [];
3
+ const bfs = (el1, el2, prefix = []) => {
4
+ if (typeof el1 !== typeof el2) {
5
+ if (typeof el1 === "undefined") {
6
+ diff2.push({ path: prefix, changes: el2, type: "add" });
7
+ return;
8
+ }
9
+ if (typeof el2 === "undefined") {
10
+ diff2.push({ path: prefix, changes: el1, type: "delete" });
11
+ return;
12
+ }
13
+ diff2.push({ path: prefix, changes: el2, type: "update" });
14
+ return;
15
+ }
16
+ if (typeof el1 === "object" && typeof el2 === "object" && el1 !== null && el2 !== null) {
17
+ const keys = /* @__PURE__ */ new Set([...Object.keys(el1), ...Object.keys(el2)]);
18
+ for (const key of keys) {
19
+ bfs(el1[key], el2[key], [...prefix, key]);
20
+ }
21
+ return;
22
+ }
23
+ if (el1 !== el2) {
24
+ diff2.push({ path: prefix, changes: el2, type: "update" });
25
+ }
26
+ };
27
+ bfs(doc1, doc2);
28
+ return diff2;
29
+ };
30
+ export {
31
+ diff
32
+ };
33
+ //# sourceMappingURL=diff.js.map