fix(core): Do not mark duplicates as circular references in jsonStringify (#5789)

* fix(core): jsonStringify should not mark duplicates as circular references

* not mark duplicates as circular references in the code node as well
This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2023-03-27 16:22:59 +02:00 committed by GitHub
parent f15f4bdcf2
commit 18efaf397a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 22 additions and 19 deletions

View file

@ -12,9 +12,7 @@ function isTraversable(maybe: unknown): maybe is IDataObject {
* Stringify any non-standard JS objects (e.g. `Date`, `RegExp`) inside output items at any depth. * Stringify any non-standard JS objects (e.g. `Date`, `RegExp`) inside output items at any depth.
*/ */
export function standardizeOutput(output: IDataObject) { export function standardizeOutput(output: IDataObject) {
const knownObjects = new WeakSet(); function standardizeOutputRecursive(obj: IDataObject, knownObjects = new WeakSet()): IDataObject {
function standardizeOutputRecursive(obj: IDataObject): IDataObject {
for (const [key, value] of Object.entries(obj)) { for (const [key, value] of Object.entries(obj)) {
if (!isTraversable(value)) continue; if (!isTraversable(value)) continue;
@ -29,7 +27,7 @@ export function standardizeOutput(output: IDataObject) {
obj[key] = obj[key] =
value.constructor.name !== 'Object' value.constructor.name !== 'Object'
? JSON.stringify(value) // Date, RegExp, etc. ? JSON.stringify(value) // Date, RegExp, etc.
: standardizeOutputRecursive(value); : standardizeOutputRecursive(value, knownObjects);
} }
return obj; return obj;
} }

View file

@ -64,27 +64,24 @@ export const jsonParse = <T>(jsonString: string, options?: JSONParseOptions<T>):
type JSONStringifyOptions = { type JSONStringifyOptions = {
replaceCircularRefs?: boolean; replaceCircularRefs?: boolean;
circularRefReplacement?: string;
}; };
const getReplaceCircularReferencesFn = (options: JSONStringifyOptions) => { const replaceCircularReferences = <T>(value: T, knownObjects = new WeakSet()): T => {
const knownObjects = new WeakSet(); if (value && typeof value === 'object') {
return (key: any, value: any) => { if (knownObjects.has(value)) return '[Circular Reference]' as T;
if (typeof value === 'object' && value !== null) { knownObjects.add(value);
if (knownObjects.has(value)) { const copy = (Array.isArray(value) ? [] : {}) as T;
return options?.circularRefReplacement ?? '[Circular Reference]'; for (const key in value) {
} copy[key] = replaceCircularReferences(value[key], knownObjects);
knownObjects.add(value);
} }
return value; knownObjects.delete(value);
}; return copy;
}
return value;
}; };
export const jsonStringify = (obj: unknown, options: JSONStringifyOptions = {}): string => { export const jsonStringify = (obj: unknown, options: JSONStringifyOptions = {}): string => {
const replacer = options?.replaceCircularRefs return JSON.stringify(options?.replaceCircularRefs ? replaceCircularReferences(obj) : obj);
? getReplaceCircularReferencesFn(options)
: undefined;
return JSON.stringify(obj, replacer);
}; };
export const sleep = async (ms: number): Promise<void> => export const sleep = async (ms: number): Promise<void> =>

View file

@ -30,6 +30,14 @@ describe('jsonStringify', () => {
'{"a":1,"b":2,"c":"[Circular Reference]"}', '{"a":1,"b":2,"c":"[Circular Reference]"}',
); );
}); });
it('should not detect duplicates as circular references', () => {
const y = { z: 5 };
const x = [y, y, { y }];
expect(jsonStringify(x, { replaceCircularRefs: true })).toEqual(
'[{"z":5},{"z":5},{"y":{"z":5}}]',
);
});
}); });
describe('deepCopy', () => { describe('deepCopy', () => {