n8n-mcp 2.65.2 → 2.66.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/mcp/handlers-n8n-manager.d.ts.map +1 -1
- package/dist/mcp/handlers-n8n-manager.js +38 -8
- package/dist/mcp/handlers-n8n-manager.js.map +1 -1
- package/dist/mcp/handlers-workflow-diff.d.ts.map +1 -1
- package/dist/mcp/handlers-workflow-diff.js +18 -3
- package/dist/mcp/handlers-workflow-diff.js.map +1 -1
- package/dist/mcp/tool-docs/workflow_management/n8n-create-workflow.d.ts.map +1 -1
- package/dist/mcp/tool-docs/workflow_management/n8n-create-workflow.js +2 -1
- package/dist/mcp/tool-docs/workflow_management/n8n-create-workflow.js.map +1 -1
- package/dist/mcp/tool-docs/workflow_management/n8n-get-workflow.d.ts.map +1 -1
- package/dist/mcp/tool-docs/workflow_management/n8n-get-workflow.js +5 -3
- package/dist/mcp/tool-docs/workflow_management/n8n-get-workflow.js.map +1 -1
- package/dist/mcp/tool-docs/workflow_management/n8n-update-full-workflow.d.ts.map +1 -1
- package/dist/mcp/tool-docs/workflow_management/n8n-update-full-workflow.js +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-update-full-workflow.js.map +1 -1
- package/dist/mcp/tool-docs/workflow_management/n8n-update-partial-workflow.d.ts.map +1 -1
- package/dist/mcp/tool-docs/workflow_management/n8n-update-partial-workflow.js +11 -4
- package/dist/mcp/tool-docs/workflow_management/n8n-update-partial-workflow.js.map +1 -1
- package/dist/mcp/tools-n8n-manager.d.ts.map +1 -1
- package/dist/mcp/tools-n8n-manager.js +29 -1
- package/dist/mcp/tools-n8n-manager.js.map +1 -1
- package/dist/services/n8n-api-client.d.ts +11 -2
- package/dist/services/n8n-api-client.d.ts.map +1 -1
- package/dist/services/n8n-api-client.js +114 -16
- package/dist/services/n8n-api-client.js.map +1 -1
- package/dist/services/n8n-validation.d.ts.map +1 -1
- package/dist/services/n8n-validation.js +2 -0
- package/dist/services/n8n-validation.js.map +1 -1
- package/dist/services/node-groups.d.ts +60 -0
- package/dist/services/node-groups.d.ts.map +1 -0
- package/dist/services/node-groups.js +237 -0
- package/dist/services/node-groups.js.map +1 -0
- package/dist/services/workflow-diff-engine.d.ts +6 -0
- package/dist/services/workflow-diff-engine.d.ts.map +1 -1
- package/dist/services/workflow-diff-engine.js +132 -4
- package/dist/services/workflow-diff-engine.js.map +1 -1
- package/dist/services/workflow-validator.d.ts +7 -0
- package/dist/services/workflow-validator.d.ts.map +1 -1
- package/dist/services/workflow-validator.js +20 -0
- package/dist/services/workflow-validator.js.map +1 -1
- package/dist/services/workflow-versioning-service.d.ts +2 -0
- package/dist/services/workflow-versioning-service.d.ts.map +1 -1
- package/dist/services/workflow-versioning-service.js +11 -3
- package/dist/services/workflow-versioning-service.js.map +1 -1
- package/dist/telemetry/intent-classifier.d.ts.map +1 -1
- package/dist/telemetry/intent-classifier.js +1 -0
- package/dist/telemetry/intent-classifier.js.map +1 -1
- package/dist/telemetry/mutation-tracker.d.ts.map +1 -1
- package/dist/telemetry/mutation-tracker.js +5 -0
- package/dist/telemetry/mutation-tracker.js.map +1 -1
- package/dist/types/n8n-api.d.ts +10 -0
- package/dist/types/n8n-api.d.ts.map +1 -1
- package/dist/types/n8n-api.js.map +1 -1
- package/dist/types/workflow-diff.d.ts +13 -2
- package/dist/types/workflow-diff.d.ts.map +1 -1
- package/dist/types/workflow-diff.js +1 -1
- package/dist/types/workflow-diff.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.nodeGroupInputSchema = exports.GROUP_DESCRIPTION_MAX_LENGTH = void 0;
|
|
4
|
+
exports.parseNodeGroupsInput = parseNodeGroupsInput;
|
|
5
|
+
exports.toWorkflowNodeGroup = toWorkflowNodeGroup;
|
|
6
|
+
exports.nodeGroupsField = nodeGroupsField;
|
|
7
|
+
exports.sanitizeGroupsForApi = sanitizeGroupsForApi;
|
|
8
|
+
exports.repairNodeGroups = repairNodeGroups;
|
|
9
|
+
exports.checkNodeGroups = checkNodeGroups;
|
|
10
|
+
exports.dropRejectedGroup = dropRejectedGroup;
|
|
11
|
+
exports.classifyGroupError = classifyGroupError;
|
|
12
|
+
const zod_1 = require("zod");
|
|
13
|
+
const uuid_1 = require("uuid");
|
|
14
|
+
const mcp_input_normalizer_1 = require("../utils/mcp-input-normalizer");
|
|
15
|
+
exports.GROUP_DESCRIPTION_MAX_LENGTH = 155;
|
|
16
|
+
exports.nodeGroupInputSchema = zod_1.z.object({
|
|
17
|
+
id: zod_1.z.string().trim().min(1).optional(),
|
|
18
|
+
name: zod_1.z.string().trim().min(1),
|
|
19
|
+
nodeIds: zod_1.z.array(zod_1.z.string().trim().min(1)).min(1),
|
|
20
|
+
description: zod_1.z.string().trim().max(exports.GROUP_DESCRIPTION_MAX_LENGTH).optional()
|
|
21
|
+
});
|
|
22
|
+
function parseNodeGroupsInput(value) {
|
|
23
|
+
if (value === undefined || value === null)
|
|
24
|
+
return undefined;
|
|
25
|
+
const groups = zod_1.z.array(exports.nodeGroupInputSchema).parse((0, mcp_input_normalizer_1.normalizeMcpJsonValue)(value));
|
|
26
|
+
return groups.map(toWorkflowNodeGroup);
|
|
27
|
+
}
|
|
28
|
+
function toWorkflowNodeGroup(group) {
|
|
29
|
+
const normalized = {
|
|
30
|
+
id: group.id?.trim() || (0, uuid_1.v4)(),
|
|
31
|
+
name: group.name.trim(),
|
|
32
|
+
nodeIds: group.nodeIds
|
|
33
|
+
};
|
|
34
|
+
const description = typeof group.description === 'string' ? group.description.trim() : '';
|
|
35
|
+
if (description)
|
|
36
|
+
normalized.description = description;
|
|
37
|
+
return normalized;
|
|
38
|
+
}
|
|
39
|
+
function isGroupLike(value) {
|
|
40
|
+
if (typeof value !== 'object' || value === null)
|
|
41
|
+
return false;
|
|
42
|
+
const group = value;
|
|
43
|
+
return typeof group.id === 'string' && typeof group.name === 'string' && Array.isArray(group.nodeIds);
|
|
44
|
+
}
|
|
45
|
+
function groupLabel(group) {
|
|
46
|
+
return group.name?.trim() ? group.name : group.id;
|
|
47
|
+
}
|
|
48
|
+
function nodeGroupsField(groups) {
|
|
49
|
+
return Array.isArray(groups) && groups.length > 0 ? { nodeGroups: groups } : {};
|
|
50
|
+
}
|
|
51
|
+
function sanitizeGroupsForApi(groups, options) {
|
|
52
|
+
if (!Array.isArray(groups))
|
|
53
|
+
return [];
|
|
54
|
+
return groups.filter(isGroupLike).map(group => {
|
|
55
|
+
const sanitized = {
|
|
56
|
+
id: group.id,
|
|
57
|
+
name: group.name,
|
|
58
|
+
nodeIds: group.nodeIds.filter(id => typeof id === 'string')
|
|
59
|
+
};
|
|
60
|
+
if (options.includeDescription && typeof group.description === 'string') {
|
|
61
|
+
const description = group.description.trim();
|
|
62
|
+
if (description)
|
|
63
|
+
sanitized.description = description;
|
|
64
|
+
}
|
|
65
|
+
return sanitized;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function repairNodeGroups(workflow, options = {}) {
|
|
69
|
+
const groups = workflow.nodeGroups;
|
|
70
|
+
if (!Array.isArray(groups) || groups.length === 0) {
|
|
71
|
+
return { nodeGroups: groups, issues: [] };
|
|
72
|
+
}
|
|
73
|
+
const knownIds = new Set((workflow.nodes ?? []).map(node => node?.id).filter((id) => typeof id === 'string'));
|
|
74
|
+
const authored = options.authoredGroups ?? new Set();
|
|
75
|
+
const issues = [];
|
|
76
|
+
const repaired = [];
|
|
77
|
+
const errors = [];
|
|
78
|
+
let changed = false;
|
|
79
|
+
for (const group of groups) {
|
|
80
|
+
if (!isGroupLike(group)) {
|
|
81
|
+
changed = true;
|
|
82
|
+
issues.push({
|
|
83
|
+
code: 'group-malformed',
|
|
84
|
+
group: 'unknown',
|
|
85
|
+
message: 'A canvas group was dropped because it is missing an id, a name, or its member list.'
|
|
86
|
+
});
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const label = groupLabel(group);
|
|
90
|
+
const keptIds = group.nodeIds.filter(id => typeof id === 'string' && knownIds.has(id));
|
|
91
|
+
const removedCount = group.nodeIds.length - keptIds.length;
|
|
92
|
+
if (removedCount > 0 && authored.has(group.name)) {
|
|
93
|
+
const missing = group.nodeIds.filter(id => !keptIds.includes(id));
|
|
94
|
+
errors.push(`Node group "${label}" references ${missing.length === 1 ? 'node' : 'nodes'} ${missing
|
|
95
|
+
.map(id => `"${id}"`)
|
|
96
|
+
.join(', ')} that ${missing.length === 1 ? 'is' : 'are'} not in the workflow.`);
|
|
97
|
+
repaired.push(group);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (keptIds.length === 0) {
|
|
101
|
+
changed = true;
|
|
102
|
+
issues.push({
|
|
103
|
+
code: 'group-empty',
|
|
104
|
+
group: label,
|
|
105
|
+
message: `Node group "${label}" was removed because none of its nodes are left in the workflow.`
|
|
106
|
+
});
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (removedCount > 0) {
|
|
110
|
+
changed = true;
|
|
111
|
+
issues.push({
|
|
112
|
+
code: 'group-member-removed',
|
|
113
|
+
group: label,
|
|
114
|
+
message: `Node group "${label}" lost ${removedCount} member${removedCount === 1 ? '' : 's'} that no longer exist in the workflow; the group was kept with its remaining ${keptIds.length} node${keptIds.length === 1 ? '' : 's'}.`
|
|
115
|
+
});
|
|
116
|
+
repaired.push({ ...group, nodeIds: keptIds });
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
repaired.push(group);
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
nodeGroups: changed ? repaired : groups,
|
|
123
|
+
issues,
|
|
124
|
+
errors: errors.length > 0 ? errors : undefined
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function checkNodeGroups(workflow, options = {}) {
|
|
128
|
+
const groups = workflow.nodeGroups;
|
|
129
|
+
if (!Array.isArray(groups) || groups.length === 0)
|
|
130
|
+
return [];
|
|
131
|
+
const issues = [];
|
|
132
|
+
const nodesById = new Map();
|
|
133
|
+
for (const node of workflow.nodes ?? []) {
|
|
134
|
+
if (node?.id)
|
|
135
|
+
nodesById.set(node.id, node);
|
|
136
|
+
}
|
|
137
|
+
const seenNames = new Set();
|
|
138
|
+
const groupByNodeId = new Map();
|
|
139
|
+
for (const group of groups) {
|
|
140
|
+
if (!isGroupLike(group))
|
|
141
|
+
continue;
|
|
142
|
+
const label = groupLabel(group);
|
|
143
|
+
if (seenNames.has(group.name)) {
|
|
144
|
+
issues.push({
|
|
145
|
+
code: 'group-duplicate-name',
|
|
146
|
+
group: label,
|
|
147
|
+
message: `Two node groups are named "${group.name}"; n8n requires group names to be unique.`
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
seenNames.add(group.name);
|
|
151
|
+
if (group.nodeIds.length === 0) {
|
|
152
|
+
issues.push({
|
|
153
|
+
code: 'group-empty',
|
|
154
|
+
group: label,
|
|
155
|
+
message: `Node group "${label}" has no members; n8n rejects empty groups.`
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
for (const nodeId of group.nodeIds) {
|
|
159
|
+
const node = nodesById.get(nodeId);
|
|
160
|
+
if (!node) {
|
|
161
|
+
issues.push({
|
|
162
|
+
code: 'group-member-removed',
|
|
163
|
+
group: label,
|
|
164
|
+
message: `Node group "${label}" references node ID "${nodeId}", which is not in the workflow.`
|
|
165
|
+
});
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
const owner = groupByNodeId.get(nodeId);
|
|
169
|
+
if (owner) {
|
|
170
|
+
issues.push({
|
|
171
|
+
code: 'group-node-in-multiple-groups',
|
|
172
|
+
group: label,
|
|
173
|
+
message: `Node "${node.name}" is in both "${owner}" and "${label}"; a node can only belong to one group.`
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
groupByNodeId.set(nodeId, label);
|
|
178
|
+
}
|
|
179
|
+
if (options.isTrigger?.(node)) {
|
|
180
|
+
issues.push({
|
|
181
|
+
code: 'group-contains-trigger',
|
|
182
|
+
group: label,
|
|
183
|
+
message: `Node group "${label}" contains trigger node "${node.name}"; n8n does not allow triggers inside a group.`
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return issues;
|
|
189
|
+
}
|
|
190
|
+
function dropRejectedGroup(groups, target) {
|
|
191
|
+
const byId = target.groupId ? groups.findIndex(group => group.id === target.groupId) : -1;
|
|
192
|
+
const index = byId !== -1
|
|
193
|
+
? byId
|
|
194
|
+
: target.groupName
|
|
195
|
+
? groups.findIndex(group => group.name === target.groupName)
|
|
196
|
+
: -1;
|
|
197
|
+
if (index === -1)
|
|
198
|
+
return { groups, dropped: null };
|
|
199
|
+
return { groups: groups.filter((_, i) => i !== index), dropped: groups[index] };
|
|
200
|
+
}
|
|
201
|
+
function classifyGroupError(error) {
|
|
202
|
+
const apiError = error;
|
|
203
|
+
const message = typeof apiError?.message === 'string' ? apiError.message : '';
|
|
204
|
+
if (!apiError || apiError.statusCode !== 400) {
|
|
205
|
+
return { kind: 'unrelated', message };
|
|
206
|
+
}
|
|
207
|
+
let detailsText = '';
|
|
208
|
+
try {
|
|
209
|
+
detailsText = apiError.details === undefined ? '' : JSON.stringify(apiError.details);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
detailsText = '';
|
|
213
|
+
}
|
|
214
|
+
const haystack = `${message} ${detailsText}`;
|
|
215
|
+
if (/must NOT have additional propert/i.test(haystack)) {
|
|
216
|
+
const nested = /request\/body\/([A-Za-z0-9_]+)/.exec(haystack);
|
|
217
|
+
if (nested) {
|
|
218
|
+
return nested[1] === 'nodeGroups'
|
|
219
|
+
? { kind: 'schema-description', message }
|
|
220
|
+
: { kind: 'unrelated', message };
|
|
221
|
+
}
|
|
222
|
+
return { kind: 'schema-field', message };
|
|
223
|
+
}
|
|
224
|
+
const identified = /(?:node group|group)\s+"(.+?)"\s*\(([^)]+)\)/i.exec(haystack);
|
|
225
|
+
if (identified) {
|
|
226
|
+
return { kind: 'semantic', groupName: identified[1], groupId: identified[2], message };
|
|
227
|
+
}
|
|
228
|
+
const named = /(?:node group|group)\s+"([^"]+)"/i.exec(haystack);
|
|
229
|
+
if (named) {
|
|
230
|
+
return { kind: 'semantic', groupName: named[1], message };
|
|
231
|
+
}
|
|
232
|
+
if (/nodeGroups/.test(haystack)) {
|
|
233
|
+
return { kind: 'semantic', message };
|
|
234
|
+
}
|
|
235
|
+
return { kind: 'unrelated', message };
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=node-groups.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-groups.js","sourceRoot":"","sources":["../../src/services/node-groups.ts"],"names":[],"mappings":";;;AA6DA,oDAIC;AAOD,kDAUC;AAiDD,0CAIC;AAOD,oDAoBC;AAQD,4CAiFC;AAOD,0CAsEC;AAMD,8CAiBC;AAiCD,gDA+DC;AAvaD,6BAAwB;AACxB,+BAAoC;AAEpC,wEAAsE;AAGzD,QAAA,4BAA4B,GAAG,GAAG,CAAC;AASnC,QAAA,oBAAoB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC3C,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACvC,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,OAAO,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACjD,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,oCAA4B,CAAC,CAAC,QAAQ,EAAE;CAC5E,CAAC,CAAC;AAiBH,SAAgB,oBAAoB,CAAC,KAAc;IACjD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5D,MAAM,MAAM,GAAG,OAAC,CAAC,KAAK,CAAC,4BAAoB,CAAC,CAAC,KAAK,CAAC,IAAA,4CAAqB,EAAC,KAAK,CAAC,CAAC,CAAC;IACjF,OAAO,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;AACzC,CAAC;AAOD,SAAgB,mBAAmB,CAAC,KAAqB;IACvD,MAAM,UAAU,GAAsB;QACpC,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,IAAA,SAAM,GAAE;QAChC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE;QACvB,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC;IAEF,MAAM,WAAW,GAAG,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1F,IAAI,WAAW;QAAE,UAAU,CAAC,WAAW,GAAG,WAAW,CAAC;IACtD,OAAO,UAAU,CAAC;AACpB,CAAC;AAkCD,SAAS,WAAW,CAAC,KAAc;IACjC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,KAAK,GAAG,KAAgC,CAAC;IAC/C,OAAO,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AACxG,CAAC;AAGD,SAAS,UAAU,CAAC,KAAwB;IAC1C,OAAO,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;AACpD,CAAC;AAMD,SAAgB,eAAe,CAC7B,MAAuC;IAEvC,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAClF,CAAC;AAOD,SAAgB,oBAAoB,CAClC,MAAe,EACf,OAAwC;IAExC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAC;IAEtC,OAAO,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;QAC5C,MAAM,SAAS,GAAsB;YACnC,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,CAAC;SAC5D,CAAC;QAGF,IAAI,OAAO,CAAC,kBAAkB,IAAI,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ,EAAE,CAAC;YACxE,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;YAC7C,IAAI,WAAW;gBAAE,SAAS,CAAC,WAAW,GAAG,WAAW,CAAC;QACvD,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC,CAAC;AACL,CAAC;AAQD,SAAgB,gBAAgB,CAC9B,QAAgD,EAChD,UAOI,EAAE;IAEN,MAAM,MAAM,GAAG,QAAQ,CAAC,UAAU,CAAC;IACnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClD,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC5C,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,GAAG,CACtB,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,CAAC,CAClG,CAAC;IACF,MAAM,QAAQ,GAAG,OAAO,CAAC,cAAc,IAAI,IAAI,GAAG,EAAU,CAAC;IAC7D,MAAM,MAAM,GAAqB,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAwB,EAAE,CAAC;IACzC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO,GAAG,IAAI,CAAC;YACf,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,iBAAiB;gBACvB,KAAK,EAAE,SAAS;gBAChB,OAAO,EACL,qFAAqF;aACxF,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAChC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,IAAI,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACvF,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAE3D,IAAI,YAAY,GAAG,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACjD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;YAClE,MAAM,CAAC,IAAI,CACT,eAAe,KAAK,gBAAgB,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,OAAO;iBACnF,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC;iBACpB,IAAI,CAAC,IAAI,CAAC,SAAS,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,uBAAuB,CACjF,CAAC;YACF,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrB,SAAS;QACX,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,GAAG,IAAI,CAAC;YACf,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,aAAa;gBACnB,KAAK,EAAE,KAAK;gBACZ,OAAO,EAAE,eAAe,KAAK,mEAAmE;aACjG,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO,GAAG,IAAI,CAAC;YACf,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,sBAAsB;gBAC5B,KAAK,EAAE,KAAK;gBACZ,OAAO,EAAE,eAAe,KAAK,UAAU,YAAY,UAAU,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,gFAAgF,OAAO,CAAC,MAAM,QAAQ,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG;aACnO,CAAC,CAAC;YACH,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;YAC9C,SAAS;QACX,CAAC;QAED,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IAED,OAAO;QACL,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM;QACvC,MAAM;QACN,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;KAC/C,CAAC;AACJ,CAAC;AAOD,SAAgB,eAAe,CAC7B,QAAgD,EAChD,UAA2D,EAAE;IAE7D,MAAM,MAAM,GAAG,QAAQ,CAAC,UAAU,CAAC;IACnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAE7D,MAAM,MAAM,GAAqB,EAAE,CAAC;IACpC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAwB,CAAC;IAClD,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;QACxC,IAAI,IAAI,EAAE,EAAE;YAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEhD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;YAAE,SAAS;QAClC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAEhC,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,sBAAsB;gBAC5B,KAAK,EAAE,KAAK;gBACZ,OAAO,EAAE,8BAA8B,KAAK,CAAC,IAAI,2CAA2C;aAC7F,CAAC,CAAC;QACL,CAAC;QACD,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAE1B,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,aAAa;gBACnB,KAAK,EAAE,KAAK;gBACZ,OAAO,EAAE,eAAe,KAAK,6CAA6C;aAC3E,CAAC,CAAC;QACL,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACnC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,sBAAsB;oBAC5B,KAAK,EAAE,KAAK;oBACZ,OAAO,EAAE,eAAe,KAAK,yBAAyB,MAAM,kCAAkC;iBAC/F,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACxC,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,+BAA+B;oBACrC,KAAK,EAAE,KAAK;oBACZ,OAAO,EAAE,SAAS,IAAI,CAAC,IAAI,iBAAiB,KAAK,UAAU,KAAK,yCAAyC;iBAC1G,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YACnC,CAAC;YAED,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9B,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,wBAAwB;oBAC9B,KAAK,EAAE,KAAK;oBACZ,OAAO,EAAE,eAAe,KAAK,4BAA4B,IAAI,CAAC,IAAI,gDAAgD;iBACnH,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAMD,SAAgB,iBAAiB,CAC/B,MAA2B,EAC3B,MAAgD;IAMhD,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1F,MAAM,KAAK,GACT,IAAI,KAAK,CAAC,CAAC;QACT,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,MAAM,CAAC,SAAS;YAChB,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,SAAS,CAAC;YAC5D,CAAC,CAAC,CAAC,CAAC,CAAC;IACX,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACnD,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AAClF,CAAC;AAiCD,SAAgB,kBAAkB,CAAC,KAAc;IAC/C,MAAM,QAAQ,GAAG,KAA4E,CAAC;IAC9F,MAAM,OAAO,GAAG,OAAO,QAAQ,EAAE,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAK9E,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;QAC7C,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;IACxC,CAAC;IAED,IAAI,WAAW,GAAG,EAAE,CAAC;IACrB,IAAI,CAAC;QACH,WAAW,GAAG,QAAQ,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACvF,CAAC;IAAC,MAAM,CAAC;QACP,WAAW,GAAG,EAAE,CAAC;IACnB,CAAC;IACD,MAAM,QAAQ,GAAG,GAAG,OAAO,IAAI,WAAW,EAAE,CAAC;IAa7C,IAAI,mCAAmC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvD,MAAM,MAAM,GAAG,gCAAgC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/D,IAAI,MAAM,EAAE,CAAC;YAGX,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,YAAY;gBAC/B,CAAC,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE;gBACzC,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;QACrC,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC;IAC3C,CAAC;IAUD,MAAM,UAAU,GAAG,+CAA+C,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClF,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;IACzF,CAAC;IACD,MAAM,KAAK,GAAG,mCAAmC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjE,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5D,CAAC;IACD,IAAI,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;IACvC,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;AACxC,CAAC"}
|
|
@@ -8,6 +8,7 @@ export declare class WorkflowDiffEngine {
|
|
|
8
8
|
private tagsToAdd;
|
|
9
9
|
private tagsToRemove;
|
|
10
10
|
private transferToProjectId;
|
|
11
|
+
private authoredGroupNames;
|
|
11
12
|
applyDiff(workflow: Workflow, request: WorkflowDiffRequest): Promise<WorkflowDiffResult>;
|
|
12
13
|
private validateOperation;
|
|
13
14
|
private applyOperation;
|
|
@@ -33,6 +34,11 @@ export declare class WorkflowDiffEngine {
|
|
|
33
34
|
private applyRewireConnection;
|
|
34
35
|
private applyUpdateSettings;
|
|
35
36
|
private applyUpdateName;
|
|
37
|
+
private validateSetNodeGroups;
|
|
38
|
+
private resolveGroupMembers;
|
|
39
|
+
private applySetNodeGroups;
|
|
40
|
+
private authoredGroupNamesOrUndefined;
|
|
41
|
+
private finalizeNodeGroups;
|
|
36
42
|
private applyAddTag;
|
|
37
43
|
private applyRemoveTag;
|
|
38
44
|
private validateActivateWorkflow;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-diff-engine.d.ts","sourceRoot":"","sources":["../../src/services/workflow-diff-engine.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,mBAAmB,EACnB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"workflow-diff-engine.d.ts","sourceRoot":"","sources":["../../src/services/workflow-diff-engine.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,mBAAmB,EACnB,kBAAkB,EAyBnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAuD,MAAM,kBAAkB,CAAC;AAwHjG,qBAAa,kBAAkB;IAE7B,OAAO,CAAC,SAAS,CAAkC;IAEnD,OAAO,CAAC,QAAQ,CAAqC;IAErD,OAAO,CAAC,eAAe,CAAqB;IAE5C,OAAO,CAAC,gBAAgB,CAAqB;IAE7C,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,YAAY,CAAgB;IAEpC,OAAO,CAAC,mBAAmB,CAAqB;IAEhD,OAAO,CAAC,kBAAkB,CAAqB;IAKzC,SAAS,CACb,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC;IA+L9B,OAAO,CAAC,iBAAiB;IA8CzB,OAAO,CAAC,cAAc;IAkEtB,OAAO,CAAC,eAAe;IAwBvB,OAAO,CAAC,kBAAkB;IAuB1B,OAAO,CAAC,kBAAkB;IAmE1B,OAAO,CAAC,sBAAsB;IA8E9B,OAAO,CAAC,gBAAgB;IA4BxB,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,qBAAqB;IAqD7B,OAAO,CAAC,wBAAwB;IA6ChC,OAAO,CAAC,wBAAwB;IA4DhC,OAAO,CAAC,YAAY;IA4BpB,OAAO,CAAC,eAAe;IAwCvB,OAAO,CAAC,eAAe;IAsDvB,OAAO,CAAC,mBAAmB;IAgE3B,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,sBAAsB;IA6D9B,OAAO,CAAC,kBAAkB;IAiD1B,OAAO,CAAC,qBAAqB;IAuC7B,OAAO,CAAC,qBAAqB;IAkF7B,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,qBAAqB;IA+E7B,OAAO,CAAC,mBAAmB;IA+B3B,OAAO,CAAC,kBAAkB;IAuB1B,OAAO,CAAC,6BAA6B;IAWrC,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,wBAAwB;IAchC,OAAO,CAAC,0BAA0B;IAMlC,OAAO,CAAC,qBAAqB;IAQ7B,OAAO,CAAC,uBAAuB;IAM/B,OAAO,CAAC,wBAAwB;IAOhC,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,6BAA6B;IAKrC,OAAO,CAAC,0BAA0B;IA0BlC,OAAO,CAAC,0BAA0B;IA+ElC,OAAO,CAAC,uBAAuB;IAe/B,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,0BAA0B;IAmElC,OAAO,CAAC,iBAAiB;IAsBzB,OAAO,CAAC,QAAQ;IAsChB,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,iBAAiB;CA0C1B"}
|
|
@@ -4,6 +4,7 @@ exports.WorkflowDiffEngine = void 0;
|
|
|
4
4
|
const uuid_1 = require("uuid");
|
|
5
5
|
const workflow_diff_1 = require("../types/workflow-diff");
|
|
6
6
|
const logger_1 = require("../utils/logger");
|
|
7
|
+
const node_groups_1 = require("./node-groups");
|
|
7
8
|
const node_sanitizer_1 = require("./node-sanitizer");
|
|
8
9
|
const node_type_utils_1 = require("../utils/node-type-utils");
|
|
9
10
|
const logger = new logger_1.Logger({ prefix: '[WorkflowDiffEngine]' });
|
|
@@ -82,6 +83,7 @@ class WorkflowDiffEngine {
|
|
|
82
83
|
this.removedNodeNames = new Set();
|
|
83
84
|
this.tagsToAdd = [];
|
|
84
85
|
this.tagsToRemove = [];
|
|
86
|
+
this.authoredGroupNames = new Set();
|
|
85
87
|
}
|
|
86
88
|
async applyDiff(workflow, request) {
|
|
87
89
|
try {
|
|
@@ -92,6 +94,7 @@ class WorkflowDiffEngine {
|
|
|
92
94
|
this.tagsToAdd = [];
|
|
93
95
|
this.tagsToRemove = [];
|
|
94
96
|
this.transferToProjectId = undefined;
|
|
97
|
+
this.authoredGroupNames.clear();
|
|
95
98
|
const workflowCopy = JSON.parse(JSON.stringify(workflow));
|
|
96
99
|
const operationEntries = buildExecutionEntries(request.operations);
|
|
97
100
|
const nodeOperationCount = request.operations.filter(workflow_diff_1.isNodeOperation).length;
|
|
@@ -126,6 +129,7 @@ class WorkflowDiffEngine {
|
|
|
126
129
|
failedIndices.push(index);
|
|
127
130
|
}
|
|
128
131
|
}
|
|
132
|
+
this.finalizeNodeGroups(workflowCopy);
|
|
129
133
|
if (request.validateOnly) {
|
|
130
134
|
return {
|
|
131
135
|
success: errors.length === 0,
|
|
@@ -136,7 +140,8 @@ class WorkflowDiffEngine {
|
|
|
136
140
|
errors: errors.length > 0 ? errors : undefined,
|
|
137
141
|
warnings: this.warnings.length > 0 ? this.warnings : undefined,
|
|
138
142
|
applied: appliedIndices,
|
|
139
|
-
failed: failedIndices
|
|
143
|
+
failed: failedIndices,
|
|
144
|
+
authoredGroupNames: this.authoredGroupNamesOrUndefined()
|
|
140
145
|
};
|
|
141
146
|
}
|
|
142
147
|
const shouldActivate = workflowCopy._shouldActivate === true;
|
|
@@ -157,7 +162,8 @@ class WorkflowDiffEngine {
|
|
|
157
162
|
shouldDeactivate: shouldDeactivate || undefined,
|
|
158
163
|
tagsToAdd: this.tagsToAdd.length > 0 ? this.tagsToAdd : undefined,
|
|
159
164
|
tagsToRemove: this.tagsToRemove.length > 0 ? this.tagsToRemove : undefined,
|
|
160
|
-
transferToProjectId: this.transferToProjectId || undefined
|
|
165
|
+
transferToProjectId: this.transferToProjectId || undefined,
|
|
166
|
+
authoredGroupNames: this.authoredGroupNamesOrUndefined()
|
|
161
167
|
};
|
|
162
168
|
}
|
|
163
169
|
else {
|
|
@@ -197,11 +203,14 @@ class WorkflowDiffEngine {
|
|
|
197
203
|
});
|
|
198
204
|
logger.debug(`Sanitized ${this.modifiedNodeIds.size} modified nodes`);
|
|
199
205
|
}
|
|
206
|
+
this.finalizeNodeGroups(workflowCopy);
|
|
200
207
|
if (request.validateOnly) {
|
|
201
208
|
return {
|
|
202
209
|
success: true,
|
|
203
210
|
workflow: workflowCopy,
|
|
204
|
-
message: 'Validation successful. Operations are valid but not applied.'
|
|
211
|
+
message: 'Validation successful. Operations are valid but not applied.',
|
|
212
|
+
warnings: this.warnings.length > 0 ? this.warnings : undefined,
|
|
213
|
+
authoredGroupNames: this.authoredGroupNamesOrUndefined()
|
|
205
214
|
};
|
|
206
215
|
}
|
|
207
216
|
const operationsApplied = request.operations.length;
|
|
@@ -219,7 +228,8 @@ class WorkflowDiffEngine {
|
|
|
219
228
|
shouldDeactivate: shouldDeactivate || undefined,
|
|
220
229
|
tagsToAdd: this.tagsToAdd.length > 0 ? this.tagsToAdd : undefined,
|
|
221
230
|
tagsToRemove: this.tagsToRemove.length > 0 ? this.tagsToRemove : undefined,
|
|
222
|
-
transferToProjectId: this.transferToProjectId || undefined
|
|
231
|
+
transferToProjectId: this.transferToProjectId || undefined,
|
|
232
|
+
authoredGroupNames: this.authoredGroupNamesOrUndefined()
|
|
223
233
|
};
|
|
224
234
|
}
|
|
225
235
|
}
|
|
@@ -260,6 +270,8 @@ class WorkflowDiffEngine {
|
|
|
260
270
|
case 'addTag':
|
|
261
271
|
case 'removeTag':
|
|
262
272
|
return null;
|
|
273
|
+
case 'setNodeGroups':
|
|
274
|
+
return this.validateSetNodeGroups(workflow, operation);
|
|
263
275
|
case 'transferWorkflow':
|
|
264
276
|
return this.validateTransferWorkflow(workflow, operation);
|
|
265
277
|
case 'activateWorkflow':
|
|
@@ -312,6 +324,9 @@ class WorkflowDiffEngine {
|
|
|
312
324
|
case 'updateName':
|
|
313
325
|
this.applyUpdateName(workflow, operation);
|
|
314
326
|
break;
|
|
327
|
+
case 'setNodeGroups':
|
|
328
|
+
this.applySetNodeGroups(workflow, operation);
|
|
329
|
+
break;
|
|
315
330
|
case 'addTag':
|
|
316
331
|
this.applyAddTag(workflow, operation);
|
|
317
332
|
break;
|
|
@@ -375,6 +390,9 @@ class WorkflowDiffEngine {
|
|
|
375
390
|
if (!node) {
|
|
376
391
|
return this.formatNodeNotFoundError(workflow, operation.nodeId || operation.nodeName || '', 'updateNode');
|
|
377
392
|
}
|
|
393
|
+
if ('id' in operation.updates && operation.updates.id !== node.id) {
|
|
394
|
+
return `Cannot change the id of node "${node.name}": node IDs are immutable because canvas groups and pinned data reference them. Remove and re-add the node instead.`;
|
|
395
|
+
}
|
|
378
396
|
if (operation.updates.name && operation.updates.name !== node.name) {
|
|
379
397
|
const normalizedNewName = this.normalizeNodeName(operation.updates.name);
|
|
380
398
|
const normalizedCurrentName = this.normalizeNodeName(node.name);
|
|
@@ -420,6 +438,9 @@ class WorkflowDiffEngine {
|
|
|
420
438
|
if (pathSegments.some(k => DANGEROUS_PATH_KEYS.has(k))) {
|
|
421
439
|
return `patchNodeField: fieldPath "${operation.fieldPath}" contains a forbidden key (__proto__, constructor, or prototype)`;
|
|
422
440
|
}
|
|
441
|
+
if (pathSegments[0] === 'id') {
|
|
442
|
+
return `Cannot patch the id of a node: node IDs are immutable because canvas groups and pinned data reference them. Remove and re-add the node instead.`;
|
|
443
|
+
}
|
|
423
444
|
if (!Array.isArray(operation.patches) || operation.patches.length === 0) {
|
|
424
445
|
return `patchNodeField requires a non-empty "patches" array of {find, replace} objects`;
|
|
425
446
|
}
|
|
@@ -904,6 +925,113 @@ class WorkflowDiffEngine {
|
|
|
904
925
|
applyUpdateName(workflow, operation) {
|
|
905
926
|
workflow.name = operation.name;
|
|
906
927
|
}
|
|
928
|
+
validateSetNodeGroups(workflow, operation) {
|
|
929
|
+
const groups = operation.nodeGroups;
|
|
930
|
+
if (!Array.isArray(groups)) {
|
|
931
|
+
return `setNodeGroups requires a "nodeGroups" array. Pass every group you want to keep, or [] to ungroup everything. Example: {type: "setNodeGroups", nodeGroups: [{name: "Enrich lead", nodeNames: ["Fetch company", "Score lead"]}]}`;
|
|
932
|
+
}
|
|
933
|
+
const seenNames = new Set();
|
|
934
|
+
const claimedNodes = new Map();
|
|
935
|
+
for (const group of groups) {
|
|
936
|
+
if (!group || typeof group !== 'object') {
|
|
937
|
+
return `setNodeGroups: every entry must be an object like {name: "Enrich lead", nodeNames: ["Fetch company", "Score lead"]}`;
|
|
938
|
+
}
|
|
939
|
+
const name = typeof group.name === 'string' ? group.name.trim() : '';
|
|
940
|
+
if (!name) {
|
|
941
|
+
return `setNodeGroups: every group needs a non-empty "name"`;
|
|
942
|
+
}
|
|
943
|
+
if (seenNames.has(name)) {
|
|
944
|
+
return `setNodeGroups: duplicate group name "${name}". n8n requires group names to be unique.`;
|
|
945
|
+
}
|
|
946
|
+
seenNames.add(name);
|
|
947
|
+
if (group.id !== undefined && (typeof group.id !== 'string' || !group.id.trim())) {
|
|
948
|
+
return `setNodeGroups: group "${name}" has a non-string "id". Omit it to have one generated.`;
|
|
949
|
+
}
|
|
950
|
+
if (group.description !== undefined) {
|
|
951
|
+
if (typeof group.description !== 'string') {
|
|
952
|
+
return `setNodeGroups: group "${name}" has a non-string "description"`;
|
|
953
|
+
}
|
|
954
|
+
if (group.description.trim().length > node_groups_1.GROUP_DESCRIPTION_MAX_LENGTH) {
|
|
955
|
+
return `setNodeGroups: group "${name}" has a description of ${group.description.trim().length} characters; n8n allows at most ${node_groups_1.GROUP_DESCRIPTION_MAX_LENGTH}.`;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
const hasNames = Array.isArray(group.nodeNames) && group.nodeNames.length > 0;
|
|
959
|
+
const hasIds = Array.isArray(group.nodeIds) && group.nodeIds.length > 0;
|
|
960
|
+
if (hasNames === hasIds) {
|
|
961
|
+
return hasNames
|
|
962
|
+
? `setNodeGroups: group "${name}" sets both "nodeNames" and "nodeIds" — use one or the other`
|
|
963
|
+
: `setNodeGroups: group "${name}" needs members in "nodeNames" (or "nodeIds")`;
|
|
964
|
+
}
|
|
965
|
+
const members = hasIds ? group.nodeIds : group.nodeNames;
|
|
966
|
+
const badIndex = members.findIndex(member => typeof member !== 'string' || !member.trim());
|
|
967
|
+
if (badIndex !== -1) {
|
|
968
|
+
return `setNodeGroups: group "${name}" has a member that is not a node ${hasIds ? 'ID' : 'name'} (${JSON.stringify(members[badIndex])})`;
|
|
969
|
+
}
|
|
970
|
+
const resolvedMembers = this.resolveGroupMembers(workflow, group);
|
|
971
|
+
if (typeof resolvedMembers === 'string')
|
|
972
|
+
return resolvedMembers;
|
|
973
|
+
for (const node of resolvedMembers) {
|
|
974
|
+
const owner = claimedNodes.get(node.id);
|
|
975
|
+
if (owner && owner !== name) {
|
|
976
|
+
return `setNodeGroups: node "${node.name}" is in both "${owner}" and "${name}" — a node can only belong to one group`;
|
|
977
|
+
}
|
|
978
|
+
claimedNodes.set(node.id, name);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
return null;
|
|
982
|
+
}
|
|
983
|
+
resolveGroupMembers(workflow, group) {
|
|
984
|
+
const label = group.name?.trim() || group.id || 'unnamed group';
|
|
985
|
+
const resolved = [];
|
|
986
|
+
if (Array.isArray(group.nodeIds) && group.nodeIds.length > 0) {
|
|
987
|
+
for (const nodeId of group.nodeIds) {
|
|
988
|
+
const node = workflow.nodes.find(n => n.id === nodeId);
|
|
989
|
+
if (!node) {
|
|
990
|
+
return `setNodeGroups: group "${label}" references node ID "${nodeId}", which is not in the workflow. ${this.formatNodeNotFoundError(workflow, nodeId, 'setNodeGroups')}`;
|
|
991
|
+
}
|
|
992
|
+
resolved.push(node);
|
|
993
|
+
}
|
|
994
|
+
return resolved;
|
|
995
|
+
}
|
|
996
|
+
for (const nodeName of group.nodeNames ?? []) {
|
|
997
|
+
const normalized = this.normalizeNodeName(nodeName);
|
|
998
|
+
const node = workflow.nodes.find(n => this.normalizeNodeName(n.name) === normalized);
|
|
999
|
+
if (!node) {
|
|
1000
|
+
return `setNodeGroups: group "${label}" references node "${nodeName}", which is not in the workflow. ${this.formatNodeNotFoundError(workflow, nodeName, 'setNodeGroups')}`;
|
|
1001
|
+
}
|
|
1002
|
+
resolved.push(node);
|
|
1003
|
+
}
|
|
1004
|
+
return resolved;
|
|
1005
|
+
}
|
|
1006
|
+
applySetNodeGroups(workflow, operation) {
|
|
1007
|
+
const groups = [];
|
|
1008
|
+
for (const group of operation.nodeGroups) {
|
|
1009
|
+
const members = this.resolveGroupMembers(workflow, group);
|
|
1010
|
+
if (typeof members === 'string')
|
|
1011
|
+
throw new Error(members);
|
|
1012
|
+
const resolved = (0, node_groups_1.toWorkflowNodeGroup)({
|
|
1013
|
+
id: group.id,
|
|
1014
|
+
name: group.name,
|
|
1015
|
+
nodeIds: [...new Set(members.map(node => node.id))],
|
|
1016
|
+
description: group.description
|
|
1017
|
+
});
|
|
1018
|
+
groups.push(resolved);
|
|
1019
|
+
this.authoredGroupNames.add(resolved.name);
|
|
1020
|
+
}
|
|
1021
|
+
workflow.nodeGroups = groups;
|
|
1022
|
+
}
|
|
1023
|
+
authoredGroupNamesOrUndefined() {
|
|
1024
|
+
return this.authoredGroupNames.size > 0 ? [...this.authoredGroupNames] : undefined;
|
|
1025
|
+
}
|
|
1026
|
+
finalizeNodeGroups(workflow) {
|
|
1027
|
+
if (!Array.isArray(workflow.nodeGroups) || workflow.nodeGroups.length === 0)
|
|
1028
|
+
return;
|
|
1029
|
+
const { nodeGroups, issues } = (0, node_groups_1.repairNodeGroups)(workflow);
|
|
1030
|
+
workflow.nodeGroups = nodeGroups;
|
|
1031
|
+
for (const issue of issues) {
|
|
1032
|
+
this.warnings.push({ operation: -1, message: issue.message });
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
907
1035
|
applyAddTag(workflow, operation) {
|
|
908
1036
|
const removeIdx = this.tagsToRemove.indexOf(operation.tag);
|
|
909
1037
|
if (removeIdx !== -1) {
|