@scalar/json-magic 0.1.0 → 0.3.1

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 (57) hide show
  1. package/.turbo/turbo-build.log +4 -3
  2. package/CHANGELOG.md +28 -0
  3. package/README.md +21 -3
  4. package/dist/bundle/bundle.d.ts +84 -14
  5. package/dist/bundle/bundle.d.ts.map +1 -1
  6. package/dist/bundle/bundle.js +60 -15
  7. package/dist/bundle/bundle.js.map +3 -3
  8. package/dist/bundle/index.d.ts +2 -1
  9. package/dist/bundle/index.d.ts.map +1 -1
  10. package/dist/bundle/index.js.map +2 -2
  11. package/dist/bundle/plugins/fetch-urls/index.d.ts +2 -2
  12. package/dist/bundle/plugins/fetch-urls/index.d.ts.map +1 -1
  13. package/dist/bundle/plugins/fetch-urls/index.js +1 -0
  14. package/dist/bundle/plugins/fetch-urls/index.js.map +2 -2
  15. package/dist/bundle/plugins/parse-json/index.d.ts +2 -2
  16. package/dist/bundle/plugins/parse-json/index.d.ts.map +1 -1
  17. package/dist/bundle/plugins/parse-json/index.js +1 -0
  18. package/dist/bundle/plugins/parse-json/index.js.map +2 -2
  19. package/dist/bundle/plugins/parse-yaml/index.d.ts +2 -2
  20. package/dist/bundle/plugins/parse-yaml/index.d.ts.map +1 -1
  21. package/dist/bundle/plugins/parse-yaml/index.js +1 -0
  22. package/dist/bundle/plugins/parse-yaml/index.js.map +2 -2
  23. package/dist/bundle/plugins/read-files/index.d.ts +2 -2
  24. package/dist/bundle/plugins/read-files/index.d.ts.map +1 -1
  25. package/dist/bundle/plugins/read-files/index.js +1 -0
  26. package/dist/bundle/plugins/read-files/index.js.map +2 -2
  27. package/dist/diff/apply.d.ts +1 -1
  28. package/dist/diff/apply.d.ts.map +1 -1
  29. package/dist/diff/apply.js.map +2 -2
  30. package/dist/diff/diff.d.ts +2 -2
  31. package/dist/diff/diff.d.ts.map +1 -1
  32. package/dist/diff/diff.js.map +2 -2
  33. package/dist/diff/merge.d.ts +3 -3
  34. package/dist/diff/merge.d.ts.map +1 -1
  35. package/dist/diff/merge.js.map +2 -2
  36. package/dist/magic-proxy/proxy.d.ts +23 -42
  37. package/dist/magic-proxy/proxy.d.ts.map +1 -1
  38. package/dist/magic-proxy/proxy.js +103 -80
  39. package/dist/magic-proxy/proxy.js.map +3 -3
  40. package/dist/utils/is-object.d.ts +1 -1
  41. package/dist/utils/is-object.d.ts.map +1 -1
  42. package/dist/utils/is-object.js.map +2 -2
  43. package/package.json +11 -10
  44. package/src/bundle/bundle.test.ts +591 -47
  45. package/src/bundle/bundle.ts +173 -32
  46. package/src/bundle/index.ts +2 -1
  47. package/src/bundle/plugins/fetch-urls/index.ts +3 -2
  48. package/src/bundle/plugins/parse-json/index.ts +3 -2
  49. package/src/bundle/plugins/parse-yaml/index.ts +3 -2
  50. package/src/bundle/plugins/read-files/index.ts +4 -2
  51. package/src/dereference/dereference.test.ts +26 -18
  52. package/src/diff/apply.ts +8 -3
  53. package/src/diff/diff.ts +3 -3
  54. package/src/diff/merge.ts +6 -6
  55. package/src/magic-proxy/proxy.test.ts +1095 -100
  56. package/src/magic-proxy/proxy.ts +150 -171
  57. package/src/utils/is-object.ts +1 -1
