From 660f2ff09ba56872d34ef8def0d772ba56a85202 Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Mon, 11 Aug 2025 15:25:52 +0200 Subject: [PATCH] chore(core): Allow undefined in the node output value (#18198) --- package.json | 2 +- .../__tests__/is-json-compatible.test.ts | 7 +- packages/core/src/utils/is-json-compatible.ts | 83 +++++++++---------- 3 files changed, 40 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index 4125bd5ef4..0a4936de78 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "optimize-svg": "find ./packages -name '*.svg' ! -name 'pipedrive.svg' -print0 | xargs -0 -P16 -L20 npx svgo", "setup-backend-module": "node scripts/ensure-zx.mjs && zx scripts/backend-module/setup.mjs", "start": "run-script-os", - "start:default": "cd packages/cli/bin && ./n8n", + "start:default": "cd packages/cli/bin && node --inspect ./n8n", "start:tunnel": "./packages/cli/bin/n8n start --tunnel", "start:windows": "cd packages/cli/bin && n8n", "test": "JEST_JUNIT_CLASSNAME={filepath} turbo run test", diff --git a/packages/core/src/utils/__tests__/is-json-compatible.test.ts b/packages/core/src/utils/__tests__/is-json-compatible.test.ts index 23c5e787e6..49b12b871e 100644 --- a/packages/core/src/utils/__tests__/is-json-compatible.test.ts +++ b/packages/core/src/utils/__tests__/is-json-compatible.test.ts @@ -99,12 +99,6 @@ describe('isJsonCompatible', () => { errorPath: 'value.nan', errorMessage: 'is NaN, which is not JSON-compatible', }, - { - name: 'undefined', - value: { undefined }, - errorPath: 'value.undefined', - errorMessage: 'is of unknown type (undefined) with value undefined', - }, { name: 'an object with symbol keys', value: { [Symbol.for('key')]: 1 }, @@ -125,6 +119,7 @@ describe('isJsonCompatible', () => { const objectRef = {}; test.each([ { name: 'null', value: { null: null } }, + { name: 'undefined', value: { noValue: undefined } }, { name: 'an array of primitives', value: { array: [1, 'string', true, false] } }, { name: 'an object without a prototype chain', diff --git a/packages/core/src/utils/is-json-compatible.ts b/packages/core/src/utils/is-json-compatible.ts index 8f3149c405..11355ddfd7 100644 --- a/packages/core/src/utils/is-json-compatible.ts +++ b/packages/core/src/utils/is-json-compatible.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line complexity const check = ( val: unknown, path = 'value', @@ -6,7 +7,7 @@ const check = ( ): { isValid: true } | { isValid: false; errorPath: string; errorMessage: string } => { const type = typeof val; - if (val === null || type === 'boolean' || type === 'string') { + if (val === null || type === 'boolean' || type === 'string' || type === 'undefined') { return { isValid: true }; } @@ -46,52 +47,44 @@ const check = ( return { isValid: true }; } - if (type === 'object') { - if (stack.has(val)) { - return { - isValid: false, - errorPath: path, - errorMessage: 'contains a circular reference', - }; - } - stack.add(val); - - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const proto = Object.getPrototypeOf(val); - if (proto !== Object.prototype && proto !== null) { - return { - isValid: false, - errorPath: path, - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - errorMessage: `has non-plain prototype (${proto?.constructor?.name || 'unknown'})`, - }; - } - for (const key of Reflect.ownKeys(val as object)) { - if (typeof key === 'symbol') { - return { - isValid: false, - errorPath: `${path}.${key.toString()}`, - errorMessage: `has a symbol key (${String(key)}), which is not JSON-compatible`, - }; - } - - if (keysToIgnore.has(key)) { - continue; - } - - const subVal = (val as Record)[key]; - const result = check(subVal, `${path}.${key}`, stack, keysToIgnore); - if (!result.isValid) return result; - } - stack.delete(val); - return { isValid: true }; + if (stack.has(val)) { + return { + isValid: false, + errorPath: path, + errorMessage: 'contains a circular reference', + }; } + stack.add(val); - return { - isValid: false, - errorPath: path, - errorMessage: `is of unknown type (${type}) with value ${JSON.stringify(val)}`, - }; + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const proto = Object.getPrototypeOf(val); + if (proto !== Object.prototype && proto !== null) { + return { + isValid: false, + errorPath: path, + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + errorMessage: `has non-plain prototype (${proto?.constructor?.name || 'unknown'})`, + }; + } + for (const key of Reflect.ownKeys(val as object)) { + if (typeof key === 'symbol') { + return { + isValid: false, + errorPath: `${path}.${key.toString()}`, + errorMessage: `has a symbol key (${String(key)}), which is not JSON-compatible`, + }; + } + + if (keysToIgnore.has(key)) { + continue; + } + + const subVal = (val as Record)[key]; + const result = check(subVal, `${path}.${key}`, stack, keysToIgnore); + if (!result.isValid) return result; + } + stack.delete(val); + return { isValid: true }; }; /**