UNPKG

decycle

Version:

JSON decycle replaces circular references with JSON path references

1 lines 4.54 kB
{"version":3,"file":"index.cjs","sources":["../src/decycle.ts"],"sourcesContent":["type ReplacerFunction = (value: unknown) => unknown;\n\ninterface DecycledObject {\n [key: string]: unknown;\n $ref?: string;\n}\n\n/**\n * Makes a deep copy of an object or array, assuring that there is at most\n * one instance of each object or array in the resulting structure. The\n * duplicate references (which might be forming cycles) are replaced with\n * an object of the form `{\"$ref\": PATH}` where the PATH is a JSONPath\n * string that locates the first occurance.\n *\n * @example\n * ```ts\n * var a = [];\n * a[0] = a;\n * JSON.stringify(decycle(a)); // '[{\"$ref\":\"$\"}]'\n * ```\n *\n * If a replacer function is provided, then it will be called for each value.\n * A replacer function receives a value and returns a replacement value.\n *\n * JSONPath is used to locate the unique object. `$` indicates the top level of\n * the object or array. `[NUMBER]` or `[STRING]` indicates a child element or\n * property.\n *\n * @param value - The object or array to decycle.\n * @param replacer - Optional replacer function called for each value.\n * @returns - A deep copy of the object with circular references replaced by `$ref` objects.\n */\nexport function decycle(value: unknown, replacer?: ReplacerFunction) {\n const visitedObjects = new WeakMap<object, string>();\n\n return deepCopy(value, '$', visitedObjects, replacer);\n}\n\n/**\n * Recursively deep copies a value, replacing circular references with\n * `{\"$ref\": PATH}` objects.\n *\n * @param value - The current value to copy.\n * @param path - The JSONPath to the current value.\n * @param visitedObjects - WeakMap tracking already-visited objects and their paths.\n * @param replacer - Optional replacer function called for each value.\n * @returns - The deep-copied value.\n */\nfunction deepCopy(\n value: unknown,\n path: string,\n visitedObjects: WeakMap<object, string>,\n replacer?: ReplacerFunction,\n): unknown {\n if (typeof replacer === 'function') {\n value = replacer(value);\n }\n\n if (!isPlainObjectOrArray(value)) {\n return value;\n }\n\n const existingPath = visitedObjects.get(value);\n\n if (existingPath !== undefined) {\n return { $ref: existingPath };\n }\n\n visitedObjects.set(value, path);\n\n if (Array.isArray(value)) {\n const copy: unknown[] = [];\n\n for (const [index, element] of value.entries()) {\n const newPath = `${path}[${index.toString()}]`;\n copy[index] = deepCopy(element, newPath, visitedObjects, replacer);\n }\n\n return copy;\n }\n\n const record = value as Record<string, unknown>;\n const copy: DecycledObject = {};\n\n for (const key of Object.keys(record)) {\n const newPath = `${path}[${JSON.stringify(key)}]`;\n copy[key] = deepCopy(record[key], newPath, visitedObjects, replacer);\n }\n\n return copy;\n}\n\n/**\n * Checks whether a value is a plain object or array (not a primitive or\n * built-in wrapper like `Boolean`, `Date`, `Number`, `RegExp`, or `String`).\n *\n * @param value - The value to check.\n * @returns - `true` if the value is a plain object or array.\n */\nfunction isPlainObjectOrArray(value: unknown): value is object {\n return (\n typeof value === 'object' &&\n value !== null &&\n !(value instanceof Boolean) &&\n !(value instanceof Date) &&\n !(value instanceof Number) &&\n !(value instanceof RegExp) &&\n !(value instanceof String)\n );\n}\n"],"names":["decycle","value","replacer","deepCopy","path","visitedObjects","isPlainObjectOrArray","existingPath","copy","index","element","newPath","record","key"],"mappings":"gFAgCO,SAASA,EAAQC,EAAgBC,EAA6B,CAGnE,OAAOC,EAASF,EAAO,QAFI,QAEiBC,CAAQ,CACtD,CAYA,SAASC,EACPF,EACAG,EACAC,EACAH,EACS,CAKT,GAJI,OAAOA,GAAa,aACtBD,EAAQC,EAASD,CAAK,GAGpB,CAACK,EAAqBL,CAAK,EAC7B,OAAOA,EAGT,MAAMM,EAAeF,EAAe,IAAIJ,CAAK,EAE7C,GAAIM,IAAiB,OACnB,MAAO,CAAE,KAAMA,CAAA,EAKjB,GAFAF,EAAe,IAAIJ,EAAOG,CAAI,EAE1B,MAAM,QAAQH,CAAK,EAAG,CACxB,MAAMO,EAAkB,CAAA,EAExB,SAAW,CAACC,EAAOC,CAAO,IAAKT,EAAM,UAAW,CAC9C,MAAMU,EAAU,GAAGP,CAAI,IAAIK,EAAM,UAAU,IAC3CD,EAAKC,CAAK,EAAIN,EAASO,EAASC,EAASN,EAAgBH,CAAQ,CACnE,CAEA,OAAOM,CACT,CAEA,MAAMI,EAASX,EACTO,EAAuB,CAAA,EAE7B,UAAWK,KAAO,OAAO,KAAKD,CAAM,EAAG,CACrC,MAAMD,EAAU,GAAGP,CAAI,IAAI,KAAK,UAAUS,CAAG,CAAC,IAC9CL,EAAKK,CAAG,EAAIV,EAASS,EAAOC,CAAG,EAAGF,EAASN,EAAgBH,CAAQ,CACrE,CAEA,OAAOM,CACT,CASA,SAASF,EAAqBL,EAAiC,CAC7D,OACE,OAAOA,GAAU,UACjBA,IAAU,MACV,EAAEA,aAAiB,UACnB,EAAEA,aAAiB,OACnB,EAAEA,aAAiB,SACnB,EAAEA,aAAiB,SACnB,EAAEA,aAAiB,OAEvB"}