@@ -37,6 +37,7 @@ async function fetchUrl(url, limiter, config) {
37
37
  function fetchUrls(config) {
38
38
  const limiter = config?.limit ? createLimiter(config.limit) : (fn) => fn();
39
39
  return {
40
+ type: "loader",
40
41
  validate: isRemoteUrl,
41
42
  exec: (value) => fetchUrl(value, limiter, config)
42
43
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/bundle/plugins/fetch-urls/index.ts"],
4
- "sourcesContent": ["import { normalize } from '@/utils/normalize'\nimport { createLimiter } from '@/bundle/create-limiter'\nimport type { Plugin, ResolveResult } from '@/bundle'\nimport { isRemoteUrl } from '@/bundle/bundle'\n\ntype FetchConfig = Partial<{\n headers: { headers: HeadersInit; domains: string[] }[]\n fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response>\n}>\n\n/**\n * Safely checks for host from a URL\n * Needed because we cannot create a URL from a relative remote URL ex: examples/openapi.json\n */\nconst getHost = (url: string): string | null => {\n try {\n return new URL(url).host\n } catch {\n return null\n }\n}\n\n/**\n * Fetches and normalizes data from a remote URL\n * @param url - The URL to fetch data from\n * @returns A promise that resolves to either the normalized data or an error result\n * @example\n * ```ts\n * const result = await fetchUrl('https://api.example.com/data.json')\n * if (result.ok) {\n * console.log(result.data) // The normalized data\n * } else {\n * console.log('Failed to fetch data')\n * }\n * ```\n */\nexport async function fetchUrl(\n url: string,\n limiter: <T>(fn: () => Promise<T>) => Promise<T>,\n config?: FetchConfig,\n): Promise<ResolveResult> {\n try {\n const host = getHost(url)\n\n // Get the headers that match the domain\n const headers = config?.headers?.find((a) => a.domains.find((d) => d === host) !== undefined)?.headers\n\n const exec = config?.fetch ?? fetch\n\n const result = await limiter(() =>\n exec(url, {\n headers,\n }),\n )\n\n if (result.ok) {\n const body = await result.text()\n\n return {\n ok: true,\n data: normalize(body),\n }\n }\n\n return {\n ok: false,\n }\n } catch {\n return {\n ok: false,\n }\n }\n}\n\n/**\n * Creates a plugin for handling remote URL references.\n * This plugin validates and fetches data from HTTP/HTTPS URLs.\n *\n * @returns A plugin object with validate and exec functions\n * @example\n * const urlPlugin = fetchUrls()\n * if (urlPlugin.validate('https://example.com/schema.json')) {\n * const result = await urlPlugin.exec('https://example.com/schema.json')\n * }\n */\nexport function fetchUrls(config?: FetchConfig & Partial<{ limit: number | null }>): Plugin {\n // If there is a limit specified we limit the number of concurrent calls\n const limiter = config?.limit ? createLimiter(config.limit) : <T>(fn: () => Promise<T>) => fn()\n\n return {\n validate: isRemoteUrl,\n exec: (value) => fetchUrl(value, limiter, config),\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,iBAAiB;AAC1B,SAAS,qBAAqB;AAE9B,SAAS,mBAAmB;AAW5B,MAAM,UAAU,CAAC,QAA+B;AAC9C,MAAI;AACF,WAAO,IAAI,IAAI,GAAG,EAAE;AAAA,EACtB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAgBA,eAAsB,SACpB,KACA,SACA,QACwB;AACxB,MAAI;AACF,UAAM,OAAO,QAAQ,GAAG;AAGxB,UAAM,UAAU,QAAQ,SAAS,KAAK,CAAC,MAAM,EAAE,QAAQ,KAAK,CAAC,MAAM,MAAM,IAAI,MAAM,MAAS,GAAG;AAE/F,UAAM,OAAO,QAAQ,SAAS;AAE9B,UAAM,SAAS,MAAM;AAAA,MAAQ,MAC3B,KAAK,KAAK;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH;AAEA,QAAI,OAAO,IAAI;AACb,YAAM,OAAO,MAAM,OAAO,KAAK;AAE/B,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM,UAAU,IAAI;AAAA,MACtB;AAAA,IACF;AAEA,WAAO;AAAA,MACL,IAAI;AAAA,IACN;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,MACL,IAAI;AAAA,IACN;AAAA,EACF;AACF;AAaO,SAAS,UAAU,QAAkE;AAE1F,QAAM,UAAU,QAAQ,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAI,OAAyB,GAAG;AAE9F,SAAO;AAAA,IACL,UAAU;AAAA,IACV,MAAM,CAAC,UAAU,SAAS,OAAO,SAAS,MAAM;AAAA,EAClD;AACF;",
4
+ "sourcesContent": ["import { normalize } from '@/utils/normalize'\nimport { createLimiter } from '@/bundle/create-limiter'\nimport type { LoaderPlugin, ResolveResult } from '@/bundle'\nimport { isRemoteUrl } from '@/bundle/bundle'\n\ntype FetchConfig = Partial<{\n headers: { headers: HeadersInit; domains: string[] }[]\n fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response>\n}>\n\n/**\n * Safely checks for host from a URL\n * Needed because we cannot create a URL from a relative remote URL ex: examples/openapi.json\n */\nconst getHost = (url: string): string | null => {\n try {\n return new URL(url).host\n } catch {\n return null\n }\n}\n\n/**\n * Fetches and normalizes data from a remote URL\n * @param url - The URL to fetch data from\n * @returns A promise that resolves to either the normalized data or an error result\n * @example\n * ```ts\n * const result = await fetchUrl('https://api.example.com/data.json')\n * if (result.ok) {\n * console.log(result.data) // The normalized data\n * } else {\n * console.log('Failed to fetch data')\n * }\n * ```\n */\nexport async function fetchUrl(\n url: string,\n limiter: <T>(fn: () => Promise<T>) => Promise<T>,\n config?: FetchConfig,\n): Promise<ResolveResult> {\n try {\n const host = getHost(url)\n\n // Get the headers that match the domain\n const headers = config?.headers?.find((a) => a.domains.find((d) => d === host) !== undefined)?.headers\n\n const exec = config?.fetch ?? fetch\n\n const result = await limiter(() =>\n exec(url, {\n headers,\n }),\n )\n\n if (result.ok) {\n const body = await result.text()\n\n return {\n ok: true,\n data: normalize(body),\n }\n }\n\n return {\n ok: false,\n }\n } catch {\n return {\n ok: false,\n }\n }\n}\n\n/**\n * Creates a plugin for handling remote URL references.\n * This plugin validates and fetches data from HTTP/HTTPS URLs.\n *\n * @returns A plugin object with validate and exec functions\n * @example\n * const urlPlugin = fetchUrls()\n * if (urlPlugin.validate('https://example.com/schema.json')) {\n * const result = await urlPlugin.exec('https://example.com/schema.json')\n * }\n */\nexport function fetchUrls(config?: FetchConfig & Partial<{ limit: number | null }>): LoaderPlugin {\n // If there is a limit specified we limit the number of concurrent calls\n const limiter = config?.limit ? createLimiter(config.limit) : <T>(fn: () => Promise<T>) => fn()\n\n return {\n type: 'loader',\n validate: isRemoteUrl,\n exec: (value) => fetchUrl(value, limiter, config),\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAC1B,SAAS,qBAAqB;AAE9B,SAAS,mBAAmB;AAW5B,MAAM,UAAU,CAAC,QAA+B;AAC9C,MAAI;AACF,WAAO,IAAI,IAAI,GAAG,EAAE;AAAA,EACtB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAgBA,eAAsB,SACpB,KACA,SACA,QACwB;AACxB,MAAI;AACF,UAAM,OAAO,QAAQ,GAAG;AAGxB,UAAM,UAAU,QAAQ,SAAS,KAAK,CAAC,MAAM,EAAE,QAAQ,KAAK,CAAC,MAAM,MAAM,IAAI,MAAM,MAAS,GAAG;AAE/F,UAAM,OAAO,QAAQ,SAAS;AAE9B,UAAM,SAAS,MAAM;AAAA,MAAQ,MAC3B,KAAK,KAAK;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH;AAEA,QAAI,OAAO,IAAI;AACb,YAAM,OAAO,MAAM,OAAO,KAAK;AAE/B,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM,UAAU,IAAI;AAAA,MACtB;AAAA,IACF;AAEA,WAAO;AAAA,MACL,IAAI;AAAA,IACN;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,MACL,IAAI;AAAA,IACN;AAAA,EACF;AACF;AAaO,SAAS,UAAU,QAAwE;AAEhG,QAAM,UAAU,QAAQ,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAI,OAAyB,GAAG;AAE9F,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU;AAAA,IACV,MAAM,CAAC,UAAU,SAAS,OAAO,SAAS,MAAM;AAAA,EAClD;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,4 @@
1
- import type { Plugin } from '../../../bundle/index.js';
1
+ import type { LoaderPlugin } from '../../../bundle/index.js';
2
2
  /**
3
3
  * Creates a plugin that parses JSON strings into JavaScript objects.
4
4
  * @returns A plugin object with validate and exec functions
@@ -9,5 +9,5 @@ import type { Plugin } from '../../../bundle/index.js';
9
9
  * // result = { name: 'John', age: 30 }
10
10
  * ```
11
11
  */
12
- export declare function parseJson(): Plugin;
12
+ export declare function parseJson(): LoaderPlugin;
13
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/bundle/plugins/parse-json/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,UAAU,CAAA;AAErD;;;;;;;;;GASG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAgBlC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/bundle/plugins/parse-json/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,UAAU,CAAA;AAE3D;;;;;;;;;GASG;AACH,wBAAgB,SAAS,IAAI,YAAY,CAiBxC"}
@@ -1,6 +1,7 @@
1
1
  import { isJsonObject } from "../../../utils/is-json-object.js";
2
2
  function parseJson() {
3
3
  return {
4
+ type: "loader",
4
5
  validate: isJsonObject,
5
6
  exec: async (value) => {
6
7
  try {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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;",
4
+ "sourcesContent": ["import { isJsonObject } from '@/utils/is-json-object'\nimport type { LoaderPlugin, 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(): LoaderPlugin {\n return {\n type: 'loader',\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,YAA0B;AACxC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,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
6
  "names": []
7
7
  }
@@ -1,4 +1,4 @@
1
- import type { Plugin } from '../../../bundle/bundle.js';
1
+ import type { LoaderPlugin } from '../../../bundle/index.js';
2
2
  /**
3
3
  * Creates a plugin that parses YAML strings into JavaScript objects.
4
4
  * @returns A plugin object with validate and exec functions
@@ -9,5 +9,5 @@ import type { Plugin } from '../../../bundle/bundle.js';
9
9
  * // result = { name: 'John', age: 30 }
10
10
  * ```
11
11
  */
12
- export declare function parseYaml(): Plugin;
12
+ export declare function parseYaml(): LoaderPlugin;
13
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/bundle/plugins/parse-yaml/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,UAAU,CAAA;AAI3D;;;;;;;;;GASG;AACH,wBAAgB,SAAS,IAAI,YAAY,CAiBxC"}
@@ -2,6 +2,7 @@ import { isYaml } from "../../../utils/is-yaml.js";
2
2
  import YAML from "yaml";
3
3
  function parseYaml() {
4
4
  return {
5
+ type: "loader",
5
6
  validate: isYaml,
6
7
  exec: async (value) => {
7
8
  try {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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;",
4
+ "sourcesContent": ["import type { LoaderPlugin, ResolveResult } from '@/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(): LoaderPlugin {\n return {\n type: 'loader',\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,YAA0B;AACxC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,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
6
  "names": []
7
7
  }
@@ -1,4 +1,4 @@
1
- import { type Plugin, type ResolveResult } from '../../../bundle/bundle.js';
1
+ import type { LoaderPlugin, ResolveResult } from '../../../bundle/index.js';
2
2
  /**
3
3
  * Reads and normalizes data from a local file
4
4
  * @param path - The file path to read from
@@ -25,5 +25,5 @@ export declare function readFile(path: string): Promise<ResolveResult>;
25
25
  * const result = await filePlugin.exec('./local-schema.json')
26
26
  * }
27
27
  */
28
- export declare function readFiles(): Plugin;
28
+ export declare function readFiles(): LoaderPlugin;
29
29
  //# sourceMappingURL=index.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/bundle/plugins/read-files/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAG3D;;;;;;;;;;;;;GAaG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAmBnE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,IAAI,YAAY,CAMxC"}
@@ -19,6 +19,7 @@ async function readFile(path) {
19
19
  }
20
20
  function readFiles() {
21
21
  return {
22
+ type: "loader",
22
23
  validate: isFilePath,
23
24
  exec: readFile
24
25
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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;",
4
+ "sourcesContent": ["import { normalize } from '@/utils/normalize'\nimport type { LoaderPlugin, ResolveResult } from '@/bundle'\nimport { isFilePath } 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(): LoaderPlugin {\n return {\n type: 'loader',\n validate: isFilePath,\n exec: readFile,\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAE1B,SAAS,kBAAkB;AAgB3B,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,YAA0B;AACxC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU;AAAA,IACV,MAAM;AAAA,EACR;AACF;",
6
6
  "names": []
7
7
  }
@@ -31,5 +31,5 @@ export declare class InvalidChangesDetectedError extends Error {
31
31
  * const updated = apply(original, changes)
32
32
  * // Result: original document with content added to the 200 response
33
33
  */
34
- export declare const apply: (document: Record<string, unknown>, diff: Difference[]) => Record<string, unknown>;
34
+ export declare const apply: <T extends Record<string, unknown>>(document: Record<string, unknown>, diff: Difference<T>[]) => T;
35
35
  //# sourceMappingURL=apply.d.ts.map
@@ -1 +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"}
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,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAC3C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAC3B,UAAU,CAAC,CAAC,CAAC,EAAE,KACpB,CAyCF,CAAA"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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;",
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 = <T extends Record<string, unknown>>(\n document: Record<string, unknown>,\n diff: Difference<T>[],\n): T => {\n // Traverse the object and apply the change\n const applyChange = (current: any, path: string[], d: Difference<T>, 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 // It is safe to cast here because this function mutates the input document\n // to match the target type T as described by the diff changeset.\n return document as T\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,CACnB,UACA,SACM;AAEN,QAAM,cAAc,CAAC,SAAc,MAAgB,GAAkB,QAAQ,MAAM;AACjF,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;AAIA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -11,7 +11,7 @@ type ChangeType = 'add' | 'update' | 'delete';
11
11
  * @property changes - The new value for the property (for add/update) or the old value (for delete)
12
12
  * @property type - The type of change that occurred
13
13
  */
14
- export type Difference = {
14
+ export type Difference<_T> = {
15
15
  path: string[];
16
16
  changes: any;
17
17
  type: ChangeType;
@@ -51,6 +51,6 @@ export type Difference = {
51
51
  * // { path: ['user', 'settings', 'theme'], changes: 'dark', type: 'update' }
52
52
  * // ]
53
53
  */
54
- export declare const diff: (doc1: Record<string, unknown>, doc2: Record<string, unknown>) => Difference[];
54
+ export declare const diff: <T extends Record<string, unknown>>(doc1: Record<string, unknown>, doc2: T) => Difference<T>[];
55
55
  export {};
56
56
  //# sourceMappingURL=diff.d.ts.map
@@ -1 +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"}
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,CAAC,EAAE,IAAI;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAA;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,oBA0C7F,CAAA"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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;",
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<_T> = { 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 = <T extends Record<string, unknown>>(doc1: Record<string, unknown>, doc2: T) => {\n const diff: Difference<T>[] = []\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,CAAoC,MAA+B,SAAY;AACjG,QAAMA,QAAwB,CAAC;AAE/B,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
6
  "names": ["diff"]
7
7
  }
@@ -36,8 +36,8 @@ import type { Difference } from '../diff/diff.js';
36
36
  * // ]
37
37
  * // }
38
38
  */
39
- export declare const merge: (diff1: Difference[], diff2: Difference[]) => {
40
- diffs: Difference[];
41
- conflicts: [Difference[], Difference[]][];
39
+ export declare const merge: <T>(diff1: Difference<T>[], diff2: Difference<T>[]) => {
40
+ diffs: Difference<T>[];
41
+ conflicts: [Difference<T>[], Difference<T>[]][];
42
42
  };
43
43
  //# sourceMappingURL=merge.d.ts.map
@@ -1 +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"}
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,GAAI,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE;;;CA8FtE,CAAA"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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;",
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 = <T>(diff1: Difference<T>[], diff2: Difference<T>[]) => {\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<T> }>()\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<T>[], Difference<T>[]]>()\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<T>[], Difference<T>[]]>()\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<T>[], Difference<T>[]][] = [...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<T>[] = [\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,CAAI,OAAwB,UAA2B;AAU1E,QAAM,OAAO,IAAI,KAAgD;AAGjE,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,IAAgD;AAG1E,QAAM,gBAAgB,oBAAI,IAAgD;AAE1E,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,YAAkD,CAAC,GAAG,cAAc,OAAO,GAAG,GAAG,cAAc,OAAO,CAAC;AAI7G,QAAM,QAAyB;AAAA,IAC7B,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
6
  "names": []
7
7
  }
@@ -1,54 +1,35 @@
1
1
  import type { UnknownObject } from '../types.js';
2
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.
3
+ * Creates a "magic" proxy for a given object or array, enabling transparent access to
4
+ * JSON Reference ($ref) values as if they were directly present on the object.
6
5
  *
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
6
+ * - If an object contains a `$ref` property, accessing the special `$ref-value` property
7
+ * will resolve and return the referenced value from the root object.
8
+ * - All nested objects and arrays are recursively wrapped in proxies, so reference resolution
9
+ * works at any depth.
10
+ * - Setting, deleting, and enumerating properties works as expected, including for proxied references.
11
+ *
12
+ * @param target - The object or array to wrap in a magic proxy
13
+ * @param root - The root object for resolving local JSON references (defaults to target)
14
+ * @returns A proxied version of the input object/array with magic $ref-value support
11
15
  *
12
16
  * @example
13
- * // Basic usage with local references
14
- * const doc = {
15
- * components: {
16
- * schemas: {
17
- * User: { type: 'object', properties: { name: { type: 'string' } } }
18
- * }
17
+ * const input = {
18
+ * definitions: {
19
+ * foo: { bar: 123 }
19
20
  * },
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
- * }
21
+ * refObj: { $ref: '#/definitions/foo' }
35
22
  * }
23
+ * const proxy = createMagicProxy(input)
36
24
  *
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' } } }
25
+ * // Accessing proxy.refObj['$ref-value'] will resolve to { bar: 123 }
26
+ * console.log(proxy.refObj['$ref-value']) // { bar: 123 }
41
27
  *
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
28
+ * // Setting and deleting properties works as expected
29
+ * proxy.refObj.extra = 'hello'
30
+ * delete proxy.refObj.extra
49
31
  */
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;
32
+ export declare const createMagicProxy: <T extends Record<keyof T & symbol, unknown>, S extends UnknownObject>(target: T, root?: S | T, cache?: Map<string, unknown>) => T;
52
33
  /**
53
34
  * Gets the raw (non-proxied) version of an object created by createMagicProxy.
54
35
  * This is useful when you need to access the original object without the magic proxy wrapper.
@@ -59,5 +40,5 @@ export declare const TARGET_SYMBOL: unique symbol;
59
40
  * const proxy = createMagicProxy({ foo: { $ref: '#/bar' } })
60
41
  * const raw = getRaw(proxy) // { foo: { $ref: '#/bar' } }
61
42
  */
62
- export declare function getRaw<T extends UnknownObject>(obj: T): T;
43
+ export declare function getRaw<T>(obj: T): T;
63
44
  //# sourceMappingURL=proxy.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/magic-proxy/proxy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAW5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,SAAS,aAAa,UAC3F,CAAC,SACH,CAAC,GAAG,CAAC,oCAyIZ,CAAA;AAED;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAUnC"}