(object: unknown, refKey = '__ref__')
| 4 | // Normally schemas will be decycled on the server and retrocycled on the client. |
| 5 | |
| 6 | export function decycle(object: unknown, refKey = '__ref__'): unknown { |
| 7 | // Make a deep copy of an object or array, assuring that there is at most |
| 8 | // one instance of each object or array in the resulting structure. The |
| 9 | // duplicate references (which might be forming cycles) are replaced with |
| 10 | // an object of the form {"__ref__": PATH} where the PATH is a JSONPath string that locates the first occurance. |
| 11 | |
| 12 | // So, |
| 13 | // let a = []; |
| 14 | // a[0] = a; |
| 15 | // return JSON.stringify(JSON.decycle(a)); |
| 16 | |
| 17 | // produces the string '[{"__ref__":"$"}]'. |
| 18 | |
| 19 | // JSONPath is used to locate the unique object. $ indicates the top level of |
| 20 | // the object or array. [NUMBER] or [STRING] indicates a child element or |
| 21 | // property. |
| 22 | |
| 23 | const objects = new WeakMap() // object to path mappings |
| 24 | |
| 25 | return (function derez(value: unknown, path: string): unknown { |
| 26 | // The derez function recurses through the object, producing the deep copy. |
| 27 | |
| 28 | let oldPath // The path of an earlier occurance of value |
| 29 | let nu // The new object or array |
| 30 | |
| 31 | // typeof null === "object", so go on if this value is really an object but not |
| 32 | // one of the weird builtin objects. |
| 33 | |
| 34 | if ( |
| 35 | typeof value === 'object' && |
| 36 | value !== null && |
| 37 | !(value instanceof Boolean) && |
| 38 | !(value instanceof Date) && |
| 39 | !(value instanceof Number) && |
| 40 | !(value instanceof RegExp) && |
| 41 | !(value instanceof String) |
| 42 | ) { |
| 43 | // If the value is an object or array, look to see if we have already |
| 44 | // encountered it. If so, return a {"__ref__":PATH} object. This uses an |
| 45 | // ES6 WeakMap. |
| 46 | |
| 47 | oldPath = objects.get(value) |
| 48 | if (oldPath !== undefined) { |
| 49 | return { [refKey]: oldPath } |
| 50 | } |
| 51 | |
| 52 | // Otherwise, accumulate the unique value and its path. |
| 53 | |
| 54 | objects.set(value, path) |
| 55 | |
| 56 | // If it is an array, replicate the array. |
| 57 | |
| 58 | if (Array.isArray(value)) { |
| 59 | nu = [] |
| 60 | value.forEach(function (element, i) { |
| 61 | nu[i] = derez(element, `${path}[${i}]`) |
| 62 | }) |
| 63 | } else { |
no test coverage detected