diff --git a/packages/@n8n/expression-runtime/src/extensions/array-extensions.ts b/packages/@n8n/expression-runtime/src/extensions/array-extensions.ts index df5cabfb7b5..48d652e04bb 100644 --- a/packages/@n8n/expression-runtime/src/extensions/array-extensions.ts +++ b/packages/@n8n/expression-runtime/src/extensions/array-extensions.ts @@ -30,6 +30,10 @@ function last(value: unknown[]): unknown { return value[value.length - 1]; } +function reverse(value: unknown[]): unknown[] { + return [...value].reverse(); +} + function pluck(value: unknown[], extraArgs: unknown[]): unknown[] { if (!Array.isArray(extraArgs)) { throw new ExpressionExtensionError('arguments must be passed to pluck'); @@ -684,6 +688,7 @@ export const arrayExtensions: ExtensionMap = { unique, first, last, + reverse, pluck, randomItem, sum, diff --git a/packages/workflow/src/extensions/array-extensions.ts b/packages/workflow/src/extensions/array-extensions.ts index c296cfaa1e4..11ea2153a16 100644 --- a/packages/workflow/src/extensions/array-extensions.ts +++ b/packages/workflow/src/extensions/array-extensions.ts @@ -30,6 +30,10 @@ function last(value: unknown[]): unknown { return value[value.length - 1]; } +function reverse(value: unknown[]): unknown[] { + return [...value].reverse(); +} + function pluck(value: unknown[], extraArgs: unknown[]): unknown[] { if (!Array.isArray(extraArgs)) { throw new ExpressionError('arguments must be passed to pluck'); @@ -686,6 +690,7 @@ export const arrayExtensions: ExtensionMap = { unique, first, last, + reverse, pluck, randomItem, sum, diff --git a/packages/workflow/src/native-methods/array.methods.ts b/packages/workflow/src/native-methods/array.methods.ts index d6f13495754..0c5fbff15ac 100644 --- a/packages/workflow/src/native-methods/array.methods.ts +++ b/packages/workflow/src/native-methods/array.methods.ts @@ -356,7 +356,7 @@ export const arrayMethods: NativeDoc = { reverse: { doc: { name: 'reverse', - description: 'Reverses the order of the elements in the array', + description: 'Returns a new array with the elements in reverse order', examples: [ { example: "['dog', 'bites', 'man'].reverse()", evaluated: "['man', 'bites', 'dog']" }, ], diff --git a/packages/workflow/test/expression-array-proxy-semantics.test.ts b/packages/workflow/test/expression-array-proxy-semantics.test.ts index 2978703ad6e..b46fd88bdba 100644 --- a/packages/workflow/test/expression-array-proxy-semantics.test.ts +++ b/packages/workflow/test/expression-array-proxy-semantics.test.ts @@ -64,4 +64,18 @@ describe('Expression — array proxy semantics (engine parity)', () => { it('implicit string coercion uses Array.prototype.toString', () => { expect(evaluate('={{ "items: " + $json.arr }}', { arr: [1, 2, 3] })).toBe('items: 1,2,3'); }); + + it('reverse returns a reversed copy without mutating a subsequent sibling read', () => { + expect(evaluate('={{ [$json.arr.reverse(), $json.arr] }}', { arr: [1, 2, 3] })).toEqual([ + [3, 2, 1], + [1, 2, 3], + ]); + }); + + it('reverse does not mutate a preceding sibling read', () => { + expect(evaluate('={{ [$json.arr, $json.arr.reverse()] }}', { arr: [1, 2, 3] })).toEqual([ + [1, 2, 3], + [3, 2, 1], + ]); + }); });