n8n-nodes-while-loop 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2 -0
- package/dist/nodes/WhileLoop/WhileLoop.node.js +116 -0
- package/dist/nodes/icon.svg +10 -0
- package/package.json +32 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WhileLoop = void 0;
|
|
4
|
+
const { NodeApiError } = require("n8n-workflow");
|
|
5
|
+
class WhileLoop {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.description = {
|
|
8
|
+
displayName: "While Loop",
|
|
9
|
+
name: "whileLoop",
|
|
10
|
+
icon: "file:icon.svg",
|
|
11
|
+
group: ["transform"],
|
|
12
|
+
version: 1,
|
|
13
|
+
description: "Control-flow node to loop until a condition is false",
|
|
14
|
+
defaults: {
|
|
15
|
+
name: "While Loop",
|
|
16
|
+
},
|
|
17
|
+
inputs: ["main"],
|
|
18
|
+
outputs: ["main", "main"],
|
|
19
|
+
outputNames: ["Continue", "Done"],
|
|
20
|
+
properties: [
|
|
21
|
+
{
|
|
22
|
+
displayName: "Mode",
|
|
23
|
+
name: "mode",
|
|
24
|
+
type: "options",
|
|
25
|
+
options: [
|
|
26
|
+
{ name: "Counter", value: "counter", description: "Loop a fixed number of times" },
|
|
27
|
+
{ name: "Expression", value: "expression", description: "Loop while an expression is true" },
|
|
28
|
+
],
|
|
29
|
+
default: "counter",
|
|
30
|
+
description: "How to decide whether to continue the loop",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
displayName: "Max Iterations",
|
|
34
|
+
name: "maxIterations",
|
|
35
|
+
type: "number",
|
|
36
|
+
default: 5,
|
|
37
|
+
typeOptions: { minValue: 1, maxValue: 100000 },
|
|
38
|
+
description: "Safety cap to prevent infinite loops",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
displayName: "Condition Expression",
|
|
42
|
+
name: "conditionExpression",
|
|
43
|
+
type: "string",
|
|
44
|
+
typeOptions: { rows: 2 },
|
|
45
|
+
default: "={{$json.counter < 5}}",
|
|
46
|
+
description: "n8n expression that must evaluate to true to continue looping. You can use {{$loop.iteration}}.",
|
|
47
|
+
displayOptions: { show: { mode: ["expression"] } },
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
displayName: "Add Loop Metadata",
|
|
51
|
+
name: "addMeta",
|
|
52
|
+
type: "boolean",
|
|
53
|
+
default: true,
|
|
54
|
+
description: "Whether to add loop info (loopIteration, loopFirst, loopMax) to each item",
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async execute() {
|
|
60
|
+
const items = this.getInputData();
|
|
61
|
+
const mode = this.getNodeParameter("mode", 0, "counter");
|
|
62
|
+
const maxIterations = this.getNodeParameter("maxIterations", 0, 5);
|
|
63
|
+
const addMeta = this.getNodeParameter("addMeta", 0, true);
|
|
64
|
+
const conditionExpression = mode === "expression"
|
|
65
|
+
? this.getNodeParameter("conditionExpression", 0, "")
|
|
66
|
+
: "";
|
|
67
|
+
const state = this.getWorkflowStaticData("execution");
|
|
68
|
+
const nodeKey = this.getNode().id;
|
|
69
|
+
state.__whileLoop = state.__whileLoop || {};
|
|
70
|
+
const loopState = state.__whileLoop[nodeKey] || { iteration: 0 };
|
|
71
|
+
const continueItems = [];
|
|
72
|
+
const doneItems = [];
|
|
73
|
+
for (let i = 0; i < items.length; i++) {
|
|
74
|
+
let iteration = loopState.iteration || 0;
|
|
75
|
+
let shouldContinue = false;
|
|
76
|
+
if (mode === "counter") {
|
|
77
|
+
shouldContinue = iteration < maxIterations;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
try {
|
|
81
|
+
const exprResult = this.evaluateExpression(conditionExpression, i, items[i], { $loop: { iteration } });
|
|
82
|
+
shouldContinue = Boolean(exprResult) && iteration < maxIterations;
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
throw new NodeApiError(this.getNode(), error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (shouldContinue) {
|
|
89
|
+
const newItem = { ...items[i] };
|
|
90
|
+
if (addMeta) {
|
|
91
|
+
newItem.json = {
|
|
92
|
+
...newItem.json,
|
|
93
|
+
loopIteration: iteration,
|
|
94
|
+
loopFirst: iteration === 0,
|
|
95
|
+
loopMax: maxIterations,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
continueItems.push(newItem);
|
|
99
|
+
loopState.iteration = iteration + 1;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// reset state for next execution
|
|
103
|
+
delete state.__whileLoop[nodeKey];
|
|
104
|
+
doneItems.push(items[i]);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (continueItems.length) {
|
|
108
|
+
state.__whileLoop[nodeKey] = loopState;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
delete state.__whileLoop[nodeKey];
|
|
112
|
+
}
|
|
113
|
+
return [continueItems, doneItems];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
exports.WhileLoop = WhileLoop;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g clip-path="url(#clip0_23_4)">
|
|
3
|
+
<path d="M12.92 41.2178C14.89 44.5412 17.735 47.2218 21.1251 48.9486C24.5151 50.6753 28.3113 51.3776 32.0735 50.974C35.8356 50.5703 39.4094 49.0772 42.3806 46.6679C45.3517 44.2586 47.5984 41.0317 48.86 37.3616H55.8267C54.5483 42.4471 51.8794 47.0485 48.1365 50.6198C44.3937 54.1911 39.734 56.5823 34.7104 57.5098C29.6868 58.4373 24.5102 57.8621 19.7947 55.8524C15.0791 53.8428 11.0227 50.4831 8.10667 46.1719L0 54.5156V33.9308H20L12.92 41.2178ZM47.0833 19.7787C45.1123 16.4549 42.2663 13.7741 38.8752 12.0473C35.4841 10.3205 31.687 9.61836 27.924 10.0224C24.161 10.4264 20.5863 11.9199 17.6145 14.3299C14.6427 16.7398 12.3954 19.9674 11.1333 23.6384H4.16667C5.44454 18.5509 8.11366 13.9475 11.8573 10.3745C15.6009 6.80149 20.2619 4.4089 25.2871 3.48066C30.3123 2.55242 35.4908 3.12749 40.208 5.13763C44.9252 7.14777 48.983 10.5086 51.9 14.8212L60 6.4844V27.0692H40L47.0833 19.7787Z" fill="#983030"/>
|
|
4
|
+
</g>
|
|
5
|
+
<defs>
|
|
6
|
+
<clipPath id="clip0_23_4">
|
|
7
|
+
<rect width="60" height="55" fill="white" transform="translate(0 3)"/>
|
|
8
|
+
</clipPath>
|
|
9
|
+
</defs>
|
|
10
|
+
</svg>
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-while-loop",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Simple while-loop control node for n8n with counter or expression conditions",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"loop",
|
|
8
|
+
"control",
|
|
9
|
+
"while"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"author": {
|
|
13
|
+
"name": "Custom integration scaffold"
|
|
14
|
+
},
|
|
15
|
+
"main": "dist/index.js",
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18.0.0"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"n8n-core": "*",
|
|
24
|
+
"n8n-workflow": "*"
|
|
25
|
+
},
|
|
26
|
+
"n8n": {
|
|
27
|
+
"n8nNodesApiVersion": 1,
|
|
28
|
+
"nodes": [
|
|
29
|
+
"dist/nodes/WhileLoop/WhileLoop.node.js"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
}
|