first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
@@ -0,0 +1,19 @@
{
"node": "n8n-nodes-base.splitOut",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"details": "",
"categories": ["Core Nodes"],
"resources": {
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.splitout/"
}
],
"generic": []
},
"alias": ["Split", "Nested", "Transform", "Array", "List", "Item"],
"subcategories": {
"Core Nodes": ["Data Transformation"]
}
}
@@ -0,0 +1,282 @@
import get from 'lodash/get';
import unset from 'lodash/unset';
import { NodeOperationError, deepCopy, NodeConnectionTypes } from 'n8n-workflow';
import type {
IBinaryData,
IDataObject,
IExecuteFunctions,
INodeExecutionData,
INodeType,
INodeTypeDescription,
} from 'n8n-workflow';
import { prepareFieldsArray } from '../utils/utils';
import { FieldsTracker } from './utils';
export class SplitOut implements INodeType {
description: INodeTypeDescription = {
displayName: 'Split Out',
name: 'splitOut',
icon: 'file:splitOut.svg',
group: ['transform'],
subtitle: '',
version: 1,
description: 'Turn a list inside item(s) into separate items',
defaults: {
name: 'Split Out',
},
inputs: [NodeConnectionTypes.Main],
outputs: [NodeConnectionTypes.Main],
builderHint: {
relatedNodes: [
{
nodeType: 'n8n-nodes-base.aggregate',
relationHint: 'Reverse operation - combine items back',
},
],
},
properties: [
{
displayName: 'Fields To Split Out',
name: 'fieldToSplitOut',
type: 'string',
default: '',
required: true,
placeholder: 'Drag fields from the left or type their names',
description:
'The name of the input fields to break out into separate items. Separate multiple field names by commas. For binary data, use $binary.',
requiresDataPath: 'multiple',
hint: 'Use $binary to split out the input item by binary data',
},
{
displayName: 'Include',
name: 'include',
type: 'options',
options: [
{
name: 'No Other Fields',
value: 'noOtherFields',
},
{
name: 'All Other Fields',
value: 'allOtherFields',
},
{
name: 'Selected Other Fields',
value: 'selectedOtherFields',
},
],
default: 'noOtherFields',
description: 'Whether to copy any other fields into the new items',
},
{
displayName: 'Fields To Include',
name: 'fieldsToInclude',
type: 'string',
placeholder: 'e.g. email, name',
requiresDataPath: 'multiple',
description: 'Fields in the input items to aggregate together',
default: '',
displayOptions: {
show: {
include: ['selectedOtherFields'],
},
},
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
options: [
{
displayName: 'Disable Dot Notation',
name: 'disableDotNotation',
type: 'boolean',
default: false,
description:
'Whether to disallow referencing child fields using `parent.child` in the field name',
},
{
displayName: 'Destination Field Name',
name: 'destinationFieldName',
type: 'string',
requiresDataPath: 'multiple',
default: '',
description: 'The field in the output under which to put the split field contents',
},
{
displayName: 'Include Binary',
name: 'includeBinary',
type: 'boolean',
default: false,
description: 'Whether to include the binary data in the new items',
},
],
},
],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const returnData: INodeExecutionData[] = [];
const items = this.getInputData();
const fieldsTracker = new FieldsTracker();
for (let i = 0; i < items.length; i++) {
const fieldsToSplitOut = (this.getNodeParameter('fieldToSplitOut', i) as string)
.split(',')
.map((field) => field.trim().replace(/^\$json\./, ''));
const options = this.getNodeParameter('options', i, {});
const disableDotNotation = options.disableDotNotation as boolean;
const destinationFields = ((options.destinationFieldName as string) || '')
.split(',')
.filter((field) => field.trim() !== '')
.map((field) => field.trim());
if (destinationFields.length && destinationFields.length !== fieldsToSplitOut.length) {
throw new NodeOperationError(
this.getNode(),
'If multiple fields to split out are given, the same number of destination fields must be given',
);
}
const include = this.getNodeParameter('include', i) as
| 'selectedOtherFields'
| 'allOtherFields'
| 'noOtherFields';
const multiSplit = fieldsToSplitOut.length > 1;
const item = { ...items[i].json };
const splited: INodeExecutionData[] = [];
for (const [entryIndex, fieldToSplitOut] of fieldsToSplitOut.entries()) {
const destinationFieldName = destinationFields[entryIndex] || '';
let entityToSplit: IDataObject[] = [];
if (fieldToSplitOut === '$binary') {
entityToSplit = Object.entries(items[i].binary || {}).map(([key, value]) => ({
[key]: value,
}));
} else {
if (!disableDotNotation) {
entityToSplit = get(item, fieldToSplitOut) as IDataObject[];
} else {
entityToSplit = item[fieldToSplitOut] as IDataObject[];
}
fieldsTracker.add(fieldToSplitOut);
const entryExists = entityToSplit !== undefined;
if (!entryExists) {
entityToSplit = [];
}
fieldsTracker.update(fieldToSplitOut, entryExists);
if (typeof entityToSplit !== 'object' || entityToSplit === null) {
entityToSplit = [entityToSplit] as unknown as IDataObject[];
}
if (!Array.isArray(entityToSplit)) {
entityToSplit = Object.values(entityToSplit);
}
}
for (const [elementIndex, element] of entityToSplit.entries()) {
if (splited[elementIndex] === undefined) {
splited[elementIndex] = { json: {}, pairedItem: { item: i } };
}
const fieldName = destinationFieldName || fieldToSplitOut;
if (fieldToSplitOut === '$binary') {
if (splited[elementIndex].binary === undefined) {
splited[elementIndex].binary = {};
}
splited[elementIndex].binary[Object.keys(element)[0]] = Object.values(
element,
)[0] as IBinaryData;
continue;
}
if (typeof element === 'object' && element !== null && include === 'noOtherFields') {
if (destinationFieldName === '' && !multiSplit) {
splited[elementIndex] = {
json: { ...splited[elementIndex].json, ...element },
pairedItem: { item: i },
};
} else {
splited[elementIndex].json[fieldName] = element;
}
} else {
splited[elementIndex].json[fieldName] = element;
}
}
}
for (const splitEntry of splited) {
let newItem: INodeExecutionData = splitEntry;
if (include === 'allOtherFields') {
const itemCopy = deepCopy(item);
for (const fieldToSplitOut of fieldsToSplitOut) {
if (!disableDotNotation) {
unset(itemCopy, fieldToSplitOut);
} else {
delete itemCopy[fieldToSplitOut];
}
}
newItem.json = { ...itemCopy, ...splitEntry.json };
}
if (include === 'selectedOtherFields') {
const fieldsToInclude = prepareFieldsArray(
this.getNodeParameter('fieldsToInclude', i, '') as string,
'Fields To Include',
);
if (!fieldsToInclude.length) {
throw new NodeOperationError(this.getNode(), 'No fields specified', {
description: 'Please add a field to include',
});
}
for (const field of fieldsToInclude) {
if (!disableDotNotation) {
splitEntry.json[field] = get(item, field);
} else {
splitEntry.json[field] = item[field];
}
}
newItem = splitEntry;
}
const includeBinary = options.includeBinary as boolean;
if (includeBinary) {
if (items[i].binary && !newItem.binary) {
newItem.binary = items[i].binary;
}
}
returnData.push(newItem);
}
}
const hints = fieldsTracker.getHints();
if (hints.length) {
this.addExecutionHints(...hints);
}
return [returnData];
}
}
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" fill="none"><g fill="#9B6DD5" clip-path="url(#a)"><path fill-rule="evenodd" d="M480 148c0-6.627-5.373-12-12-12H322c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h146c6.627 0 12-5.373 12-12zm0 96c0-6.627-5.373-12-12-12H322c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h146c6.627 0 12-5.373 12-12zm0 96c0-6.627-5.373-12-12-12H322c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h146c6.627 0 12-5.373 12-12z" clip-rule="evenodd"/><path d="M438 76c0 6.627-5.373 12-12 12H309.783c-17.673 0-32 14.327-32 32v56c0 26.978-10.272 51.557-27.119 70.039-5.055 5.545-5.055 14.377 0 19.922 16.847 18.482 27.119 43.061 27.119 70.039v56c0 17.673 14.327 32 32 32H426c6.627 0 12 5.373 12 12v24c0 6.627-5.373 12-12 12H309.783c-44.183 0-80-35.817-80-80v-56c0-30.928-25.072-56-56-56a5.783 5.783 0 0 1-5.783-5.783v-36.434a5.783 5.783 0 0 1 5.783-5.783c30.928 0 56-25.072 56-56v-56c0-44.183 35.817-80 80-80H426c6.627 0 12 5.373 12 12z"/><path fill-rule="evenodd" d="M136 244c0-6.627-5.373-12-12-12H12c-6.627 0-12 5.373-12 12v24c0 6.627 5.373 12 12 12h112c6.627 0 12-5.373 12-12z" clip-rule="evenodd"/></g><defs><clipPath id="a"><path fill="#fff" d="M512 0H0v512h512z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,5 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
describe('Test Split Out Node', () => {
new NodeTestHarness().setupTests();
});
@@ -0,0 +1,68 @@
import { FieldsTracker } from '../utils';
describe('FieldsTracker', () => {
let fieldsTracker: FieldsTracker;
beforeEach(() => {
fieldsTracker = new FieldsTracker();
});
describe('add', () => {
it('should add field with false value', () => {
fieldsTracker.add('testField');
expect(fieldsTracker.fields.testField).toBe(false);
});
it('should not overwrite existing field', () => {
fieldsTracker.add('testField');
fieldsTracker.fields.testField = true;
fieldsTracker.add('testField');
expect(fieldsTracker.fields.testField).toBe(true);
});
});
describe('update', () => {
it('should update field from false to true', () => {
fieldsTracker.add('testField');
fieldsTracker.update('testField', true);
expect(fieldsTracker.fields.testField).toBe(true);
});
it('should not update field from true to false', () => {
fieldsTracker.add('testField');
fieldsTracker.fields.testField = true;
fieldsTracker.update('testField', false);
expect(fieldsTracker.fields.testField).toBe(true);
});
});
describe('getHints', () => {
it('should return empty array when no fields tracked', () => {
expect(fieldsTracker.getHints()).toEqual([]);
});
it('should return hint for missing field', () => {
fieldsTracker.add('missingField');
const hints = fieldsTracker.getHints();
expect(hints).toEqual([
{
message: "The field 'missingField' wasn't found in any input item",
location: 'outputPane',
},
]);
});
it('should not return hint for found field', () => {
fieldsTracker.add('foundField');
fieldsTracker.update('foundField', true);
expect(fieldsTracker.getHints()).toEqual([]);
});
});
});
@@ -0,0 +1,388 @@
{
"name": "splitOut test",
"nodes": [
{
"parameters": {},
"id": "6c90bf81-0c0e-4c5f-9f0c-297f06d9668a",
"name": "When clicking \"Execute Workflow\"",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [-400, 400]
},
{
"parameters": {
"data": {
"data": [
{
"id": 3,
"char": "c"
},
{
"id": 4,
"char": "d"
},
{
"id": 5,
"char": "e"
},
{
"id": 1,
"char": "a"
},
{
"id": 2,
"char": "b"
}
],
"data2": [
{
"text": "foo"
}
],
"data3": [
{
"text": "bar"
}
]
}
},
"id": "2e0011d5-c6a0-4a40-ab8c-9d011cde40d5",
"name": "Code",
"type": "n8n-nodes-testing.testData",
"typeVersion": 1,
"position": [-180, 400]
},
{
"parameters": {
"fieldToSplitOut": "data",
"options": {}
},
"id": "e7eac465-8fe6-498c-9942-ebd47df537c1",
"name": "Item Lists",
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [80, 160]
},
{
"parameters": {
"fieldToSplitOut": "data",
"include": "allOtherFields",
"options": {}
},
"id": "09b7fe15-dbad-4ca6-bf1e-3093139d14e5",
"name": "Item Lists1",
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [80, 320]
},
{
"parameters": {
"fieldToSplitOut": "data",
"include": "selectedOtherFields",
"fieldsToInclude": ["data3"],
"options": {}
},
"id": "7ea63dc7-8141-4233-af47-9894919c7fe4",
"name": "Item Lists2",
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [80, 480]
},
{
"parameters": {
"fieldToSplitOut": "data",
"options": {
"destinationFieldName": "output"
}
},
"id": "89c3c1b4-9577-480a-931f-3b34450b23cb",
"name": "Item Lists3",
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [80, 660]
}
],
"pinData": {
"Item Lists": [
{
"json": {
"id": 3,
"char": "c"
}
},
{
"json": {
"id": 4,
"char": "d"
}
},
{
"json": {
"id": 5,
"char": "e"
}
},
{
"json": {
"id": 1,
"char": "a"
}
},
{
"json": {
"id": 2,
"char": "b"
}
}
],
"Item Lists1": [
{
"json": {
"data2": [
{
"text": "foo"
}
],
"data3": [
{
"text": "bar"
}
],
"data": {
"id": 3,
"char": "c"
}
}
},
{
"json": {
"data2": [
{
"text": "foo"
}
],
"data3": [
{
"text": "bar"
}
],
"data": {
"id": 4,
"char": "d"
}
}
},
{
"json": {
"data2": [
{
"text": "foo"
}
],
"data3": [
{
"text": "bar"
}
],
"data": {
"id": 5,
"char": "e"
}
}
},
{
"json": {
"data2": [
{
"text": "foo"
}
],
"data3": [
{
"text": "bar"
}
],
"data": {
"id": 1,
"char": "a"
}
}
},
{
"json": {
"data2": [
{
"text": "foo"
}
],
"data3": [
{
"text": "bar"
}
],
"data": {
"id": 2,
"char": "b"
}
}
}
],
"Item Lists2": [
{
"json": {
"data3": [
{
"text": "bar"
}
],
"data": {
"id": 3,
"char": "c"
}
}
},
{
"json": {
"data3": [
{
"text": "bar"
}
],
"data": {
"id": 4,
"char": "d"
}
}
},
{
"json": {
"data3": [
{
"text": "bar"
}
],
"data": {
"id": 5,
"char": "e"
}
}
},
{
"json": {
"data3": [
{
"text": "bar"
}
],
"data": {
"id": 1,
"char": "a"
}
}
},
{
"json": {
"data3": [
{
"text": "bar"
}
],
"data": {
"id": 2,
"char": "b"
}
}
}
],
"Item Lists3": [
{
"json": {
"output": {
"id": 3,
"char": "c"
}
}
},
{
"json": {
"output": {
"id": 4,
"char": "d"
}
}
},
{
"json": {
"output": {
"id": 5,
"char": "e"
}
}
},
{
"json": {
"output": {
"id": 1,
"char": "a"
}
}
},
{
"json": {
"output": {
"id": 2,
"char": "b"
}
}
}
]
},
"connections": {
"When clicking \"Execute Workflow\"": {
"main": [
[
{
"node": "Code",
"type": "main",
"index": 0
}
]
]
},
"Code": {
"main": [
[
{
"node": "Item Lists",
"type": "main",
"index": 0
},
{
"node": "Item Lists1",
"type": "main",
"index": 0
},
{
"node": "Item Lists2",
"type": "main",
"index": 0
},
{
"node": "Item Lists3",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {},
"versionId": "9230f580-6f41-47c9-9949-bf258fc3fa47",
"id": "105",
"meta": {
"instanceId": "36203ea1ce3cef713fa25999bd9874ae26b9e4c2c3a90a365f2882a154d031d0"
},
"tags": []
}
@@ -0,0 +1,409 @@
{
"name": "itemList split Object",
"nodes": [
{
"parameters": {},
"id": "ade46a75-ab57-48c6-886b-0c118f5ef1c6",
"name": "When clicking \"Execute Workflow\"",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [520, 800]
},
{
"parameters": {
"fieldToSplitOut": "data",
"include": "selectedOtherFields",
"fieldsToInclude": ["tag"],
"options": {}
},
"id": "45e1d7a3-d6e8-4b69-a68a-1038db13be4c",
"name": "Item Lists1",
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [1120, 340]
},
{
"parameters": {
"data": {
"data": {
"entry1": {
"id": 1,
"info": "some info 1"
},
"entry2": {
"id": 2,
"info": "some info 2"
},
"entry3": {
"id": 3,
"info": "some info 3"
}
},
"data2": ["a", "b", "c"],
"data3": {
"a": 1,
"b": 2,
"c": 3
},
"data4": null,
"tag": "bar"
}
},
"id": "faa78fac-468d-42b8-96e9-0fb62c312da3",
"name": "Code1",
"type": "n8n-nodes-testing.testData",
"typeVersion": 1,
"position": [760, 800]
},
{
"parameters": {},
"id": "5baaf321-7e89-473d-a313-7cb90b3f13b3",
"name": "No Operation, do nothing",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1380, 340]
},
{
"parameters": {
"fieldToSplitOut": "data3",
"include": "allOtherFields",
"options": {
"destinationFieldName": "extracted"
}
},
"id": "a786bea9-eb29-4c6d-aea6-a22aee622bc6",
"name": "Item Lists",
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [1120, 720]
},
{
"parameters": {},
"id": "0521a24b-c74a-48fa-ae50-48a242b97806",
"name": "No Operation, do nothing1",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1380, 720]
},
{
"parameters": {
"fieldToSplitOut": "data3",
"options": {}
},
"id": "0c1c8827-72ab-4738-918c-d529e66505c6",
"name": "Item Lists2",
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [1120, 540]
},
{
"parameters": {},
"id": "4c0dca36-c2ae-4d40-8952-0e728ac93fa3",
"name": "No Operation, do nothing2",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1380, 540]
},
{
"parameters": {
"fieldToSplitOut": "data2",
"options": {}
},
"id": "b2031380-b2a8-426d-8f7a-ab072d23b979",
"name": "Item Lists3",
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [1120, 920]
},
{
"parameters": {},
"id": "617f7259-beee-42f1-bba2-4e75a83fe369",
"name": "No Operation, do nothing3",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1380, 920]
},
{
"parameters": {
"fieldToSplitOut": "data4",
"include": "allOtherFields",
"options": {}
},
"id": "8909b8eb-e5a9-4436-8e62-09d8c9670ac1",
"name": "Item Lists4",
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [1120, 1140],
"continueOnFail": true
},
{
"parameters": {},
"id": "a9278f90-8ad9-42dc-85b6-28bf1b6764b7",
"name": "No Operation, do nothing4",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1380, 1140]
}
],
"pinData": {
"No Operation, do nothing": [
{
"json": {
"tag": "bar",
"data": {
"id": 1,
"info": "some info 1"
}
}
},
{
"json": {
"tag": "bar",
"data": {
"id": 2,
"info": "some info 2"
}
}
},
{
"json": {
"tag": "bar",
"data": {
"id": 3,
"info": "some info 3"
}
}
}
],
"No Operation, do nothing2": [
{
"json": {
"data3": 1
}
},
{
"json": {
"data3": 2
}
},
{
"json": {
"data3": 3
}
}
],
"No Operation, do nothing1": [
{
"json": {
"data": {
"entry1": {
"id": 1,
"info": "some info 1"
},
"entry2": {
"id": 2,
"info": "some info 2"
},
"entry3": {
"id": 3,
"info": "some info 3"
}
},
"data2": ["a", "b", "c"],
"data4": null,
"tag": "bar",
"extracted": 1
}
},
{
"json": {
"data": {
"entry1": {
"id": 1,
"info": "some info 1"
},
"entry2": {
"id": 2,
"info": "some info 2"
},
"entry3": {
"id": 3,
"info": "some info 3"
}
},
"data2": ["a", "b", "c"],
"data4": null,
"tag": "bar",
"extracted": 2
}
},
{
"json": {
"data": {
"entry1": {
"id": 1,
"info": "some info 1"
},
"entry2": {
"id": 2,
"info": "some info 2"
},
"entry3": {
"id": 3,
"info": "some info 3"
}
},
"data2": ["a", "b", "c"],
"data4": null,
"tag": "bar",
"extracted": 3
}
}
],
"No Operation, do nothing3": [
{
"json": {
"data2": "a"
}
},
{
"json": {
"data2": "b"
}
},
{
"json": {
"data2": "c"
}
}
],
"No Operation, do nothing4": [
{
"json": {
"data": {
"entry1": {
"id": 1,
"info": "some info 1"
},
"entry2": {
"id": 2,
"info": "some info 2"
},
"entry3": {
"id": 3,
"info": "some info 3"
}
},
"data2": ["a", "b", "c"],
"data3": {
"a": 1,
"b": 2,
"c": 3
},
"data4": null,
"tag": "bar"
}
}
]
},
"connections": {
"When clicking \"Execute Workflow\"": {
"main": [
[
{
"node": "Code1",
"type": "main",
"index": 0
}
]
]
},
"Code1": {
"main": [
[
{
"node": "Item Lists1",
"type": "main",
"index": 0
},
{
"node": "Item Lists2",
"type": "main",
"index": 0
},
{
"node": "Item Lists",
"type": "main",
"index": 0
},
{
"node": "Item Lists3",
"type": "main",
"index": 0
},
{
"node": "Item Lists4",
"type": "main",
"index": 0
}
]
]
},
"Item Lists1": {
"main": [
[
{
"node": "No Operation, do nothing",
"type": "main",
"index": 0
}
]
]
},
"Item Lists": {
"main": [
[
{
"node": "No Operation, do nothing1",
"type": "main",
"index": 0
}
]
]
},
"Item Lists2": {
"main": [
[
{
"node": "No Operation, do nothing2",
"type": "main",
"index": 0
}
]
]
},
"Item Lists3": {
"main": [
[
{
"node": "No Operation, do nothing3",
"type": "main",
"index": 0
}
]
]
},
"Item Lists4": {
"main": [
[
{
"node": "No Operation, do nothing4",
"type": "main",
"index": 0
}
]
]
}
},
"active": false
}
@@ -0,0 +1,32 @@
import type { NodeExecutionHint } from 'n8n-workflow';
export class FieldsTracker {
fields: { [key: string]: boolean } = {};
add(key: string) {
if (this.fields[key] === undefined) {
this.fields[key] = false;
}
}
update(key: string, value: boolean) {
if (!this.fields[key] && value) {
this.fields[key] = true;
}
}
getHints() {
const hints: NodeExecutionHint[] = [];
for (const [field, value] of Object.entries(this.fields)) {
if (!value) {
hints.push({
message: `The field '${field}' wasn't found in any input item`,
location: 'outputPane',
});
}
}
return hints;
}
}