mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-03-27 12:43:12 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47a1cb135d | ||
|
|
14962a39b6 | ||
|
|
f7a1cfe8bf | ||
|
|
65ab94deb2 |
46
CHANGELOG.md
46
CHANGELOG.md
@@ -7,6 +7,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [2.38.0] - 2026-03-20
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **`transferWorkflow` diff operation** (Issue #644): Move workflows between projects via `n8n_update_partial_workflow`
|
||||||
|
- New `transferWorkflow` operation type with `destinationProjectId` parameter
|
||||||
|
- Calls `PUT /workflows/{id}/transfer` via dedicated API after workflow update
|
||||||
|
- Proper error handling: returns `{ success: false, saved: true }` when transfer fails after update
|
||||||
|
- Transfer executes before activation so workflow is in target project first
|
||||||
|
- Zod schema validates `destinationProjectId` is non-empty
|
||||||
|
- Updated tool description and documentation to list the new operation
|
||||||
|
- `inferIntentFromOperations` returns descriptive intent for transfer operations
|
||||||
|
- `N8nApiClient.transferWorkflow()` method added
|
||||||
|
|
||||||
|
Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en
|
||||||
|
|
||||||
|
## [2.37.4] - 2026-03-18
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **Updated n8n dependencies**: n8n 2.11.4 → 2.12.3, n8n-core 2.11.1 → 2.12.0, n8n-workflow 2.11.1 → 2.12.0, @n8n/n8n-nodes-langchain 2.11.2 → 2.12.0
|
||||||
|
- **Rebuilt node database**: 1,239 nodes (809 from n8n-nodes-base and @n8n/n8n-nodes-langchain, 430 community)
|
||||||
|
|
||||||
|
Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en
|
||||||
|
|
||||||
|
## [2.37.3] - 2026-03-15
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **updateNode `name`/`id` field normalization**: LLMs sending `{type: "updateNode", name: "Code", ...}` instead of `nodeName` no longer get "Node not found" errors. The Zod schema now normalizes `name` → `nodeName` and `id` → `nodeId` for node-targeting operations (updateNode, removeNode, moveNode, enableNode, disableNode)
|
||||||
|
- **AI connection types in disconnected-node detection** (Issue #581): Replaced hardcoded 7-type list with dynamic iteration over all connection types present in workflow data. Nodes connected via `ai_outputParser`, `ai_document`, `ai_textSplitter`, `ai_agent`, `ai_chain`, `ai_retriever` are no longer falsely flagged as disconnected during save
|
||||||
|
- **Connection schema and reference validation** (Issue #581): Added `.catchall()` to `workflowConnectionSchema` for unknown AI connection types, and extended connection reference validation to check all connection types (not just `main`)
|
||||||
|
- **autofix `filterOperationsByFixes` ID-vs-name mismatch**: Typeversion-upgrade operations now include `nodeName` alongside `nodeId`, and the filter checks both fields. Previously, `applyFixes=true` silently dropped all typeversion fixes because `fixedNodes` contained names but the filter only checked `nodeId` (UUID)
|
||||||
|
|
||||||
|
Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en
|
||||||
|
|
||||||
|
## [2.37.2] - 2026-03-15
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Code validator `$()` false positive** (Issue #294): `$('Previous Node').first().json` no longer triggers "Invalid $ usage detected" warning. Added `(` and `_` to the regex negative lookahead to support standard n8n cross-node references and valid JS identifiers like `$_var`
|
||||||
|
- **Code validator helper function return false positive** (Issue #293): `function isValid(item) { return false; }` no longer triggers "Cannot return primitive values directly" error. Added helper function detection to skip primitive return checks when named functions or arrow function assignments are present
|
||||||
|
- **Null property removal in diff engine** (Issue #611): `{continueOnFail: null}` no longer causes Zod validation error "Expected boolean, received null". The diff engine now treats `null` values as property deletion (`delete` operator), and documentation updated from `undefined` to `null` for property removal
|
||||||
|
|
||||||
|
Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en
|
||||||
|
|
||||||
## [2.37.1] - 2026-03-14
|
## [2.37.1] - 2026-03-14
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
[](https://www.npmjs.com/package/n8n-mcp)
|
[](https://www.npmjs.com/package/n8n-mcp)
|
||||||
[](https://codecov.io/gh/czlonkowski/n8n-mcp)
|
[](https://codecov.io/gh/czlonkowski/n8n-mcp)
|
||||||
[](https://github.com/czlonkowski/n8n-mcp/actions)
|
[](https://github.com/czlonkowski/n8n-mcp/actions)
|
||||||
[](https://github.com/n8n-io/n8n)
|
[](https://github.com/n8n-io/n8n)
|
||||||
[](https://github.com/czlonkowski/n8n-mcp/pkgs/container/n8n-mcp)
|
[](https://github.com/czlonkowski/n8n-mcp/pkgs/container/n8n-mcp)
|
||||||
[](https://railway.com/deploy/n8n-mcp?referralCode=n8n-mcp)
|
[](https://railway.com/deploy/n8n-mcp?referralCode=n8n-mcp)
|
||||||
|
|
||||||
|
|||||||
BIN
data/nodes.db
BIN
data/nodes.db
Binary file not shown.
2
dist/mcp/handlers-n8n-manager.d.ts.map
vendored
2
dist/mcp/handlers-n8n-manager.d.ts.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"handlers-n8n-manager.d.ts","sourceRoot":"","sources":["../../src/mcp/handlers-n8n-manager.ts"],"names":[],"mappings":"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"}
|
{"version":3,"file":"handlers-n8n-manager.d.ts","sourceRoot":"","sources":["../../src/mcp/handlers-n8n-manager.ts"],"names":[],"mappings":"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"}
|
||||||
9
dist/mcp/handlers-n8n-manager.js
vendored
9
dist/mcp/handlers-n8n-manager.js
vendored
@@ -288,6 +288,15 @@ async function handleCreateWorkflow(args, context) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
const workflow = await client.createWorkflow(input);
|
const workflow = await client.createWorkflow(input);
|
||||||
|
if (!workflow || !workflow.id) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Workflow creation failed: n8n API returned an empty or invalid response. Verify your N8N_API_URL points to the correct /api/v1 endpoint and that the n8n instance supports workflow creation.',
|
||||||
|
details: {
|
||||||
|
response: workflow ? { keys: Object.keys(workflow) } : null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
telemetry_1.telemetry.trackWorkflowCreation(workflow, true);
|
telemetry_1.telemetry.trackWorkflowCreation(workflow, true);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
2
dist/mcp/handlers-n8n-manager.js.map
vendored
2
dist/mcp/handlers-n8n-manager.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/mcp/handlers-workflow-diff.d.ts.map
vendored
2
dist/mcp/handlers-workflow-diff.d.ts.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"handlers-workflow-diff.d.ts","sourceRoot":"","sources":["../../src/mcp/handlers-workflow-diff.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAMnD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE5D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AA0D7D,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,cAAc,EAC1B,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,eAAe,CAAC,CA6V1B"}
|
{"version":3,"file":"handlers-workflow-diff.d.ts","sourceRoot":"","sources":["../../src/mcp/handlers-workflow-diff.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAMnD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE5D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAkF7D,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,cAAc,EAC1B,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,eAAe,CAAC,CAib1B"}
|
||||||
118
dist/mcp/handlers-workflow-diff.js
vendored
118
dist/mcp/handlers-workflow-diff.js
vendored
@@ -50,6 +50,9 @@ function getValidator(repository) {
|
|||||||
}
|
}
|
||||||
return cachedValidator;
|
return cachedValidator;
|
||||||
}
|
}
|
||||||
|
const NODE_TARGETING_OPERATIONS = new Set([
|
||||||
|
'updateNode', 'removeNode', 'moveNode', 'enableNode', 'disableNode'
|
||||||
|
]);
|
||||||
const workflowDiffSchema = zod_1.z.object({
|
const workflowDiffSchema = zod_1.z.object({
|
||||||
id: zod_1.z.string(),
|
id: zod_1.z.string(),
|
||||||
operations: zod_1.z.array(zod_1.z.object({
|
operations: zod_1.z.array(zod_1.z.object({
|
||||||
@@ -64,8 +67,8 @@ const workflowDiffSchema = zod_1.z.object({
|
|||||||
target: zod_1.z.string().optional(),
|
target: zod_1.z.string().optional(),
|
||||||
from: zod_1.z.string().optional(),
|
from: zod_1.z.string().optional(),
|
||||||
to: zod_1.z.string().optional(),
|
to: zod_1.z.string().optional(),
|
||||||
sourceOutput: zod_1.z.string().optional(),
|
sourceOutput: zod_1.z.union([zod_1.z.string(), zod_1.z.number()]).transform(String).optional(),
|
||||||
targetInput: zod_1.z.string().optional(),
|
targetInput: zod_1.z.union([zod_1.z.string(), zod_1.z.number()]).transform(String).optional(),
|
||||||
sourceIndex: zod_1.z.number().optional(),
|
sourceIndex: zod_1.z.number().optional(),
|
||||||
targetIndex: zod_1.z.number().optional(),
|
targetIndex: zod_1.z.number().optional(),
|
||||||
branch: zod_1.z.enum(['true', 'false']).optional(),
|
branch: zod_1.z.enum(['true', 'false']).optional(),
|
||||||
@@ -76,6 +79,20 @@ const workflowDiffSchema = zod_1.z.object({
|
|||||||
settings: zod_1.z.any().optional(),
|
settings: zod_1.z.any().optional(),
|
||||||
name: zod_1.z.string().optional(),
|
name: zod_1.z.string().optional(),
|
||||||
tag: zod_1.z.string().optional(),
|
tag: zod_1.z.string().optional(),
|
||||||
|
destinationProjectId: zod_1.z.string().min(1).optional(),
|
||||||
|
id: zod_1.z.string().optional(),
|
||||||
|
}).transform((op) => {
|
||||||
|
if (NODE_TARGETING_OPERATIONS.has(op.type)) {
|
||||||
|
if (!op.nodeName && !op.nodeId && op.name) {
|
||||||
|
op.nodeName = op.name;
|
||||||
|
op.name = undefined;
|
||||||
|
}
|
||||||
|
if (!op.nodeId && op.id) {
|
||||||
|
op.nodeId = op.id;
|
||||||
|
op.id = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return op;
|
||||||
})),
|
})),
|
||||||
validateOnly: zod_1.z.boolean().optional(),
|
validateOnly: zod_1.z.boolean().optional(),
|
||||||
continueOnError: zod_1.z.boolean().optional(),
|
continueOnError: zod_1.z.boolean().optional(),
|
||||||
@@ -167,11 +184,12 @@ async function handleUpdatePartialWorkflow(args, repository, context) {
|
|||||||
else {
|
else {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
|
saved: false,
|
||||||
error: 'Failed to apply diff operations',
|
error: 'Failed to apply diff operations',
|
||||||
|
operationsApplied: diffResult.operationsApplied,
|
||||||
details: {
|
details: {
|
||||||
errors: diffResult.errors,
|
errors: diffResult.errors,
|
||||||
warnings: diffResult.warnings,
|
warnings: diffResult.warnings,
|
||||||
operationsApplied: diffResult.operationsApplied,
|
|
||||||
applied: diffResult.applied,
|
applied: diffResult.applied,
|
||||||
failed: diffResult.failed
|
failed: diffResult.failed
|
||||||
}
|
}
|
||||||
@@ -239,6 +257,7 @@ async function handleUpdatePartialWorkflow(args, repository, context) {
|
|||||||
if (!skipValidation) {
|
if (!skipValidation) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
|
saved: false,
|
||||||
error: errorMessage,
|
error: errorMessage,
|
||||||
details: {
|
details: {
|
||||||
errors: structureErrors,
|
errors: structureErrors,
|
||||||
@@ -247,7 +266,7 @@ async function handleUpdatePartialWorkflow(args, repository, context) {
|
|||||||
applied: diffResult.applied,
|
applied: diffResult.applied,
|
||||||
recoveryGuidance: recoverySteps,
|
recoveryGuidance: recoverySteps,
|
||||||
note: 'Operations were applied but created an invalid workflow structure. The workflow was NOT saved to n8n to prevent UI rendering errors.',
|
note: 'Operations were applied but created an invalid workflow structure. The workflow was NOT saved to n8n to prevent UI rendering errors.',
|
||||||
autoSanitizationNote: 'Auto-sanitization runs on all nodes during updates to fix operator structures and add missing metadata. However, it cannot fix all issues (e.g., broken connections, branch mismatches). Use the recovery guidance above to resolve remaining issues.'
|
autoSanitizationNote: 'Auto-sanitization runs on modified nodes during updates to fix operator structures and add missing metadata. However, it cannot fix all issues (e.g., broken connections, branch mismatches). Use the recovery guidance above to resolve remaining issues.'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -259,6 +278,77 @@ async function handleUpdatePartialWorkflow(args, repository, context) {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const updatedWorkflow = await client.updateWorkflow(input.id, diffResult.workflow);
|
const updatedWorkflow = await client.updateWorkflow(input.id, diffResult.workflow);
|
||||||
|
let tagWarnings = [];
|
||||||
|
if (diffResult.tagsToAdd?.length || diffResult.tagsToRemove?.length) {
|
||||||
|
try {
|
||||||
|
const existingTags = Array.isArray(updatedWorkflow.tags)
|
||||||
|
? updatedWorkflow.tags.map((t) => typeof t === 'object' ? { id: t.id, name: t.name } : { id: '', name: t })
|
||||||
|
: [];
|
||||||
|
const allTags = await client.listTags();
|
||||||
|
const tagMap = new Map();
|
||||||
|
for (const t of allTags.data) {
|
||||||
|
if (t.id)
|
||||||
|
tagMap.set(t.name.toLowerCase(), t.id);
|
||||||
|
}
|
||||||
|
for (const tagName of (diffResult.tagsToAdd || [])) {
|
||||||
|
if (!tagMap.has(tagName.toLowerCase())) {
|
||||||
|
try {
|
||||||
|
const newTag = await client.createTag({ name: tagName });
|
||||||
|
if (newTag.id)
|
||||||
|
tagMap.set(tagName.toLowerCase(), newTag.id);
|
||||||
|
}
|
||||||
|
catch (createErr) {
|
||||||
|
tagWarnings.push(`Failed to create tag "${tagName}": ${createErr instanceof Error ? createErr.message : 'Unknown error'}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const currentTagIds = new Set();
|
||||||
|
for (const et of existingTags) {
|
||||||
|
if (et.id) {
|
||||||
|
currentTagIds.add(et.id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const resolved = tagMap.get(et.name.toLowerCase());
|
||||||
|
if (resolved)
|
||||||
|
currentTagIds.add(resolved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const tagName of (diffResult.tagsToAdd || [])) {
|
||||||
|
const tagId = tagMap.get(tagName.toLowerCase());
|
||||||
|
if (tagId)
|
||||||
|
currentTagIds.add(tagId);
|
||||||
|
}
|
||||||
|
for (const tagName of (diffResult.tagsToRemove || [])) {
|
||||||
|
const tagId = tagMap.get(tagName.toLowerCase());
|
||||||
|
if (tagId)
|
||||||
|
currentTagIds.delete(tagId);
|
||||||
|
}
|
||||||
|
await client.updateWorkflowTags(input.id, Array.from(currentTagIds));
|
||||||
|
}
|
||||||
|
catch (tagError) {
|
||||||
|
tagWarnings.push(`Tag update failed: ${tagError instanceof Error ? tagError.message : 'Unknown error'}`);
|
||||||
|
logger_1.logger.warn('Tag operations failed (non-blocking)', tagError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let transferMessage = '';
|
||||||
|
if (diffResult.transferToProjectId) {
|
||||||
|
try {
|
||||||
|
await client.transferWorkflow(input.id, diffResult.transferToProjectId);
|
||||||
|
transferMessage = ` Workflow transferred to project ${diffResult.transferToProjectId}.`;
|
||||||
|
}
|
||||||
|
catch (transferError) {
|
||||||
|
logger_1.logger.error('Failed to transfer workflow to project', transferError);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
saved: true,
|
||||||
|
error: 'Workflow updated successfully but project transfer failed',
|
||||||
|
details: {
|
||||||
|
workflowUpdated: true,
|
||||||
|
transferError: transferError instanceof Error ? transferError.message : 'Unknown error'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
let finalWorkflow = updatedWorkflow;
|
let finalWorkflow = updatedWorkflow;
|
||||||
let activationMessage = '';
|
let activationMessage = '';
|
||||||
try {
|
try {
|
||||||
@@ -286,6 +376,7 @@ async function handleUpdatePartialWorkflow(args, repository, context) {
|
|||||||
logger_1.logger.error('Failed to activate workflow after update', activationError);
|
logger_1.logger.error('Failed to activate workflow after update', activationError);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
|
saved: true,
|
||||||
error: 'Workflow updated successfully but activation failed',
|
error: 'Workflow updated successfully but activation failed',
|
||||||
details: {
|
details: {
|
||||||
workflowUpdated: true,
|
workflowUpdated: true,
|
||||||
@@ -303,6 +394,7 @@ async function handleUpdatePartialWorkflow(args, repository, context) {
|
|||||||
logger_1.logger.error('Failed to deactivate workflow after update', deactivationError);
|
logger_1.logger.error('Failed to deactivate workflow after update', deactivationError);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
|
saved: true,
|
||||||
error: 'Workflow updated successfully but deactivation failed',
|
error: 'Workflow updated successfully but deactivation failed',
|
||||||
details: {
|
details: {
|
||||||
workflowUpdated: true,
|
workflowUpdated: true,
|
||||||
@@ -329,6 +421,7 @@ async function handleUpdatePartialWorkflow(args, repository, context) {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
saved: true,
|
||||||
data: {
|
data: {
|
||||||
id: finalWorkflow.id,
|
id: finalWorkflow.id,
|
||||||
name: finalWorkflow.name,
|
name: finalWorkflow.name,
|
||||||
@@ -336,12 +429,12 @@ async function handleUpdatePartialWorkflow(args, repository, context) {
|
|||||||
nodeCount: finalWorkflow.nodes?.length || 0,
|
nodeCount: finalWorkflow.nodes?.length || 0,
|
||||||
operationsApplied: diffResult.operationsApplied
|
operationsApplied: diffResult.operationsApplied
|
||||||
},
|
},
|
||||||
message: `Workflow "${finalWorkflow.name}" updated successfully. Applied ${diffResult.operationsApplied} operations.${activationMessage} Use n8n_get_workflow with mode 'structure' to verify current state.`,
|
message: `Workflow "${finalWorkflow.name}" updated successfully. Applied ${diffResult.operationsApplied} operations.${transferMessage}${activationMessage} Use n8n_get_workflow with mode 'structure' to verify current state.`,
|
||||||
details: {
|
details: {
|
||||||
applied: diffResult.applied,
|
applied: diffResult.applied,
|
||||||
failed: diffResult.failed,
|
failed: diffResult.failed,
|
||||||
errors: diffResult.errors,
|
errors: diffResult.errors,
|
||||||
warnings: diffResult.warnings
|
warnings: mergeWarnings(diffResult.warnings, tagWarnings)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -379,7 +472,9 @@ async function handleUpdatePartialWorkflow(args, repository, context) {
|
|||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Invalid input',
|
error: 'Invalid input',
|
||||||
details: { errors: error.errors }
|
details: {
|
||||||
|
errors: error.errors.map(e => `${e.path.join('.')}: ${e.message}`)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
logger_1.logger.error('Failed to update partial workflow', error);
|
logger_1.logger.error('Failed to update partial workflow', error);
|
||||||
@@ -389,6 +484,13 @@ async function handleUpdatePartialWorkflow(args, repository, context) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function mergeWarnings(diffWarnings, tagWarnings) {
|
||||||
|
const merged = [
|
||||||
|
...(diffWarnings || []),
|
||||||
|
...tagWarnings.map(w => ({ operation: -1, message: w }))
|
||||||
|
];
|
||||||
|
return merged.length > 0 ? merged : undefined;
|
||||||
|
}
|
||||||
function inferIntentFromOperations(operations) {
|
function inferIntentFromOperations(operations) {
|
||||||
if (!operations || operations.length === 0) {
|
if (!operations || operations.length === 0) {
|
||||||
return 'Partial workflow update';
|
return 'Partial workflow update';
|
||||||
@@ -416,6 +518,8 @@ function inferIntentFromOperations(operations) {
|
|||||||
return 'Activate workflow';
|
return 'Activate workflow';
|
||||||
case 'deactivateWorkflow':
|
case 'deactivateWorkflow':
|
||||||
return 'Deactivate workflow';
|
return 'Deactivate workflow';
|
||||||
|
case 'transferWorkflow':
|
||||||
|
return `Transfer workflow to project ${op.destinationProjectId || ''}`.trim();
|
||||||
default:
|
default:
|
||||||
return `Workflow ${op.type}`;
|
return `Workflow ${op.type}`;
|
||||||
}
|
}
|
||||||
|
|||||||
2
dist/mcp/handlers-workflow-diff.js.map
vendored
2
dist/mcp/handlers-workflow-diff.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/mcp/server.d.ts.map
vendored
2
dist/mcp/server.d.ts.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AA0CA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAmGnE,qBAAa,yBAAyB;IACpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,EAAE,CAAgC;IAC1C,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,qBAAqB,CAAsB;IACnD,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,kBAAkB,CAA4B;IACtD,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,aAAa,CAAoC;IACzD,OAAO,CAAC,UAAU,CAAkB;gBAExB,eAAe,CAAC,EAAE,eAAe,EAAE,WAAW,CAAC,EAAE,gBAAgB;IAuGvE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YA+Cd,kBAAkB;YAiDlB,wBAAwB;IA0BtC,OAAO,CAAC,kBAAkB;YA6CZ,iBAAiB;IAa/B,OAAO,CAAC,eAAe,CAAkB;YAE3B,sBAAsB;IAgDpC,OAAO,CAAC,gBAAgB;IAqCxB,OAAO,CAAC,aAAa;IA0XrB,OAAO,CAAC,wBAAwB;IAoFhC,OAAO,CAAC,kBAAkB;IAqE1B,OAAO,CAAC,uBAAuB;IAwB/B,OAAO,CAAC,qBAAqB;IAiF7B,OAAO,CAAC,2BAA2B;YA0UrB,SAAS;YA2DT,WAAW;YAkFX,WAAW;YA0CX,cAAc;YA8Md,gBAAgB;IAqD9B,OAAO,CAAC,mBAAmB;IAwE3B,OAAO,CAAC,eAAe;YAsBT,eAAe;IA2L7B,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,uBAAuB;IA0D/B,OAAO,CAAC,iBAAiB;YAqFX,WAAW;YAgCX,oBAAoB;IAuFlC,OAAO,CAAC,aAAa;YAQP,qBAAqB;YAwDrB,iBAAiB;YAiKjB,OAAO;YAgDP,cAAc;YAwFd,iBAAiB;IAqC/B,OAAO,CAAC,iBAAiB;IA0BzB,OAAO,CAAC,iBAAiB;IA0BzB,OAAO,CAAC,eAAe;IAwCvB,OAAO,CAAC,kBAAkB;IAiC1B,OAAO,CAAC,aAAa;IAoCrB,OAAO,CAAC,0BAA0B;IAgClC,OAAO,CAAC,4BAA4B;YAKtB,oBAAoB;IAsDlC,OAAO,CAAC,gBAAgB;YAiBV,SAAS;YA6CT,kBAAkB;YAqElB,uBAAuB;YAsDvB,iBAAiB;IAqE/B,OAAO,CAAC,qBAAqB;IA8C7B,OAAO,CAAC,uBAAuB;IA4D/B,OAAO,CAAC,wBAAwB;IAkChC,OAAO,CAAC,iBAAiB;YAoDX,mBAAmB;YAoEnB,qBAAqB;IAS7B,OAAO,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;YAS9B,aAAa;YAcb,iBAAiB;YAoBjB,WAAW;YAwBX,eAAe;YAqBf,mBAAmB;YAwBnB,yBAAyB;IA4CvC,OAAO,CAAC,kBAAkB;YAiBZ,gBAAgB;YA6HhB,2BAA2B;YAiE3B,2BAA2B;IAyEnC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IA0BpB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAgEhC"}
|
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AA0CA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAmGnE,qBAAa,yBAAyB;IACpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,EAAE,CAAgC;IAC1C,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,qBAAqB,CAAsB;IACnD,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,kBAAkB,CAA4B;IACtD,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,aAAa,CAAoC;IACzD,OAAO,CAAC,UAAU,CAAkB;gBAExB,eAAe,CAAC,EAAE,eAAe,EAAE,WAAW,CAAC,EAAE,gBAAgB;IA8GvE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YA+Cd,kBAAkB;YAiDlB,wBAAwB;IA0BtC,OAAO,CAAC,kBAAkB;YA6CZ,iBAAiB;IAa/B,OAAO,CAAC,eAAe,CAAkB;YAE3B,sBAAsB;IAgDpC,OAAO,CAAC,gBAAgB;IAqCxB,OAAO,CAAC,aAAa;IA0XrB,OAAO,CAAC,wBAAwB;IAoFhC,OAAO,CAAC,kBAAkB;IAqE1B,OAAO,CAAC,uBAAuB;IAwB/B,OAAO,CAAC,qBAAqB;IAiF7B,OAAO,CAAC,2BAA2B;YA0UrB,SAAS;YA2DT,WAAW;YAkFX,WAAW;YA0CX,cAAc;YA8Md,gBAAgB;IAqD9B,OAAO,CAAC,mBAAmB;IAwE3B,OAAO,CAAC,eAAe;YAsBT,eAAe;IA2L7B,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,uBAAuB;IA0D/B,OAAO,CAAC,iBAAiB;YAqFX,WAAW;YAgCX,oBAAoB;IAuFlC,OAAO,CAAC,aAAa;YAQP,qBAAqB;YAwDrB,iBAAiB;YAiKjB,OAAO;YAgDP,cAAc;YAwFd,iBAAiB;IAqC/B,OAAO,CAAC,iBAAiB;IA0BzB,OAAO,CAAC,iBAAiB;IA0BzB,OAAO,CAAC,eAAe;IAwCvB,OAAO,CAAC,kBAAkB;IAiC1B,OAAO,CAAC,aAAa;IAoCrB,OAAO,CAAC,0BAA0B;IAgClC,OAAO,CAAC,4BAA4B;YAKtB,oBAAoB;IAsDlC,OAAO,CAAC,gBAAgB;YAiBV,SAAS;YA6CT,kBAAkB;YAqElB,uBAAuB;YAsDvB,iBAAiB;IAqE/B,OAAO,CAAC,qBAAqB;IA8C7B,OAAO,CAAC,uBAAuB;IA4D/B,OAAO,CAAC,wBAAwB;IAkChC,OAAO,CAAC,iBAAiB;YAoDX,mBAAmB;YAoEnB,qBAAqB;IAS7B,OAAO,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;YAS9B,aAAa;YAcb,iBAAiB;YAoBjB,WAAW;YAwBX,eAAe;YAqBf,mBAAmB;YAwBnB,yBAAyB;IA4CvC,OAAO,CAAC,kBAAkB;YAiBZ,gBAAgB;YA6HhB,2BAA2B;YAiE3B,2BAA2B;IAyEnC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IA0BpB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAgEhC"}
|
||||||
1
dist/mcp/server.js
vendored
1
dist/mcp/server.js
vendored
@@ -124,6 +124,7 @@ class N8NDocumentationMCPServer {
|
|||||||
this.earlyLogger.logCheckpoint(startup_checkpoints_1.STARTUP_CHECKPOINTS.N8N_API_READY);
|
this.earlyLogger.logCheckpoint(startup_checkpoints_1.STARTUP_CHECKPOINTS.N8N_API_READY);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.initialized.catch(() => { });
|
||||||
logger_1.logger.info('Initializing n8n Documentation MCP server');
|
logger_1.logger.info('Initializing n8n Documentation MCP server');
|
||||||
this.server = new index_js_1.Server({
|
this.server = new index_js_1.Server({
|
||||||
name: 'n8n-documentation-mcp',
|
name: 'n8n-documentation-mcp',
|
||||||
|
|||||||
2
dist/mcp/server.js.map
vendored
2
dist/mcp/server.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"n8n-update-partial-workflow.d.ts","sourceRoot":"","sources":["../../../../src/mcp/tool-docs/workflow_management/n8n-update-partial-workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C,eAAO,MAAM,2BAA2B,EAAE,iBA+ZzC,CAAC"}
|
{"version":3,"file":"n8n-update-partial-workflow.d.ts","sourceRoot":"","sources":["../../../../src/mcp/tool-docs/workflow_management/n8n-update-partial-workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C,eAAO,MAAM,2BAA2B,EAAE,iBAuazC,CAAC"}
|
||||||
@@ -5,7 +5,7 @@ exports.n8nUpdatePartialWorkflowDoc = {
|
|||||||
name: 'n8n_update_partial_workflow',
|
name: 'n8n_update_partial_workflow',
|
||||||
category: 'workflow_management',
|
category: 'workflow_management',
|
||||||
essentials: {
|
essentials: {
|
||||||
description: 'Update workflow incrementally with diff operations. Types: addNode, removeNode, updateNode, moveNode, enable/disableNode, addConnection, removeConnection, rewireConnection, cleanStaleConnections, replaceConnections, updateSettings, updateName, add/removeTag, activateWorkflow, deactivateWorkflow. Supports smart parameters (branch, case) for multi-output nodes. Full support for AI connections (ai_languageModel, ai_tool, ai_memory, ai_embedding, ai_vectorStore, ai_document, ai_textSplitter, ai_outputParser).',
|
description: 'Update workflow incrementally with diff operations. Types: addNode, removeNode, updateNode, moveNode, enable/disableNode, addConnection, removeConnection, rewireConnection, cleanStaleConnections, replaceConnections, updateSettings, updateName, add/removeTag, activateWorkflow, deactivateWorkflow, transferWorkflow. Supports smart parameters (branch, case) for multi-output nodes. Full support for AI connections (ai_languageModel, ai_tool, ai_memory, ai_embedding, ai_vectorStore, ai_document, ai_textSplitter, ai_outputParser).',
|
||||||
keyParameters: ['id', 'operations', 'continueOnError'],
|
keyParameters: ['id', 'operations', 'continueOnError'],
|
||||||
example: 'n8n_update_partial_workflow({id: "wf_123", operations: [{type: "rewireConnection", source: "IF", from: "Old", to: "New", branch: "true"}]})',
|
example: 'n8n_update_partial_workflow({id: "wf_123", operations: [{type: "rewireConnection", source: "IF", from: "Old", to: "New", branch: "true"}]})',
|
||||||
performance: 'Fast (50-200ms)',
|
performance: 'Fast (50-200ms)',
|
||||||
@@ -23,7 +23,8 @@ exports.n8nUpdatePartialWorkflowDoc = {
|
|||||||
'Batch AI component connections for atomic updates',
|
'Batch AI component connections for atomic updates',
|
||||||
'Auto-sanitization: ALL nodes auto-fixed during updates (operator structures, missing metadata)',
|
'Auto-sanitization: ALL nodes auto-fixed during updates (operator structures, missing metadata)',
|
||||||
'Node renames automatically update all connection references - no manual connection operations needed',
|
'Node renames automatically update all connection references - no manual connection operations needed',
|
||||||
'Activate/deactivate workflows: Use activateWorkflow/deactivateWorkflow operations (requires activatable triggers like webhook/schedule)'
|
'Activate/deactivate workflows: Use activateWorkflow/deactivateWorkflow operations (requires activatable triggers like webhook/schedule)',
|
||||||
|
'Transfer workflows between projects: Use transferWorkflow with destinationProjectId (enterprise feature)'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
full: {
|
full: {
|
||||||
@@ -56,6 +57,9 @@ exports.n8nUpdatePartialWorkflowDoc = {
|
|||||||
- **activateWorkflow**: Activate the workflow to enable automatic execution via triggers
|
- **activateWorkflow**: Activate the workflow to enable automatic execution via triggers
|
||||||
- **deactivateWorkflow**: Deactivate the workflow to prevent automatic execution
|
- **deactivateWorkflow**: Deactivate the workflow to prevent automatic execution
|
||||||
|
|
||||||
|
### Project Management Operations (1 type):
|
||||||
|
- **transferWorkflow**: Transfer the workflow to a different project. Requires \`destinationProjectId\`. Enterprise/cloud feature.
|
||||||
|
|
||||||
## Smart Parameters for Multi-Output Nodes
|
## Smart Parameters for Multi-Output Nodes
|
||||||
|
|
||||||
For **IF nodes**, use semantic 'branch' parameter instead of technical sourceIndex:
|
For **IF nodes**, use semantic 'branch' parameter instead of technical sourceIndex:
|
||||||
@@ -195,12 +199,12 @@ Please choose a different name.
|
|||||||
- Can rename a node and add/remove connections using the new name in the same batch
|
- Can rename a node and add/remove connections using the new name in the same batch
|
||||||
- Use \`validateOnly: true\` to preview effects before applying
|
- Use \`validateOnly: true\` to preview effects before applying
|
||||||
|
|
||||||
## Removing Properties with undefined
|
## Removing Properties with null
|
||||||
|
|
||||||
To remove a property from a node, set its value to \`undefined\` in the updates object. This is essential when migrating from deprecated properties or cleaning up optional configuration fields.
|
To remove a property from a node, set its value to \`null\` in the updates object. This is essential when migrating from deprecated properties or cleaning up optional configuration fields.
|
||||||
|
|
||||||
### Why Use undefined?
|
### Why Use null?
|
||||||
- **Property removal vs. null**: Setting a property to \`undefined\` removes it completely from the node object, while \`null\` sets the property to a null value
|
- **Property removal**: Setting a property to \`null\` removes it completely from the node object
|
||||||
- **Validation constraints**: Some properties are mutually exclusive (e.g., \`continueOnFail\` and \`onError\`). Simply setting one without removing the other will fail validation
|
- **Validation constraints**: Some properties are mutually exclusive (e.g., \`continueOnFail\` and \`onError\`). Simply setting one without removing the other will fail validation
|
||||||
- **Deprecated property migration**: When n8n deprecates properties, you must remove the old property before the new one will work
|
- **Deprecated property migration**: When n8n deprecates properties, you must remove the old property before the new one will work
|
||||||
|
|
||||||
@@ -212,7 +216,7 @@ n8n_update_partial_workflow({
|
|||||||
operations: [{
|
operations: [{
|
||||||
type: "updateNode",
|
type: "updateNode",
|
||||||
nodeName: "HTTP Request",
|
nodeName: "HTTP Request",
|
||||||
updates: { onError: undefined }
|
updates: { onError: null }
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -222,7 +226,7 @@ n8n_update_partial_workflow({
|
|||||||
operations: [{
|
operations: [{
|
||||||
type: "updateNode",
|
type: "updateNode",
|
||||||
nodeId: "node_abc",
|
nodeId: "node_abc",
|
||||||
updates: { disabled: undefined }
|
updates: { disabled: null }
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
\`\`\`
|
\`\`\`
|
||||||
@@ -236,7 +240,7 @@ n8n_update_partial_workflow({
|
|||||||
operations: [{
|
operations: [{
|
||||||
type: "updateNode",
|
type: "updateNode",
|
||||||
nodeName: "API Request",
|
nodeName: "API Request",
|
||||||
updates: { "parameters.authentication": undefined }
|
updates: { "parameters.authentication": null }
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -246,7 +250,7 @@ n8n_update_partial_workflow({
|
|||||||
operations: [{
|
operations: [{
|
||||||
type: "updateNode",
|
type: "updateNode",
|
||||||
nodeName: "HTTP Request",
|
nodeName: "HTTP Request",
|
||||||
updates: { "parameters.headers": undefined }
|
updates: { "parameters.headers": null }
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
\`\`\`
|
\`\`\`
|
||||||
@@ -272,7 +276,7 @@ n8n_update_partial_workflow({
|
|||||||
type: "updateNode",
|
type: "updateNode",
|
||||||
nodeName: "HTTP Request",
|
nodeName: "HTTP Request",
|
||||||
updates: {
|
updates: {
|
||||||
continueOnFail: undefined,
|
continueOnFail: null,
|
||||||
onError: "continueErrorOutput"
|
onError: "continueErrorOutput"
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
@@ -288,15 +292,15 @@ n8n_update_partial_workflow({
|
|||||||
type: "updateNode",
|
type: "updateNode",
|
||||||
nodeName: "Data Processor",
|
nodeName: "Data Processor",
|
||||||
updates: {
|
updates: {
|
||||||
continueOnFail: undefined,
|
continueOnFail: null,
|
||||||
alwaysOutputData: undefined,
|
alwaysOutputData: null,
|
||||||
"parameters.legacy_option": undefined
|
"parameters.legacy_option": null
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
|
||||||
### When to Use undefined
|
### When to Use null
|
||||||
- Removing deprecated properties during migration
|
- Removing deprecated properties during migration
|
||||||
- Cleaning up optional configuration flags
|
- Cleaning up optional configuration flags
|
||||||
- Resolving mutual exclusivity validation errors
|
- Resolving mutual exclusivity validation errors
|
||||||
@@ -342,11 +346,14 @@ n8n_update_partial_workflow({
|
|||||||
'// Rewire AI Agent to use different language model\nn8n_update_partial_workflow({id: "ai9", operations: [{type: "rewireConnection", source: "AI Agent", from: "OpenAI Chat Model", to: "Anthropic Chat Model", sourceOutput: "ai_languageModel"}]})',
|
'// Rewire AI Agent to use different language model\nn8n_update_partial_workflow({id: "ai9", operations: [{type: "rewireConnection", source: "AI Agent", from: "OpenAI Chat Model", to: "Anthropic Chat Model", sourceOutput: "ai_languageModel"}]})',
|
||||||
'// Replace all AI tools for an agent\nn8n_update_partial_workflow({id: "ai10", operations: [\n {type: "removeConnection", source: "Old Tool 1", target: "AI Agent", sourceOutput: "ai_tool"},\n {type: "removeConnection", source: "Old Tool 2", target: "AI Agent", sourceOutput: "ai_tool"},\n {type: "addConnection", source: "New HTTP Tool", target: "AI Agent", sourceOutput: "ai_tool"},\n {type: "addConnection", source: "New Code Tool", target: "AI Agent", sourceOutput: "ai_tool"}\n]})',
|
'// Replace all AI tools for an agent\nn8n_update_partial_workflow({id: "ai10", operations: [\n {type: "removeConnection", source: "Old Tool 1", target: "AI Agent", sourceOutput: "ai_tool"},\n {type: "removeConnection", source: "Old Tool 2", target: "AI Agent", sourceOutput: "ai_tool"},\n {type: "addConnection", source: "New HTTP Tool", target: "AI Agent", sourceOutput: "ai_tool"},\n {type: "addConnection", source: "New Code Tool", target: "AI Agent", sourceOutput: "ai_tool"}\n]})',
|
||||||
'\n// ============ REMOVING PROPERTIES EXAMPLES ============',
|
'\n// ============ REMOVING PROPERTIES EXAMPLES ============',
|
||||||
'// Remove a simple property\nn8n_update_partial_workflow({id: "rm1", operations: [{type: "updateNode", nodeName: "HTTP Request", updates: {onError: undefined}}]})',
|
'// Remove a simple property\nn8n_update_partial_workflow({id: "rm1", operations: [{type: "updateNode", nodeName: "HTTP Request", updates: {onError: null}}]})',
|
||||||
'// Migrate from deprecated continueOnFail to onError\nn8n_update_partial_workflow({id: "rm2", operations: [{type: "updateNode", nodeName: "HTTP Request", updates: {continueOnFail: undefined, onError: "continueErrorOutput"}}]})',
|
'// Migrate from deprecated continueOnFail to onError\nn8n_update_partial_workflow({id: "rm2", operations: [{type: "updateNode", nodeName: "HTTP Request", updates: {continueOnFail: null, onError: "continueErrorOutput"}}]})',
|
||||||
'// Remove nested property\nn8n_update_partial_workflow({id: "rm3", operations: [{type: "updateNode", nodeName: "API Request", updates: {"parameters.authentication": undefined}}]})',
|
'// Remove nested property\nn8n_update_partial_workflow({id: "rm3", operations: [{type: "updateNode", nodeName: "API Request", updates: {"parameters.authentication": null}}]})',
|
||||||
'// Remove multiple properties\nn8n_update_partial_workflow({id: "rm4", operations: [{type: "updateNode", nodeName: "Data Processor", updates: {continueOnFail: undefined, alwaysOutputData: undefined, "parameters.legacy_option": undefined}}]})',
|
'// Remove multiple properties\nn8n_update_partial_workflow({id: "rm4", operations: [{type: "updateNode", nodeName: "Data Processor", updates: {continueOnFail: null, alwaysOutputData: null, "parameters.legacy_option": null}}]})',
|
||||||
'// Remove entire array property\nn8n_update_partial_workflow({id: "rm5", operations: [{type: "updateNode", nodeName: "HTTP Request", updates: {"parameters.headers": undefined}}]})'
|
'// Remove entire array property\nn8n_update_partial_workflow({id: "rm5", operations: [{type: "updateNode", nodeName: "HTTP Request", updates: {"parameters.headers": null}}]})',
|
||||||
|
'\n// ============ PROJECT TRANSFER EXAMPLES ============',
|
||||||
|
'// Transfer workflow to a different project\nn8n_update_partial_workflow({id: "tf1", operations: [{type: "transferWorkflow", destinationProjectId: "project-abc-123"}]})',
|
||||||
|
'// Transfer and activate in one call\nn8n_update_partial_workflow({id: "tf2", operations: [{type: "transferWorkflow", destinationProjectId: "project-abc-123"}, {type: "activateWorkflow"}]})'
|
||||||
],
|
],
|
||||||
useCases: [
|
useCases: [
|
||||||
'Rewire connections when replacing nodes',
|
'Rewire connections when replacing nodes',
|
||||||
@@ -364,7 +371,8 @@ n8n_update_partial_workflow({
|
|||||||
'Add fallback language models to AI Agents',
|
'Add fallback language models to AI Agents',
|
||||||
'Configure Vector Store retrieval systems',
|
'Configure Vector Store retrieval systems',
|
||||||
'Swap language models in existing AI workflows',
|
'Swap language models in existing AI workflows',
|
||||||
'Batch-update AI tool connections'
|
'Batch-update AI tool connections',
|
||||||
|
'Transfer workflows between team projects (enterprise)'
|
||||||
],
|
],
|
||||||
performance: 'Very fast - typically 50-200ms. Much faster than full updates as only changes are processed.',
|
performance: 'Very fast - typically 50-200ms. Much faster than full updates as only changes are processed.',
|
||||||
bestPractices: [
|
bestPractices: [
|
||||||
@@ -384,9 +392,9 @@ n8n_update_partial_workflow({
|
|||||||
'Use targetIndex for fallback models (primary=0, fallback=1)',
|
'Use targetIndex for fallback models (primary=0, fallback=1)',
|
||||||
'Batch AI component connections in a single operation for atomicity',
|
'Batch AI component connections in a single operation for atomicity',
|
||||||
'Validate AI workflows after connection changes to catch configuration errors',
|
'Validate AI workflows after connection changes to catch configuration errors',
|
||||||
'To remove properties, set them to undefined (not null) in the updates object',
|
'To remove properties, set them to null in the updates object',
|
||||||
'When migrating from deprecated properties, remove the old property and add the new one in the same operation',
|
'When migrating from deprecated properties, remove the old property and add the new one in the same operation',
|
||||||
'Use undefined to resolve mutual exclusivity validation errors between properties',
|
'Use null to resolve mutual exclusivity validation errors between properties',
|
||||||
'Batch multiple property removals in a single updateNode operation for efficiency'
|
'Batch multiple property removals in a single updateNode operation for efficiency'
|
||||||
],
|
],
|
||||||
pitfalls: [
|
pitfalls: [
|
||||||
@@ -408,8 +416,8 @@ n8n_update_partial_workflow({
|
|||||||
'**Auto-sanitization runs on ALL nodes**: When ANY update is made, ALL nodes in the workflow are sanitized (not just modified ones)',
|
'**Auto-sanitization runs on ALL nodes**: When ANY update is made, ALL nodes in the workflow are sanitized (not just modified ones)',
|
||||||
'**Auto-sanitization cannot fix everything**: It fixes operator structures and missing metadata, but cannot fix broken connections or branch mismatches',
|
'**Auto-sanitization cannot fix everything**: It fixes operator structures and missing metadata, but cannot fix broken connections or branch mismatches',
|
||||||
'**Corrupted workflows beyond repair**: Workflows in paradoxical states (API returns corrupt, API rejects updates) cannot be fixed via API - must be recreated',
|
'**Corrupted workflows beyond repair**: Workflows in paradoxical states (API returns corrupt, API rejects updates) cannot be fixed via API - must be recreated',
|
||||||
'Setting a property to null does NOT remove it - use undefined instead',
|
'To remove a property, set it to null in the updates object',
|
||||||
'When properties are mutually exclusive (e.g., continueOnFail and onError), setting only the new property will fail - you must remove the old one with undefined',
|
'When properties are mutually exclusive (e.g., continueOnFail and onError), setting only the new property will fail - you must remove the old one with null',
|
||||||
'Removing a required property may cause validation errors - check node documentation first',
|
'Removing a required property may cause validation errors - check node documentation first',
|
||||||
'Nested property removal with dot notation only removes the specific nested field, not the entire parent object',
|
'Nested property removal with dot notation only removes the specific nested field, not the entire parent object',
|
||||||
'Array index notation (e.g., "parameters.headers[0]") is not supported - remove the entire array property instead'
|
'Array index notation (e.g., "parameters.headers[0]") is not supported - remove the entire array property instead'
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"n8n-update-partial-workflow.js","sourceRoot":"","sources":["../../../../src/mcp/tool-docs/workflow_management/n8n-update-partial-workflow.ts"],"names":[],"mappings":"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"}
|
{"version":3,"file":"n8n-update-partial-workflow.js","sourceRoot":"","sources":["../../../../src/mcp/tool-docs/workflow_management/n8n-update-partial-workflow.ts"],"names":[],"mappings":"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"}
|
||||||
2
dist/mcp/tools-n8n-manager.js
vendored
2
dist/mcp/tools-n8n-manager.js
vendored
@@ -137,7 +137,7 @@ exports.n8nManagementTools = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'n8n_update_partial_workflow',
|
name: 'n8n_update_partial_workflow',
|
||||||
description: `Update workflow incrementally with diff operations. Types: addNode, removeNode, updateNode, moveNode, enable/disableNode, addConnection, removeConnection, updateSettings, updateName, add/removeTag. See tools_documentation("n8n_update_partial_workflow", "full") for details.`,
|
description: `Update workflow incrementally with diff operations. Types: addNode, removeNode, updateNode, moveNode, enable/disableNode, addConnection, removeConnection, updateSettings, updateName, add/removeTag, activate/deactivateWorkflow, transferWorkflow. See tools_documentation("n8n_update_partial_workflow", "full") for details.`,
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
additionalProperties: true,
|
additionalProperties: true,
|
||||||
|
|||||||
2
dist/mcp/tools-n8n-manager.js.map
vendored
2
dist/mcp/tools-n8n-manager.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/services/ai-tool-validators.d.ts
vendored
4
dist/services/ai-tool-validators.d.ts
vendored
@@ -33,8 +33,8 @@ export declare function validateVectorStoreTool(node: WorkflowNode, reverseConne
|
|||||||
export declare function validateWorkflowTool(node: WorkflowNode, reverseConnections?: Map<string, ReverseConnection[]>): ValidationIssue[];
|
export declare function validateWorkflowTool(node: WorkflowNode, reverseConnections?: Map<string, ReverseConnection[]>): ValidationIssue[];
|
||||||
export declare function validateAIAgentTool(node: WorkflowNode, reverseConnections: Map<string, ReverseConnection[]>): ValidationIssue[];
|
export declare function validateAIAgentTool(node: WorkflowNode, reverseConnections: Map<string, ReverseConnection[]>): ValidationIssue[];
|
||||||
export declare function validateMCPClientTool(node: WorkflowNode): ValidationIssue[];
|
export declare function validateMCPClientTool(node: WorkflowNode): ValidationIssue[];
|
||||||
export declare function validateCalculatorTool(node: WorkflowNode): ValidationIssue[];
|
export declare function validateCalculatorTool(_node: WorkflowNode): ValidationIssue[];
|
||||||
export declare function validateThinkTool(node: WorkflowNode): ValidationIssue[];
|
export declare function validateThinkTool(_node: WorkflowNode): ValidationIssue[];
|
||||||
export declare function validateSerpApiTool(node: WorkflowNode): ValidationIssue[];
|
export declare function validateSerpApiTool(node: WorkflowNode): ValidationIssue[];
|
||||||
export declare function validateWikipediaTool(node: WorkflowNode): ValidationIssue[];
|
export declare function validateWikipediaTool(node: WorkflowNode): ValidationIssue[];
|
||||||
export declare function validateSearXngTool(node: WorkflowNode): ValidationIssue[];
|
export declare function validateSearXngTool(node: WorkflowNode): ValidationIssue[];
|
||||||
|
|||||||
2
dist/services/ai-tool-validators.d.ts.map
vendored
2
dist/services/ai-tool-validators.d.ts.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"ai-tool-validators.d.ts","sourceRoot":"","sources":["../../src/services/ai-tool-validators.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,UAAU,EAAE,GAAG,CAAC;IAChB,WAAW,CAAC,EAAE,GAAG,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE,GAAG,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAMD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,EAAE,CAuJ7E;AAMD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,EAAE,CAoCtE;AAMD,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,YAAY,EAClB,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,EACpD,QAAQ,EAAE,YAAY,GACrB,eAAe,EAAE,CAmCnB;AAMD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,EAAE,kBAAkB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,GAAG,eAAe,EAAE,CA0BjI;AAMD,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,YAAY,EAClB,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,GACnD,eAAe,EAAE,CAmCnB;AAMD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,EAAE,CA0B3E;AAMD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,EAAE,CAM5E;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,EAAE,CAMvE;AAMD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,EAAE,CAyBzE;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,EAAE,CA4B3E;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,EAAE,CA0BzE;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,EAAE,CAyB9E;AAKD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;CAarB,CAAC;AAKX,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGzD;AAKD,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,YAAY,EAClB,QAAQ,EAAE,MAAM,EAChB,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,EACpD,QAAQ,EAAE,YAAY,GACrB,eAAe,EAAE,CAgCnB"}
|
{"version":3,"file":"ai-tool-validators.d.ts","sourceRoot":"","sources":["../../src/services/ai-tool-validators.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,UAAU,EAAE,GAAG,CAAC;IAChB,WAAW,CAAC,EAAE,GAAG,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAiBD,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE,GAAG,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAMD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,EAAE,CAuJ7E;AAMD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,EAAE,CAoCtE;AAMD,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,YAAY,EAClB,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,EACpD,QAAQ,EAAE,YAAY,GACrB,eAAe,EAAE,CAmCnB;AAMD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,EAAE,kBAAkB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,GAAG,eAAe,EAAE,CA0BjI;AAMD,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,YAAY,EAClB,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,GACnD,eAAe,EAAE,CAmCnB;AAMD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,EAAE,CA0B3E;AAMD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,YAAY,GAAG,eAAe,EAAE,CAG7E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,GAAG,eAAe,EAAE,CAGxE;AAMD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,EAAE,CAyBzE;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,EAAE,CA4B3E;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,EAAE,CA0BzE;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,EAAE,CAyB9E;AAKD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;CAarB,CAAC;AAKX,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGzD;AAKD,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,YAAY,EAClB,QAAQ,EAAE,MAAM,EAChB,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,EACpD,QAAQ,EAAE,YAAY,GACrB,eAAe,EAAE,CAgCnB"}
|
||||||
37
dist/services/ai-tool-validators.js
vendored
37
dist/services/ai-tool-validators.js
vendored
@@ -21,9 +21,14 @@ const MIN_DESCRIPTION_LENGTH_MEDIUM = 15;
|
|||||||
const MIN_DESCRIPTION_LENGTH_LONG = 20;
|
const MIN_DESCRIPTION_LENGTH_LONG = 20;
|
||||||
const MAX_ITERATIONS_WARNING_THRESHOLD = 50;
|
const MAX_ITERATIONS_WARNING_THRESHOLD = 50;
|
||||||
const MAX_TOPK_WARNING_THRESHOLD = 20;
|
const MAX_TOPK_WARNING_THRESHOLD = 20;
|
||||||
|
function getToolDescription(node) {
|
||||||
|
return (node.parameters.toolDescription ||
|
||||||
|
node.parameters.description ||
|
||||||
|
node.parameters.options?.description);
|
||||||
|
}
|
||||||
function validateHTTPRequestTool(node) {
|
function validateHTTPRequestTool(node) {
|
||||||
const issues = [];
|
const issues = [];
|
||||||
if (!node.parameters.toolDescription) {
|
if (!getToolDescription(node)) {
|
||||||
issues.push({
|
issues.push({
|
||||||
severity: 'error',
|
severity: 'error',
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
@@ -32,7 +37,7 @@ function validateHTTPRequestTool(node) {
|
|||||||
code: 'MISSING_TOOL_DESCRIPTION'
|
code: 'MISSING_TOOL_DESCRIPTION'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (node.parameters.toolDescription.trim().length < MIN_DESCRIPTION_LENGTH_MEDIUM) {
|
else if (getToolDescription(node).trim().length < MIN_DESCRIPTION_LENGTH_MEDIUM) {
|
||||||
issues.push({
|
issues.push({
|
||||||
severity: 'warning',
|
severity: 'warning',
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
@@ -154,7 +159,7 @@ function validateHTTPRequestTool(node) {
|
|||||||
}
|
}
|
||||||
function validateCodeTool(node) {
|
function validateCodeTool(node) {
|
||||||
const issues = [];
|
const issues = [];
|
||||||
if (!node.parameters.toolDescription) {
|
if (!getToolDescription(node)) {
|
||||||
issues.push({
|
issues.push({
|
||||||
severity: 'error',
|
severity: 'error',
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
@@ -184,7 +189,7 @@ function validateCodeTool(node) {
|
|||||||
}
|
}
|
||||||
function validateVectorStoreTool(node, reverseConnections, workflow) {
|
function validateVectorStoreTool(node, reverseConnections, workflow) {
|
||||||
const issues = [];
|
const issues = [];
|
||||||
if (!node.parameters.toolDescription) {
|
if (!getToolDescription(node)) {
|
||||||
issues.push({
|
issues.push({
|
||||||
severity: 'error',
|
severity: 'error',
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
@@ -216,7 +221,7 @@ function validateVectorStoreTool(node, reverseConnections, workflow) {
|
|||||||
}
|
}
|
||||||
function validateWorkflowTool(node, reverseConnections) {
|
function validateWorkflowTool(node, reverseConnections) {
|
||||||
const issues = [];
|
const issues = [];
|
||||||
if (!node.parameters.toolDescription) {
|
if (!getToolDescription(node)) {
|
||||||
issues.push({
|
issues.push({
|
||||||
severity: 'error',
|
severity: 'error',
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
@@ -238,7 +243,7 @@ function validateWorkflowTool(node, reverseConnections) {
|
|||||||
}
|
}
|
||||||
function validateAIAgentTool(node, reverseConnections) {
|
function validateAIAgentTool(node, reverseConnections) {
|
||||||
const issues = [];
|
const issues = [];
|
||||||
if (!node.parameters.toolDescription) {
|
if (!getToolDescription(node)) {
|
||||||
issues.push({
|
issues.push({
|
||||||
severity: 'error',
|
severity: 'error',
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
@@ -270,7 +275,7 @@ function validateAIAgentTool(node, reverseConnections) {
|
|||||||
}
|
}
|
||||||
function validateMCPClientTool(node) {
|
function validateMCPClientTool(node) {
|
||||||
const issues = [];
|
const issues = [];
|
||||||
if (!node.parameters.toolDescription) {
|
if (!getToolDescription(node)) {
|
||||||
issues.push({
|
issues.push({
|
||||||
severity: 'error',
|
severity: 'error',
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
@@ -290,17 +295,15 @@ function validateMCPClientTool(node) {
|
|||||||
}
|
}
|
||||||
return issues;
|
return issues;
|
||||||
}
|
}
|
||||||
function validateCalculatorTool(node) {
|
function validateCalculatorTool(_node) {
|
||||||
const issues = [];
|
return [];
|
||||||
return issues;
|
|
||||||
}
|
}
|
||||||
function validateThinkTool(node) {
|
function validateThinkTool(_node) {
|
||||||
const issues = [];
|
return [];
|
||||||
return issues;
|
|
||||||
}
|
}
|
||||||
function validateSerpApiTool(node) {
|
function validateSerpApiTool(node) {
|
||||||
const issues = [];
|
const issues = [];
|
||||||
if (!node.parameters.toolDescription) {
|
if (!getToolDescription(node)) {
|
||||||
issues.push({
|
issues.push({
|
||||||
severity: 'error',
|
severity: 'error',
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
@@ -321,7 +324,7 @@ function validateSerpApiTool(node) {
|
|||||||
}
|
}
|
||||||
function validateWikipediaTool(node) {
|
function validateWikipediaTool(node) {
|
||||||
const issues = [];
|
const issues = [];
|
||||||
if (!node.parameters.toolDescription) {
|
if (!getToolDescription(node)) {
|
||||||
issues.push({
|
issues.push({
|
||||||
severity: 'error',
|
severity: 'error',
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
@@ -345,7 +348,7 @@ function validateWikipediaTool(node) {
|
|||||||
}
|
}
|
||||||
function validateSearXngTool(node) {
|
function validateSearXngTool(node) {
|
||||||
const issues = [];
|
const issues = [];
|
||||||
if (!node.parameters.toolDescription) {
|
if (!getToolDescription(node)) {
|
||||||
issues.push({
|
issues.push({
|
||||||
severity: 'error',
|
severity: 'error',
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
@@ -376,7 +379,7 @@ function validateWolframAlphaTool(node) {
|
|||||||
code: 'MISSING_CREDENTIALS'
|
code: 'MISSING_CREDENTIALS'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!node.parameters.description && !node.parameters.toolDescription) {
|
if (!getToolDescription(node)) {
|
||||||
issues.push({
|
issues.push({
|
||||||
severity: 'info',
|
severity: 'info',
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
|
|||||||
2
dist/services/ai-tool-validators.js.map
vendored
2
dist/services/ai-tool-validators.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/services/n8n-api-client.d.ts
vendored
2
dist/services/n8n-api-client.d.ts
vendored
@@ -20,6 +20,7 @@ export declare class N8nApiClient {
|
|||||||
getWorkflow(id: string): Promise<Workflow>;
|
getWorkflow(id: string): Promise<Workflow>;
|
||||||
updateWorkflow(id: string, workflow: Partial<Workflow>): Promise<Workflow>;
|
updateWorkflow(id: string, workflow: Partial<Workflow>): Promise<Workflow>;
|
||||||
deleteWorkflow(id: string): Promise<Workflow>;
|
deleteWorkflow(id: string): Promise<Workflow>;
|
||||||
|
transferWorkflow(id: string, destinationProjectId: string): Promise<void>;
|
||||||
activateWorkflow(id: string): Promise<Workflow>;
|
activateWorkflow(id: string): Promise<Workflow>;
|
||||||
deactivateWorkflow(id: string): Promise<Workflow>;
|
deactivateWorkflow(id: string): Promise<Workflow>;
|
||||||
listWorkflows(params?: WorkflowListParams): Promise<WorkflowListResponse>;
|
listWorkflows(params?: WorkflowListParams): Promise<WorkflowListResponse>;
|
||||||
@@ -36,6 +37,7 @@ export declare class N8nApiClient {
|
|||||||
createTag(tag: Partial<Tag>): Promise<Tag>;
|
createTag(tag: Partial<Tag>): Promise<Tag>;
|
||||||
updateTag(id: string, tag: Partial<Tag>): Promise<Tag>;
|
updateTag(id: string, tag: Partial<Tag>): Promise<Tag>;
|
||||||
deleteTag(id: string): Promise<void>;
|
deleteTag(id: string): Promise<void>;
|
||||||
|
updateWorkflowTags(workflowId: string, tagIds: string[]): Promise<Tag[]>;
|
||||||
getSourceControlStatus(): Promise<SourceControlStatus>;
|
getSourceControlStatus(): Promise<SourceControlStatus>;
|
||||||
pullSourceControl(force?: boolean): Promise<SourceControlPullResult>;
|
pullSourceControl(force?: boolean): Promise<SourceControlPullResult>;
|
||||||
pushSourceControl(message: string, fileNames?: string[]): Promise<SourceControlPushResult>;
|
pushSourceControl(message: string, fileNames?: string[]): Promise<SourceControlPushResult>;
|
||||||
|
|||||||
2
dist/services/n8n-api-client.d.ts.map
vendored
2
dist/services/n8n-api-client.d.ts.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"n8n-api-client.d.ts","sourceRoot":"","sources":["../../src/services/n8n-api-client.ts"],"names":[],"mappings":"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"}
|
{"version":3,"file":"n8n-api-client.d.ts","sourceRoot":"","sources":["../../src/services/n8n-api-client.ts"],"names":[],"mappings":"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"}
|
||||||
21
dist/services/n8n-api-client.js
vendored
21
dist/services/n8n-api-client.js
vendored
@@ -194,9 +194,17 @@ class N8nApiClient {
|
|||||||
throw (0, n8n_errors_1.handleN8nApiError)(error);
|
throw (0, n8n_errors_1.handleN8nApiError)(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async transferWorkflow(id, destinationProjectId) {
|
||||||
|
try {
|
||||||
|
await this.client.put(`/workflows/${id}/transfer`, { destinationProjectId });
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
throw (0, n8n_errors_1.handleN8nApiError)(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
async activateWorkflow(id) {
|
async activateWorkflow(id) {
|
||||||
try {
|
try {
|
||||||
const response = await this.client.post(`/workflows/${id}/activate`);
|
const response = await this.client.post(`/workflows/${id}/activate`, {});
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
@@ -205,7 +213,7 @@ class N8nApiClient {
|
|||||||
}
|
}
|
||||||
async deactivateWorkflow(id) {
|
async deactivateWorkflow(id) {
|
||||||
try {
|
try {
|
||||||
const response = await this.client.post(`/workflows/${id}/deactivate`);
|
const response = await this.client.post(`/workflows/${id}/deactivate`, {});
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
@@ -365,6 +373,15 @@ class N8nApiClient {
|
|||||||
throw (0, n8n_errors_1.handleN8nApiError)(error);
|
throw (0, n8n_errors_1.handleN8nApiError)(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async updateWorkflowTags(workflowId, tagIds) {
|
||||||
|
try {
|
||||||
|
const response = await this.client.put(`/workflows/${workflowId}/tags`, tagIds.filter(id => id).map(id => ({ id })));
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
throw (0, n8n_errors_1.handleN8nApiError)(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
async getSourceControlStatus() {
|
async getSourceControlStatus() {
|
||||||
try {
|
try {
|
||||||
const response = await this.client.get('/source-control/status');
|
const response = await this.client.get('/source-control/status');
|
||||||
|
|||||||
2
dist/services/n8n-api-client.js.map
vendored
2
dist/services/n8n-api-client.js.map
vendored
File diff suppressed because one or more lines are too long
248
dist/services/n8n-validation.d.ts
vendored
248
dist/services/n8n-validation.d.ts
vendored
@@ -144,79 +144,227 @@ export declare const workflowConnectionSchema: z.ZodRecord<z.ZodString, z.ZodObj
|
|||||||
node: string;
|
node: string;
|
||||||
index: number;
|
index: number;
|
||||||
}>, "many">, "many">>;
|
}>, "many">, "many">>;
|
||||||
|
}, "strip", z.ZodArray<z.ZodArray<z.ZodObject<{
|
||||||
|
node: z.ZodString;
|
||||||
|
type: z.ZodString;
|
||||||
|
index: z.ZodNumber;
|
||||||
}, "strip", z.ZodTypeAny, {
|
}, "strip", z.ZodTypeAny, {
|
||||||
error?: {
|
type: string;
|
||||||
type: string;
|
node: string;
|
||||||
node: string;
|
index: number;
|
||||||
index: number;
|
|
||||||
}[][] | undefined;
|
|
||||||
main?: {
|
|
||||||
type: string;
|
|
||||||
node: string;
|
|
||||||
index: number;
|
|
||||||
}[][] | undefined;
|
|
||||||
ai_tool?: {
|
|
||||||
type: string;
|
|
||||||
node: string;
|
|
||||||
index: number;
|
|
||||||
}[][] | undefined;
|
|
||||||
ai_languageModel?: {
|
|
||||||
type: string;
|
|
||||||
node: string;
|
|
||||||
index: number;
|
|
||||||
}[][] | undefined;
|
|
||||||
ai_memory?: {
|
|
||||||
type: string;
|
|
||||||
node: string;
|
|
||||||
index: number;
|
|
||||||
}[][] | undefined;
|
|
||||||
ai_embedding?: {
|
|
||||||
type: string;
|
|
||||||
node: string;
|
|
||||||
index: number;
|
|
||||||
}[][] | undefined;
|
|
||||||
ai_vectorStore?: {
|
|
||||||
type: string;
|
|
||||||
node: string;
|
|
||||||
index: number;
|
|
||||||
}[][] | undefined;
|
|
||||||
}, {
|
}, {
|
||||||
error?: {
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}>, "many">, "many">, z.objectOutputType<{
|
||||||
|
main: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
|
||||||
|
node: z.ZodString;
|
||||||
|
type: z.ZodString;
|
||||||
|
index: z.ZodNumber;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
type: string;
|
type: string;
|
||||||
node: string;
|
node: string;
|
||||||
index: number;
|
index: number;
|
||||||
}[][] | undefined;
|
}, {
|
||||||
main?: {
|
|
||||||
type: string;
|
type: string;
|
||||||
node: string;
|
node: string;
|
||||||
index: number;
|
index: number;
|
||||||
}[][] | undefined;
|
}>, "many">, "many">>;
|
||||||
ai_tool?: {
|
error: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
|
||||||
|
node: z.ZodString;
|
||||||
|
type: z.ZodString;
|
||||||
|
index: z.ZodNumber;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
type: string;
|
type: string;
|
||||||
node: string;
|
node: string;
|
||||||
index: number;
|
index: number;
|
||||||
}[][] | undefined;
|
}, {
|
||||||
ai_languageModel?: {
|
|
||||||
type: string;
|
type: string;
|
||||||
node: string;
|
node: string;
|
||||||
index: number;
|
index: number;
|
||||||
}[][] | undefined;
|
}>, "many">, "many">>;
|
||||||
ai_memory?: {
|
ai_tool: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
|
||||||
|
node: z.ZodString;
|
||||||
|
type: z.ZodString;
|
||||||
|
index: z.ZodNumber;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
type: string;
|
type: string;
|
||||||
node: string;
|
node: string;
|
||||||
index: number;
|
index: number;
|
||||||
}[][] | undefined;
|
}, {
|
||||||
ai_embedding?: {
|
|
||||||
type: string;
|
type: string;
|
||||||
node: string;
|
node: string;
|
||||||
index: number;
|
index: number;
|
||||||
}[][] | undefined;
|
}>, "many">, "many">>;
|
||||||
ai_vectorStore?: {
|
ai_languageModel: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
|
||||||
|
node: z.ZodString;
|
||||||
|
type: z.ZodString;
|
||||||
|
index: z.ZodNumber;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
type: string;
|
type: string;
|
||||||
node: string;
|
node: string;
|
||||||
index: number;
|
index: number;
|
||||||
}[][] | undefined;
|
}, {
|
||||||
}>>;
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}>, "many">, "many">>;
|
||||||
|
ai_memory: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
|
||||||
|
node: z.ZodString;
|
||||||
|
type: z.ZodString;
|
||||||
|
index: z.ZodNumber;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}>, "many">, "many">>;
|
||||||
|
ai_embedding: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
|
||||||
|
node: z.ZodString;
|
||||||
|
type: z.ZodString;
|
||||||
|
index: z.ZodNumber;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}>, "many">, "many">>;
|
||||||
|
ai_vectorStore: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
|
||||||
|
node: z.ZodString;
|
||||||
|
type: z.ZodString;
|
||||||
|
index: z.ZodNumber;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}>, "many">, "many">>;
|
||||||
|
}, z.ZodArray<z.ZodArray<z.ZodObject<{
|
||||||
|
node: z.ZodString;
|
||||||
|
type: z.ZodString;
|
||||||
|
index: z.ZodNumber;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}>, "many">, "many">, "strip">, z.objectInputType<{
|
||||||
|
main: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
|
||||||
|
node: z.ZodString;
|
||||||
|
type: z.ZodString;
|
||||||
|
index: z.ZodNumber;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}>, "many">, "many">>;
|
||||||
|
error: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
|
||||||
|
node: z.ZodString;
|
||||||
|
type: z.ZodString;
|
||||||
|
index: z.ZodNumber;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}>, "many">, "many">>;
|
||||||
|
ai_tool: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
|
||||||
|
node: z.ZodString;
|
||||||
|
type: z.ZodString;
|
||||||
|
index: z.ZodNumber;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}>, "many">, "many">>;
|
||||||
|
ai_languageModel: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
|
||||||
|
node: z.ZodString;
|
||||||
|
type: z.ZodString;
|
||||||
|
index: z.ZodNumber;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}>, "many">, "many">>;
|
||||||
|
ai_memory: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
|
||||||
|
node: z.ZodString;
|
||||||
|
type: z.ZodString;
|
||||||
|
index: z.ZodNumber;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}>, "many">, "many">>;
|
||||||
|
ai_embedding: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
|
||||||
|
node: z.ZodString;
|
||||||
|
type: z.ZodString;
|
||||||
|
index: z.ZodNumber;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}>, "many">, "many">>;
|
||||||
|
ai_vectorStore: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
|
||||||
|
node: z.ZodString;
|
||||||
|
type: z.ZodString;
|
||||||
|
index: z.ZodNumber;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}>, "many">, "many">>;
|
||||||
|
}, z.ZodArray<z.ZodArray<z.ZodObject<{
|
||||||
|
node: z.ZodString;
|
||||||
|
type: z.ZodString;
|
||||||
|
index: z.ZodNumber;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}, {
|
||||||
|
type: string;
|
||||||
|
node: string;
|
||||||
|
index: number;
|
||||||
|
}>, "many">, "many">, "strip">>>;
|
||||||
export declare const workflowSettingsSchema: z.ZodObject<{
|
export declare const workflowSettingsSchema: z.ZodObject<{
|
||||||
executionOrder: z.ZodDefault<z.ZodEnum<["v0", "v1"]>>;
|
executionOrder: z.ZodDefault<z.ZodEnum<["v0", "v1"]>>;
|
||||||
timezone: z.ZodOptional<z.ZodString>;
|
timezone: z.ZodOptional<z.ZodString>;
|
||||||
|
|||||||
2
dist/services/n8n-validation.d.ts.map
vendored
2
dist/services/n8n-validation.d.ts.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"n8n-validation.d.ts","sourceRoot":"","sources":["../../src/services/n8n-validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAM9E,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiB7B,CAAC;AAkBH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAUpC,CAAC;AAEF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWjC,CAAC;AAGH,eAAO,MAAM,uBAAuB;;;;;;CAMnC,CAAC;AAGF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,CAEhE;AAED,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,OAAO,GAAG,kBAAkB,CAEpF;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAElG;AAGD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAsBrF;AAiBD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoE5E;AAGD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,CA6P/E;AAGD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAK7D;AAMD,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,EAAE,CA+F5E;AAMD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CA0D/E;AAGD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAmB/D;AAGD,wBAAgB,2BAA2B,IAAI,MAAM,CA6CpD;AAGD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAmBpE"}
|
{"version":3,"file":"n8n-validation.d.ts","sourceRoot":"","sources":["../../src/services/n8n-validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAM9E,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiB7B,CAAC;AAkBH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAUpC,CAAC;AAEF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWjC,CAAC;AAGH,eAAO,MAAM,uBAAuB;;;;;;CAMnC,CAAC;AAGF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,CAEhE;AAED,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,OAAO,GAAG,kBAAkB,CAEpF;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAElG;AAGD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAsBrF;AAiBD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoE5E;AAGD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,CAkQ/E;AAGD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAK7D;AAMD,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,EAAE,CA+F5E;AAMD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CA0D/E;AAGD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAmB/D;AAGD,wBAAgB,2BAA2B,IAAI,MAAM,CA6CpD;AAGD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAmBpE"}
|
||||||
44
dist/services/n8n-validation.js
vendored
44
dist/services/n8n-validation.js
vendored
@@ -47,7 +47,7 @@ exports.workflowConnectionSchema = zod_1.z.record(zod_1.z.object({
|
|||||||
ai_memory: connectionArraySchema.optional(),
|
ai_memory: connectionArraySchema.optional(),
|
||||||
ai_embedding: connectionArraySchema.optional(),
|
ai_embedding: connectionArraySchema.optional(),
|
||||||
ai_vectorStore: connectionArraySchema.optional(),
|
ai_vectorStore: connectionArraySchema.optional(),
|
||||||
}));
|
}).catchall(connectionArraySchema));
|
||||||
exports.workflowSettingsSchema = zod_1.z.object({
|
exports.workflowSettingsSchema = zod_1.z.object({
|
||||||
executionOrder: zod_1.z.enum(['v0', 'v1']).default('v1'),
|
executionOrder: zod_1.z.enum(['v0', 'v1']).default('v1'),
|
||||||
timezone: zod_1.z.string().optional(),
|
timezone: zod_1.z.string().optional(),
|
||||||
@@ -152,11 +152,10 @@ function validateWorkflowStructure(workflow) {
|
|||||||
}
|
}
|
||||||
else if (connectionCount > 0 || executableNodes.length > 1) {
|
else if (connectionCount > 0 || executableNodes.length > 1) {
|
||||||
const connectedNodes = new Set();
|
const connectedNodes = new Set();
|
||||||
const ALL_CONNECTION_TYPES = ['main', 'error', 'ai_tool', 'ai_languageModel', 'ai_memory', 'ai_embedding', 'ai_vectorStore'];
|
|
||||||
Object.entries(workflow.connections).forEach(([sourceName, connection]) => {
|
Object.entries(workflow.connections).forEach(([sourceName, connection]) => {
|
||||||
connectedNodes.add(sourceName);
|
connectedNodes.add(sourceName);
|
||||||
ALL_CONNECTION_TYPES.forEach(connType => {
|
const connectionRecord = connection;
|
||||||
const connData = connection[connType];
|
Object.values(connectionRecord).forEach((connData) => {
|
||||||
if (connData && Array.isArray(connData)) {
|
if (connData && Array.isArray(connData)) {
|
||||||
connData.forEach((outputs) => {
|
connData.forEach((outputs) => {
|
||||||
if (Array.isArray(outputs)) {
|
if (Array.isArray(outputs)) {
|
||||||
@@ -282,23 +281,28 @@ function validateWorkflowStructure(workflow) {
|
|||||||
errors.push(`Connection references non-existent node: ${sourceName}`);
|
errors.push(`Connection references non-existent node: ${sourceName}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (connection.main && Array.isArray(connection.main)) {
|
const connectionRecord = connection;
|
||||||
connection.main.forEach((outputs, outputIndex) => {
|
Object.values(connectionRecord).forEach((connData) => {
|
||||||
if (Array.isArray(outputs)) {
|
if (connData && Array.isArray(connData)) {
|
||||||
outputs.forEach((target, targetIndex) => {
|
connData.forEach((outputs, outputIndex) => {
|
||||||
if (!nodeNames.has(target.node)) {
|
if (Array.isArray(outputs)) {
|
||||||
if (nodeIds.has(target.node)) {
|
outputs.forEach((target, targetIndex) => {
|
||||||
const correctName = nodeIdToName.get(target.node);
|
if (!target?.node)
|
||||||
errors.push(`Connection target uses node ID '${target.node}' but must use node name '${correctName}' (from ${sourceName}[${outputIndex}][${targetIndex}])`);
|
return;
|
||||||
|
if (!nodeNames.has(target.node)) {
|
||||||
|
if (nodeIds.has(target.node)) {
|
||||||
|
const correctName = nodeIdToName.get(target.node);
|
||||||
|
errors.push(`Connection target uses node ID '${target.node}' but must use node name '${correctName}' (from ${sourceName}[${outputIndex}][${targetIndex}])`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
errors.push(`Connection references non-existent target node: ${target.node} (from ${sourceName}[${outputIndex}][${targetIndex}])`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
});
|
||||||
errors.push(`Connection references non-existent target node: ${target.node} (from ${sourceName}[${outputIndex}][${targetIndex}])`);
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return errors;
|
return errors;
|
||||||
|
|||||||
2
dist/services/n8n-validation.js.map
vendored
2
dist/services/n8n-validation.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/services/node-sanitizer.d.ts.map
vendored
2
dist/services/node-sanitizer.d.ts.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"node-sanitizer.d.ts","sourceRoot":"","sources":["../../src/services/node-sanitizer.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAKhD,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,YAAY,CAa7D;AAKD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,GAAG,GAAG,GAAG,CASxD;AAoND,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,EAAE,CAgEjE"}
|
{"version":3,"file":"node-sanitizer.d.ts","sourceRoot":"","sources":["../../src/services/node-sanitizer.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAKhD,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,YAAY,CAa7D;AAKD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,GAAG,GAAG,GAAG,CASxD;AA6ND,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,EAAE,CAgEjE"}
|
||||||
14
dist/services/node-sanitizer.js
vendored
14
dist/services/node-sanitizer.js
vendored
@@ -17,7 +17,7 @@ function sanitizeWorkflowNodes(workflow) {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...workflow,
|
...workflow,
|
||||||
nodes: workflow.nodes.map((node) => sanitizeNode(node))
|
nodes: workflow.nodes.map(sanitizeNode)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function isFilterBasedNode(nodeType, typeVersion) {
|
function isFilterBasedNode(nodeType, typeVersion) {
|
||||||
@@ -66,7 +66,7 @@ function sanitizeFilterConditions(conditions) {
|
|||||||
...sanitized.options
|
...sanitized.options
|
||||||
};
|
};
|
||||||
if (sanitized.conditions && Array.isArray(sanitized.conditions)) {
|
if (sanitized.conditions && Array.isArray(sanitized.conditions)) {
|
||||||
sanitized.conditions = sanitized.conditions.map((condition) => sanitizeCondition(condition));
|
sanitized.conditions = sanitized.conditions.map(sanitizeCondition);
|
||||||
}
|
}
|
||||||
return sanitized;
|
return sanitized;
|
||||||
}
|
}
|
||||||
@@ -124,6 +124,10 @@ function inferDataType(operation) {
|
|||||||
if (dateOps.some(op => operation.includes(op))) {
|
if (dateOps.some(op => operation.includes(op))) {
|
||||||
return 'dateTime';
|
return 'dateTime';
|
||||||
}
|
}
|
||||||
|
const objectOps = ['empty', 'notEmpty', 'exists', 'notExists'];
|
||||||
|
if (objectOps.includes(operation)) {
|
||||||
|
return 'object';
|
||||||
|
}
|
||||||
return 'string';
|
return 'string';
|
||||||
}
|
}
|
||||||
function isUnaryOperator(operation) {
|
function isUnaryOperator(operation) {
|
||||||
@@ -132,7 +136,11 @@ function isUnaryOperator(operation) {
|
|||||||
'isNotEmpty',
|
'isNotEmpty',
|
||||||
'true',
|
'true',
|
||||||
'false',
|
'false',
|
||||||
'isNumeric'
|
'isNumeric',
|
||||||
|
'empty',
|
||||||
|
'notEmpty',
|
||||||
|
'exists',
|
||||||
|
'notExists'
|
||||||
];
|
];
|
||||||
return unaryOps.includes(operation);
|
return unaryOps.includes(operation);
|
||||||
}
|
}
|
||||||
|
|||||||
2
dist/services/node-sanitizer.js.map
vendored
2
dist/services/node-sanitizer.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"node-specific-validators.d.ts","sourceRoot":"","sources":["../../src/services/node-specific-validators.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAExE,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAED,qBAAa,sBAAsB;IAIjC,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAqE1D,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAkDvC,OAAO,CAAC,MAAM,CAAC,0BAA0B;IAsBzC,OAAO,CAAC,MAAM,CAAC,0BAA0B;IA4BzC,OAAO,CAAC,MAAM,CAAC,0BAA0B;IA2CzC,MAAM,CAAC,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAiDjE,OAAO,CAAC,MAAM,CAAC,0BAA0B;IA0BzC,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAkBvC,OAAO,CAAC,MAAM,CAAC,0BAA0B;IAsBzC,OAAO,CAAC,MAAM,CAAC,0BAA0B;IA4BzC,OAAO,CAAC,MAAM,CAAC,yBAAyB;IAwCxC,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAwF3D,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAyG5D,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAkI7D,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAmG5D,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IA6F1D,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAkF/B,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAiGhE,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IA6D5D,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IA8DzD,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAuDrC,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAuDjC,OAAO,CAAC,MAAM,CAAC,uBAAuB;IAsFtC,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAuKnC,OAAO,CAAC,MAAM,CAAC,oBAAoB;IA2EnC,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;CAoDzD"}
|
{"version":3,"file":"node-specific-validators.d.ts","sourceRoot":"","sources":["../../src/services/node-specific-validators.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAExE,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAED,qBAAa,sBAAsB;IAIjC,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAqE1D,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAkDvC,OAAO,CAAC,MAAM,CAAC,0BAA0B;IAsBzC,OAAO,CAAC,MAAM,CAAC,0BAA0B;IA4BzC,OAAO,CAAC,MAAM,CAAC,0BAA0B;IA2CzC,MAAM,CAAC,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAiDjE,OAAO,CAAC,MAAM,CAAC,0BAA0B;IA0BzC,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAkBvC,OAAO,CAAC,MAAM,CAAC,0BAA0B;IAsBzC,OAAO,CAAC,MAAM,CAAC,0BAA0B;IA4BzC,OAAO,CAAC,MAAM,CAAC,yBAAyB;IAwCxC,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAwF3D,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAyG5D,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAkI7D,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAmG5D,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IA6F1D,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAkF/B,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAiGhE,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IA6D5D,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IA8DzD,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAuDrC,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAuDjC,OAAO,CAAC,MAAM,CAAC,uBAAuB;IAyFtC,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAuKnC,OAAO,CAAC,MAAM,CAAC,oBAAoB;IA2EnC,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;CAoDzD"}
|
||||||
5
dist/services/node-specific-validators.js
vendored
5
dist/services/node-specific-validators.js
vendored
@@ -1067,7 +1067,8 @@ class NodeSpecificValidators {
|
|||||||
fix: 'Wrap in array: return [{json: yourObject}]'
|
fix: 'Wrap in array: return [{json: yourObject}]'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (/return\s+(true|false|null|undefined|\d+|['"`])/m.test(code)) {
|
const hasHelperFunctions = /(?:function\s+\w+\s*\(|(?:const|let|var)\s+\w+\s*=\s*(?:async\s+)?(?:function|\([^)]*\)\s*=>|\w+\s*=>))/.test(code);
|
||||||
|
if (!hasHelperFunctions && /return\s+(true|false|null|undefined|\d+|['"`])/m.test(code)) {
|
||||||
errors.push({
|
errors.push({
|
||||||
type: 'invalid_value',
|
type: 'invalid_value',
|
||||||
property: 'jsCode',
|
property: 'jsCode',
|
||||||
@@ -1149,7 +1150,7 @@ class NodeSpecificValidators {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (language === 'javaScript') {
|
if (language === 'javaScript') {
|
||||||
if (/\$(?![a-zA-Z])/.test(code) && !code.includes('${')) {
|
if (/\$(?![a-zA-Z_(])/.test(code) && !code.includes('${')) {
|
||||||
warnings.push({
|
warnings.push({
|
||||||
type: 'best_practice',
|
type: 'best_practice',
|
||||||
message: 'Invalid $ usage detected',
|
message: 'Invalid $ usage detected',
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
2
dist/services/workflow-auto-fixer.d.ts.map
vendored
2
dist/services/workflow-auto-fixer.d.ts.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"workflow-auto-fixer.d.ts","sourceRoot":"","sources":["../../src/services/workflow-auto-fixer.ts"],"names":[],"mappings":"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"}
|
{"version":3,"file":"workflow-auto-fixer.d.ts","sourceRoot":"","sources":["../../src/services/workflow-auto-fixer.ts"],"names":[],"mappings":"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"}
|
||||||
3
dist/services/workflow-auto-fixer.js
vendored
3
dist/services/workflow-auto-fixer.js
vendored
@@ -405,7 +405,7 @@ class WorkflowAutoFixer {
|
|||||||
const hasConnectionFixes = filteredFixes.some(f => exports.CONNECTION_FIX_TYPES.includes(f.type));
|
const hasConnectionFixes = filteredFixes.some(f => exports.CONNECTION_FIX_TYPES.includes(f.type));
|
||||||
return operations.filter(op => {
|
return operations.filter(op => {
|
||||||
if (op.type === 'updateNode') {
|
if (op.type === 'updateNode') {
|
||||||
return fixedNodes.has(op.nodeId || '');
|
return fixedNodes.has(op.nodeName || '') || fixedNodes.has(op.nodeId || '');
|
||||||
}
|
}
|
||||||
if (op.type === 'replaceConnections') {
|
if (op.type === 'replaceConnections') {
|
||||||
return hasConnectionFixes;
|
return hasConnectionFixes;
|
||||||
@@ -794,6 +794,7 @@ class WorkflowAutoFixer {
|
|||||||
const operation = {
|
const operation = {
|
||||||
type: 'updateNode',
|
type: 'updateNode',
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
|
nodeName: node.name,
|
||||||
updates: {
|
updates: {
|
||||||
typeVersion: parseFloat(latestVersion),
|
typeVersion: parseFloat(latestVersion),
|
||||||
parameters: migrationResult.updatedNode.parameters,
|
parameters: migrationResult.updatedNode.parameters,
|
||||||
|
|||||||
2
dist/services/workflow-auto-fixer.js.map
vendored
2
dist/services/workflow-auto-fixer.js.map
vendored
File diff suppressed because one or more lines are too long
7
dist/services/workflow-diff-engine.d.ts
vendored
7
dist/services/workflow-diff-engine.d.ts
vendored
@@ -3,6 +3,11 @@ import { Workflow } from '../types/n8n-api';
|
|||||||
export declare class WorkflowDiffEngine {
|
export declare class WorkflowDiffEngine {
|
||||||
private renameMap;
|
private renameMap;
|
||||||
private warnings;
|
private warnings;
|
||||||
|
private modifiedNodeIds;
|
||||||
|
private removedNodeNames;
|
||||||
|
private tagsToAdd;
|
||||||
|
private tagsToRemove;
|
||||||
|
private transferToProjectId;
|
||||||
applyDiff(workflow: Workflow, request: WorkflowDiffRequest): Promise<WorkflowDiffResult>;
|
applyDiff(workflow: Workflow, request: WorkflowDiffRequest): Promise<WorkflowDiffResult>;
|
||||||
private validateOperation;
|
private validateOperation;
|
||||||
private applyOperation;
|
private applyOperation;
|
||||||
@@ -32,6 +37,8 @@ export declare class WorkflowDiffEngine {
|
|||||||
private validateDeactivateWorkflow;
|
private validateDeactivateWorkflow;
|
||||||
private applyActivateWorkflow;
|
private applyActivateWorkflow;
|
||||||
private applyDeactivateWorkflow;
|
private applyDeactivateWorkflow;
|
||||||
|
private validateTransferWorkflow;
|
||||||
|
private applyTransferWorkflow;
|
||||||
private validateCleanStaleConnections;
|
private validateCleanStaleConnections;
|
||||||
private validateReplaceConnections;
|
private validateReplaceConnections;
|
||||||
private applyCleanStaleConnections;
|
private applyCleanStaleConnections;
|
||||||
|
|||||||
2
dist/services/workflow-diff-engine.d.ts.map
vendored
2
dist/services/workflow-diff-engine.d.ts.map
vendored
@@ -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,EAsBnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAoC,MAAM,kBAAkB,CAAC;AAQ9E,qBAAa,kBAAkB;IAE7B,OAAO,CAAC,SAAS,CAAkC;IAEnD,OAAO,CAAC,QAAQ,CAAqC;IAK/C,SAAS,CACb,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC;IA0M9B,OAAO,CAAC,iBAAiB;IAwCzB,OAAO,CAAC,cAAc;IAyDtB,OAAO,CAAC,eAAe;IAwBvB,OAAO,CAAC,kBAAkB;IAuB1B,OAAO,CAAC,kBAAkB;IAoC1B,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,qBAAqB;IAkD7B,OAAO,CAAC,wBAAwB;IAuChC,OAAO,CAAC,wBAAwB;IAmDhC,OAAO,CAAC,YAAY;IA2BpB,OAAO,CAAC,eAAe;IAkCvB,OAAO,CAAC,eAAe;IAwBvB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,sBAAsB;IAgD9B,OAAO,CAAC,kBAAkB;IA4C1B,OAAO,CAAC,qBAAqB;IA2C7B,OAAO,CAAC,qBAAqB;IA0B7B,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,wBAAwB;IAchC,OAAO,CAAC,0BAA0B;IAMlC,OAAO,CAAC,qBAAqB;IAM7B,OAAO,CAAC,uBAAuB;IAO/B,OAAO,CAAC,6BAA6B;IAKrC,OAAO,CAAC,0BAA0B;IA0BlC,OAAO,CAAC,0BAA0B;IA0ElC,OAAO,CAAC,uBAAuB;IAe/B,OAAO,CAAC,0BAA0B;IAkElC,OAAO,CAAC,iBAAiB;IAkBzB,OAAO,CAAC,QAAQ;IAsChB,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,iBAAiB;CAc1B"}
|
{"version":3,"file":"workflow-diff-engine.d.ts","sourceRoot":"","sources":["../../src/services/workflow-diff-engine.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,mBAAmB,EACnB,kBAAkB,EAuBnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAoC,MAAM,kBAAkB,CAAC;AAY9E,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;IAK1C,SAAS,CACb,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC;IAgO9B,OAAO,CAAC,iBAAiB;IA0CzB,OAAO,CAAC,cAAc;IA4DtB,OAAO,CAAC,eAAe;IAwBvB,OAAO,CAAC,kBAAkB;IAuB1B,OAAO,CAAC,kBAAkB;IAoC1B,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,qBAAqB;IAkD7B,OAAO,CAAC,wBAAwB;IA6ChC,OAAO,CAAC,wBAAwB;IAmDhC,OAAO,CAAC,YAAY;IA4BpB,OAAO,CAAC,eAAe;IAwCvB,OAAO,CAAC,eAAe;IA0BvB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,sBAAsB;IAwD9B,OAAO,CAAC,kBAAkB;IA6C1B,OAAO,CAAC,qBAAqB;IAuC7B,OAAO,CAAC,qBAAqB;IA0B7B,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,wBAAwB;IAchC,OAAO,CAAC,0BAA0B;IAMlC,OAAO,CAAC,qBAAqB;IAM7B,OAAO,CAAC,uBAAuB;IAO/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,0BAA0B;IAmElC,OAAO,CAAC,iBAAiB;IAkBzB,OAAO,CAAC,QAAQ;IAsChB,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,iBAAiB;CAoB1B"}
|
||||||
132
dist/services/workflow-diff-engine.js
vendored
132
dist/services/workflow-diff-engine.js
vendored
@@ -10,11 +10,20 @@ class WorkflowDiffEngine {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.renameMap = new Map();
|
this.renameMap = new Map();
|
||||||
this.warnings = [];
|
this.warnings = [];
|
||||||
|
this.modifiedNodeIds = new Set();
|
||||||
|
this.removedNodeNames = new Set();
|
||||||
|
this.tagsToAdd = [];
|
||||||
|
this.tagsToRemove = [];
|
||||||
}
|
}
|
||||||
async applyDiff(workflow, request) {
|
async applyDiff(workflow, request) {
|
||||||
try {
|
try {
|
||||||
this.renameMap.clear();
|
this.renameMap.clear();
|
||||||
this.warnings = [];
|
this.warnings = [];
|
||||||
|
this.modifiedNodeIds.clear();
|
||||||
|
this.removedNodeNames.clear();
|
||||||
|
this.tagsToAdd = [];
|
||||||
|
this.tagsToRemove = [];
|
||||||
|
this.transferToProjectId = undefined;
|
||||||
const workflowCopy = JSON.parse(JSON.stringify(workflow));
|
const workflowCopy = JSON.parse(JSON.stringify(workflow));
|
||||||
const nodeOperationTypes = ['addNode', 'removeNode', 'updateNode', 'moveNode', 'enableNode', 'disableNode'];
|
const nodeOperationTypes = ['addNode', 'removeNode', 'updateNode', 'moveNode', 'enableNode', 'disableNode'];
|
||||||
const nodeOperations = [];
|
const nodeOperations = [];
|
||||||
@@ -73,6 +82,10 @@ class WorkflowDiffEngine {
|
|||||||
failed: failedIndices
|
failed: failedIndices
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
const shouldActivate = workflowCopy._shouldActivate === true;
|
||||||
|
const shouldDeactivate = workflowCopy._shouldDeactivate === true;
|
||||||
|
delete workflowCopy._shouldActivate;
|
||||||
|
delete workflowCopy._shouldDeactivate;
|
||||||
const success = appliedIndices.length > 0;
|
const success = appliedIndices.length > 0;
|
||||||
return {
|
return {
|
||||||
success,
|
success,
|
||||||
@@ -82,7 +95,12 @@ class WorkflowDiffEngine {
|
|||||||
errors: errors.length > 0 ? errors : undefined,
|
errors: errors.length > 0 ? errors : undefined,
|
||||||
warnings: this.warnings.length > 0 ? this.warnings : undefined,
|
warnings: this.warnings.length > 0 ? this.warnings : undefined,
|
||||||
applied: appliedIndices,
|
applied: appliedIndices,
|
||||||
failed: failedIndices
|
failed: failedIndices,
|
||||||
|
shouldActivate: shouldActivate || undefined,
|
||||||
|
shouldDeactivate: shouldDeactivate || undefined,
|
||||||
|
tagsToAdd: this.tagsToAdd.length > 0 ? this.tagsToAdd : undefined,
|
||||||
|
tagsToRemove: this.tagsToRemove.length > 0 ? this.tagsToRemove : undefined,
|
||||||
|
transferToProjectId: this.transferToProjectId || undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -142,8 +160,15 @@ class WorkflowDiffEngine {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
workflowCopy.nodes = workflowCopy.nodes.map((node) => (0, node_sanitizer_1.sanitizeNode)(node));
|
if (this.modifiedNodeIds.size > 0) {
|
||||||
logger.debug('Applied full-workflow sanitization to all nodes');
|
workflowCopy.nodes = workflowCopy.nodes.map((node) => {
|
||||||
|
if (this.modifiedNodeIds.has(node.id)) {
|
||||||
|
return (0, node_sanitizer_1.sanitizeNode)(node);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
logger.debug(`Sanitized ${this.modifiedNodeIds.size} modified nodes`);
|
||||||
|
}
|
||||||
if (request.validateOnly) {
|
if (request.validateOnly) {
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@@ -162,7 +187,10 @@ class WorkflowDiffEngine {
|
|||||||
message: `Successfully applied ${operationsApplied} operations (${nodeOperations.length} node ops, ${otherOperations.length} other ops)`,
|
message: `Successfully applied ${operationsApplied} operations (${nodeOperations.length} node ops, ${otherOperations.length} other ops)`,
|
||||||
warnings: this.warnings.length > 0 ? this.warnings : undefined,
|
warnings: this.warnings.length > 0 ? this.warnings : undefined,
|
||||||
shouldActivate: shouldActivate || undefined,
|
shouldActivate: shouldActivate || undefined,
|
||||||
shouldDeactivate: shouldDeactivate || undefined
|
shouldDeactivate: shouldDeactivate || undefined,
|
||||||
|
tagsToAdd: this.tagsToAdd.length > 0 ? this.tagsToAdd : undefined,
|
||||||
|
tagsToRemove: this.tagsToRemove.length > 0 ? this.tagsToRemove : undefined,
|
||||||
|
transferToProjectId: this.transferToProjectId || undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,6 +229,8 @@ class WorkflowDiffEngine {
|
|||||||
case 'addTag':
|
case 'addTag':
|
||||||
case 'removeTag':
|
case 'removeTag':
|
||||||
return null;
|
return null;
|
||||||
|
case 'transferWorkflow':
|
||||||
|
return this.validateTransferWorkflow(workflow, operation);
|
||||||
case 'activateWorkflow':
|
case 'activateWorkflow':
|
||||||
return this.validateActivateWorkflow(workflow, operation);
|
return this.validateActivateWorkflow(workflow, operation);
|
||||||
case 'deactivateWorkflow':
|
case 'deactivateWorkflow':
|
||||||
@@ -266,6 +296,9 @@ class WorkflowDiffEngine {
|
|||||||
case 'replaceConnections':
|
case 'replaceConnections':
|
||||||
this.applyReplaceConnections(workflow, operation);
|
this.applyReplaceConnections(workflow, operation);
|
||||||
break;
|
break;
|
||||||
|
case 'transferWorkflow':
|
||||||
|
this.applyTransferWorkflow(workflow, operation);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
validateAddNode(workflow, operation) {
|
validateAddNode(workflow, operation) {
|
||||||
@@ -302,7 +335,7 @@ class WorkflowDiffEngine {
|
|||||||
return `Invalid parameter 'changes'. The updateNode operation requires 'updates' (not 'changes'). Example: {type: "updateNode", nodeId: "abc", updates: {name: "New Name", "parameters.url": "https://example.com"}}`;
|
return `Invalid parameter 'changes'. The updateNode operation requires 'updates' (not 'changes'). Example: {type: "updateNode", nodeId: "abc", updates: {name: "New Name", "parameters.url": "https://example.com"}}`;
|
||||||
}
|
}
|
||||||
if (!operation.updates) {
|
if (!operation.updates) {
|
||||||
return `Missing required parameter 'updates'. The updateNode operation requires an 'updates' object containing properties to modify. Example: {type: "updateNode", nodeId: "abc", updates: {name: "New Name"}}`;
|
return `Missing required parameter 'updates'. The updateNode operation requires an 'updates' object. Correct structure: {type: "updateNode", nodeId: "abc-123" OR nodeName: "My Node", updates: {name: "New Name", "parameters.url": "https://example.com"}}`;
|
||||||
}
|
}
|
||||||
const node = this.findNode(workflow, operation.nodeId, operation.nodeName);
|
const node = this.findNode(workflow, operation.nodeId, operation.nodeName);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
@@ -382,12 +415,18 @@ class WorkflowDiffEngine {
|
|||||||
const sourceNode = this.findNode(workflow, operation.source, operation.source);
|
const sourceNode = this.findNode(workflow, operation.source, operation.source);
|
||||||
const targetNode = this.findNode(workflow, operation.target, operation.target);
|
const targetNode = this.findNode(workflow, operation.target, operation.target);
|
||||||
if (!sourceNode) {
|
if (!sourceNode) {
|
||||||
|
if (this.removedNodeNames.has(operation.source)) {
|
||||||
|
return `Source node "${operation.source}" was already removed by a prior removeNode operation. Its connections were automatically cleaned up — no separate removeConnection needed.`;
|
||||||
|
}
|
||||||
const availableNodes = workflow.nodes
|
const availableNodes = workflow.nodes
|
||||||
.map(n => `"${n.name}" (id: ${n.id.substring(0, 8)}...)`)
|
.map(n => `"${n.name}" (id: ${n.id.substring(0, 8)}...)`)
|
||||||
.join(', ');
|
.join(', ');
|
||||||
return `Source node not found: "${operation.source}". Available nodes: ${availableNodes}. Tip: Use node ID for names with special characters.`;
|
return `Source node not found: "${operation.source}". Available nodes: ${availableNodes}. Tip: Use node ID for names with special characters.`;
|
||||||
}
|
}
|
||||||
if (!targetNode) {
|
if (!targetNode) {
|
||||||
|
if (this.removedNodeNames.has(operation.target)) {
|
||||||
|
return `Target node "${operation.target}" was already removed by a prior removeNode operation. Its connections were automatically cleaned up — no separate removeConnection needed.`;
|
||||||
|
}
|
||||||
const availableNodes = workflow.nodes
|
const availableNodes = workflow.nodes
|
||||||
.map(n => `"${n.name}" (id: ${n.id.substring(0, 8)}...)`)
|
.map(n => `"${n.name}" (id: ${n.id.substring(0, 8)}...)`)
|
||||||
.join(', ');
|
.join(', ');
|
||||||
@@ -461,34 +500,40 @@ class WorkflowDiffEngine {
|
|||||||
executeOnce: operation.node.executeOnce
|
executeOnce: operation.node.executeOnce
|
||||||
};
|
};
|
||||||
const sanitizedNode = (0, node_sanitizer_1.sanitizeNode)(newNode);
|
const sanitizedNode = (0, node_sanitizer_1.sanitizeNode)(newNode);
|
||||||
|
this.modifiedNodeIds.add(sanitizedNode.id);
|
||||||
workflow.nodes.push(sanitizedNode);
|
workflow.nodes.push(sanitizedNode);
|
||||||
}
|
}
|
||||||
applyRemoveNode(workflow, operation) {
|
applyRemoveNode(workflow, operation) {
|
||||||
const node = this.findNode(workflow, operation.nodeId, operation.nodeName);
|
const node = this.findNode(workflow, operation.nodeId, operation.nodeName);
|
||||||
if (!node)
|
if (!node)
|
||||||
return;
|
return;
|
||||||
|
this.removedNodeNames.add(node.name);
|
||||||
const index = workflow.nodes.findIndex(n => n.id === node.id);
|
const index = workflow.nodes.findIndex(n => n.id === node.id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
workflow.nodes.splice(index, 1);
|
workflow.nodes.splice(index, 1);
|
||||||
}
|
}
|
||||||
delete workflow.connections[node.name];
|
delete workflow.connections[node.name];
|
||||||
Object.keys(workflow.connections).forEach(sourceName => {
|
for (const [sourceName, sourceConnections] of Object.entries(workflow.connections)) {
|
||||||
const sourceConnections = workflow.connections[sourceName];
|
for (const [outputName, outputConns] of Object.entries(sourceConnections)) {
|
||||||
Object.keys(sourceConnections).forEach(outputName => {
|
sourceConnections[outputName] = outputConns.map(connections => connections.filter(conn => conn.node !== node.name));
|
||||||
sourceConnections[outputName] = sourceConnections[outputName].map(connections => connections.filter(conn => conn.node !== node.name)).filter(connections => connections.length > 0);
|
const trimmed = sourceConnections[outputName];
|
||||||
if (sourceConnections[outputName].length === 0) {
|
while (trimmed.length > 0 && trimmed[trimmed.length - 1].length === 0) {
|
||||||
|
trimmed.pop();
|
||||||
|
}
|
||||||
|
if (trimmed.length === 0) {
|
||||||
delete sourceConnections[outputName];
|
delete sourceConnections[outputName];
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
if (Object.keys(sourceConnections).length === 0) {
|
if (Object.keys(sourceConnections).length === 0) {
|
||||||
delete workflow.connections[sourceName];
|
delete workflow.connections[sourceName];
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
applyUpdateNode(workflow, operation) {
|
applyUpdateNode(workflow, operation) {
|
||||||
const node = this.findNode(workflow, operation.nodeId, operation.nodeName);
|
const node = this.findNode(workflow, operation.nodeId, operation.nodeName);
|
||||||
if (!node)
|
if (!node)
|
||||||
return;
|
return;
|
||||||
|
this.modifiedNodeIds.add(node.id);
|
||||||
if (operation.updates.name && operation.updates.name !== node.name) {
|
if (operation.updates.name && operation.updates.name !== node.name) {
|
||||||
const oldName = node.name;
|
const oldName = node.name;
|
||||||
const newName = operation.updates.name;
|
const newName = operation.updates.name;
|
||||||
@@ -521,8 +566,13 @@ class WorkflowDiffEngine {
|
|||||||
}
|
}
|
||||||
resolveSmartParameters(workflow, operation) {
|
resolveSmartParameters(workflow, operation) {
|
||||||
const sourceNode = this.findNode(workflow, operation.source, operation.source);
|
const sourceNode = this.findNode(workflow, operation.source, operation.source);
|
||||||
let sourceOutput = operation.sourceOutput ?? 'main';
|
let sourceOutput = String(operation.sourceOutput ?? 'main');
|
||||||
let sourceIndex = operation.sourceIndex ?? 0;
|
let sourceIndex = operation.sourceIndex ?? 0;
|
||||||
|
if (/^\d+$/.test(sourceOutput) && operation.sourceIndex === undefined
|
||||||
|
&& operation.branch === undefined && operation.case === undefined) {
|
||||||
|
sourceIndex = parseInt(sourceOutput, 10);
|
||||||
|
sourceOutput = 'main';
|
||||||
|
}
|
||||||
if (operation.branch !== undefined && operation.sourceIndex === undefined) {
|
if (operation.branch !== undefined && operation.sourceIndex === undefined) {
|
||||||
if (sourceNode?.type === 'n8n-nodes-base.if') {
|
if (sourceNode?.type === 'n8n-nodes-base.if') {
|
||||||
sourceIndex = operation.branch === 'true' ? 0 : 1;
|
sourceIndex = operation.branch === 'true' ? 0 : 1;
|
||||||
@@ -556,7 +606,7 @@ class WorkflowDiffEngine {
|
|||||||
if (!sourceNode || !targetNode)
|
if (!sourceNode || !targetNode)
|
||||||
return;
|
return;
|
||||||
const { sourceOutput, sourceIndex } = this.resolveSmartParameters(workflow, operation);
|
const { sourceOutput, sourceIndex } = this.resolveSmartParameters(workflow, operation);
|
||||||
const targetInput = operation.targetInput ?? sourceOutput;
|
const targetInput = String(operation.targetInput ?? sourceOutput);
|
||||||
const targetIndex = operation.targetIndex ?? 0;
|
const targetIndex = operation.targetIndex ?? 0;
|
||||||
if (!workflow.connections[sourceNode.name]) {
|
if (!workflow.connections[sourceNode.name]) {
|
||||||
workflow.connections[sourceNode.name] = {};
|
workflow.connections[sourceNode.name] = {};
|
||||||
@@ -581,12 +631,9 @@ class WorkflowDiffEngine {
|
|||||||
const sourceNode = this.findNode(workflow, operation.source, operation.source);
|
const sourceNode = this.findNode(workflow, operation.source, operation.source);
|
||||||
const targetNode = this.findNode(workflow, operation.target, operation.target);
|
const targetNode = this.findNode(workflow, operation.target, operation.target);
|
||||||
if (!sourceNode || !targetNode) {
|
if (!sourceNode || !targetNode) {
|
||||||
if (operation.ignoreErrors) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const sourceOutput = operation.sourceOutput || 'main';
|
const sourceOutput = String(operation.sourceOutput ?? 'main');
|
||||||
const connections = workflow.connections[sourceNode.name]?.[sourceOutput];
|
const connections = workflow.connections[sourceNode.name]?.[sourceOutput];
|
||||||
if (!connections)
|
if (!connections)
|
||||||
return;
|
return;
|
||||||
@@ -633,19 +680,21 @@ class WorkflowDiffEngine {
|
|||||||
workflow.name = operation.name;
|
workflow.name = operation.name;
|
||||||
}
|
}
|
||||||
applyAddTag(workflow, operation) {
|
applyAddTag(workflow, operation) {
|
||||||
if (!workflow.tags) {
|
const removeIdx = this.tagsToRemove.indexOf(operation.tag);
|
||||||
workflow.tags = [];
|
if (removeIdx !== -1) {
|
||||||
|
this.tagsToRemove.splice(removeIdx, 1);
|
||||||
}
|
}
|
||||||
if (!workflow.tags.includes(operation.tag)) {
|
if (!this.tagsToAdd.includes(operation.tag)) {
|
||||||
workflow.tags.push(operation.tag);
|
this.tagsToAdd.push(operation.tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
applyRemoveTag(workflow, operation) {
|
applyRemoveTag(workflow, operation) {
|
||||||
if (!workflow.tags)
|
const addIdx = this.tagsToAdd.indexOf(operation.tag);
|
||||||
return;
|
if (addIdx !== -1) {
|
||||||
const index = workflow.tags.indexOf(operation.tag);
|
this.tagsToAdd.splice(addIdx, 1);
|
||||||
if (index !== -1) {
|
}
|
||||||
workflow.tags.splice(index, 1);
|
if (!this.tagsToRemove.includes(operation.tag)) {
|
||||||
|
this.tagsToRemove.push(operation.tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
validateActivateWorkflow(workflow, operation) {
|
validateActivateWorkflow(workflow, operation) {
|
||||||
@@ -664,6 +713,15 @@ class WorkflowDiffEngine {
|
|||||||
applyDeactivateWorkflow(workflow, operation) {
|
applyDeactivateWorkflow(workflow, operation) {
|
||||||
workflow._shouldDeactivate = true;
|
workflow._shouldDeactivate = true;
|
||||||
}
|
}
|
||||||
|
validateTransferWorkflow(_workflow, operation) {
|
||||||
|
if (!operation.destinationProjectId) {
|
||||||
|
return 'transferWorkflow requires a non-empty destinationProjectId string';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
applyTransferWorkflow(_workflow, operation) {
|
||||||
|
this.transferToProjectId = operation.destinationProjectId;
|
||||||
|
}
|
||||||
validateCleanStaleConnections(workflow, operation) {
|
validateCleanStaleConnections(workflow, operation) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -734,7 +792,10 @@ class WorkflowDiffEngine {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
})).filter(conns => conns.length > 0);
|
}));
|
||||||
|
while (filteredConnections.length > 0 && filteredConnections[filteredConnections.length - 1].length === 0) {
|
||||||
|
filteredConnections.pop();
|
||||||
|
}
|
||||||
if (filteredConnections.length === 0) {
|
if (filteredConnections.length === 0) {
|
||||||
delete outputs[outputName];
|
delete outputs[outputName];
|
||||||
}
|
}
|
||||||
@@ -768,9 +829,10 @@ class WorkflowDiffEngine {
|
|||||||
for (let connIndex = 0; connIndex < connectionsAtIndex.length; connIndex++) {
|
for (let connIndex = 0; connIndex < connectionsAtIndex.length; connIndex++) {
|
||||||
const connection = connectionsAtIndex[connIndex];
|
const connection = connectionsAtIndex[connIndex];
|
||||||
if (renames.has(connection.node)) {
|
if (renames.has(connection.node)) {
|
||||||
|
const oldTargetName = connection.node;
|
||||||
const newTargetName = renames.get(connection.node);
|
const newTargetName = renames.get(connection.node);
|
||||||
connection.node = newTargetName;
|
connection.node = newTargetName;
|
||||||
logger.debug(`Updated connection: ${sourceName}[${outputType}][${outputIndex}][${connIndex}].node: "${connection.node}" → "${newTargetName}"`);
|
logger.debug(`Updated connection: ${sourceName}[${outputType}][${outputIndex}][${connIndex}].node: "${oldTargetName}" → "${newTargetName}"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -818,12 +880,20 @@ class WorkflowDiffEngine {
|
|||||||
let current = obj;
|
let current = obj;
|
||||||
for (let i = 0; i < keys.length - 1; i++) {
|
for (let i = 0; i < keys.length - 1; i++) {
|
||||||
const key = keys[i];
|
const key = keys[i];
|
||||||
if (!(key in current) || typeof current[key] !== 'object') {
|
if (!(key in current) || typeof current[key] !== 'object' || current[key] === null) {
|
||||||
|
if (value === null)
|
||||||
|
return;
|
||||||
current[key] = {};
|
current[key] = {};
|
||||||
}
|
}
|
||||||
current = current[key];
|
current = current[key];
|
||||||
}
|
}
|
||||||
current[keys[keys.length - 1]] = value;
|
const finalKey = keys[keys.length - 1];
|
||||||
|
if (value === null) {
|
||||||
|
delete current[finalKey];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
current[finalKey] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.WorkflowDiffEngine = WorkflowDiffEngine;
|
exports.WorkflowDiffEngine = WorkflowDiffEngine;
|
||||||
|
|||||||
2
dist/services/workflow-diff-engine.js.map
vendored
2
dist/services/workflow-diff-engine.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/types/n8n-api.d.ts
vendored
2
dist/types/n8n-api.d.ts
vendored
@@ -259,6 +259,7 @@ export interface WebhookRequest {
|
|||||||
}
|
}
|
||||||
export interface McpToolResponse {
|
export interface McpToolResponse {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
saved?: boolean;
|
||||||
data?: unknown;
|
data?: unknown;
|
||||||
error?: string;
|
error?: string;
|
||||||
message?: string;
|
message?: string;
|
||||||
@@ -266,6 +267,7 @@ export interface McpToolResponse {
|
|||||||
details?: Record<string, unknown>;
|
details?: Record<string, unknown>;
|
||||||
executionId?: string;
|
executionId?: string;
|
||||||
workflowId?: string;
|
workflowId?: string;
|
||||||
|
operationsApplied?: number;
|
||||||
}
|
}
|
||||||
export type ExecutionMode = 'preview' | 'summary' | 'filtered' | 'full' | 'error';
|
export type ExecutionMode = 'preview' | 'summary' | 'filtered' | 'full' | 'error';
|
||||||
export interface ExecutionPreview {
|
export interface ExecutionPreview {
|
||||||
|
|||||||
2
dist/types/n8n-api.d.ts.map
vendored
2
dist/types/n8n-api.d.ts.map
vendored
File diff suppressed because one or more lines are too long
9
dist/types/workflow-diff.d.ts
vendored
9
dist/types/workflow-diff.d.ts
vendored
@@ -94,6 +94,10 @@ export interface ActivateWorkflowOperation extends DiffOperation {
|
|||||||
export interface DeactivateWorkflowOperation extends DiffOperation {
|
export interface DeactivateWorkflowOperation extends DiffOperation {
|
||||||
type: 'deactivateWorkflow';
|
type: 'deactivateWorkflow';
|
||||||
}
|
}
|
||||||
|
export interface TransferWorkflowOperation extends DiffOperation {
|
||||||
|
type: 'transferWorkflow';
|
||||||
|
destinationProjectId: string;
|
||||||
|
}
|
||||||
export interface CleanStaleConnectionsOperation extends DiffOperation {
|
export interface CleanStaleConnectionsOperation extends DiffOperation {
|
||||||
type: 'cleanStaleConnections';
|
type: 'cleanStaleConnections';
|
||||||
dryRun?: boolean;
|
dryRun?: boolean;
|
||||||
@@ -110,7 +114,7 @@ export interface ReplaceConnectionsOperation extends DiffOperation {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export type WorkflowDiffOperation = AddNodeOperation | RemoveNodeOperation | UpdateNodeOperation | MoveNodeOperation | EnableNodeOperation | DisableNodeOperation | AddConnectionOperation | RemoveConnectionOperation | RewireConnectionOperation | UpdateSettingsOperation | UpdateNameOperation | AddTagOperation | RemoveTagOperation | ActivateWorkflowOperation | DeactivateWorkflowOperation | CleanStaleConnectionsOperation | ReplaceConnectionsOperation;
|
export type WorkflowDiffOperation = AddNodeOperation | RemoveNodeOperation | UpdateNodeOperation | MoveNodeOperation | EnableNodeOperation | DisableNodeOperation | AddConnectionOperation | RemoveConnectionOperation | RewireConnectionOperation | UpdateSettingsOperation | UpdateNameOperation | AddTagOperation | RemoveTagOperation | ActivateWorkflowOperation | DeactivateWorkflowOperation | CleanStaleConnectionsOperation | ReplaceConnectionsOperation | TransferWorkflowOperation;
|
||||||
export interface WorkflowDiffRequest {
|
export interface WorkflowDiffRequest {
|
||||||
id: string;
|
id: string;
|
||||||
operations: WorkflowDiffOperation[];
|
operations: WorkflowDiffOperation[];
|
||||||
@@ -137,6 +141,9 @@ export interface WorkflowDiffResult {
|
|||||||
}>;
|
}>;
|
||||||
shouldActivate?: boolean;
|
shouldActivate?: boolean;
|
||||||
shouldDeactivate?: boolean;
|
shouldDeactivate?: boolean;
|
||||||
|
tagsToAdd?: string[];
|
||||||
|
tagsToRemove?: string[];
|
||||||
|
transferToProjectId?: string;
|
||||||
}
|
}
|
||||||
export interface NodeReference {
|
export interface NodeReference {
|
||||||
id?: string;
|
id?: string;
|
||||||
|
|||||||
2
dist/types/workflow-diff.d.ts.map
vendored
2
dist/types/workflow-diff.d.ts.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"workflow-diff.d.ts","sourceRoot":"","sources":["../../src/types/workflow-diff.ts"],"names":[],"mappings":"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"}
|
{"version":3,"file":"workflow-diff.d.ts","sourceRoot":"","sources":["../../src/types/workflow-diff.ts"],"names":[],"mappings":"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"}
|
||||||
2
dist/types/workflow-diff.js.map
vendored
2
dist/types/workflow-diff.js.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"workflow-diff.js","sourceRoot":"","sources":["../../src/types/workflow-diff.ts"],"names":[],"mappings":";;AAyMA,0CAIC;AAED,sDAGC;AAED,kDAGC;AAdD,SAAgB,eAAe,CAAC,EAAyB;IAGvD,OAAO,CAAC,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;AAC5G,CAAC;AAED,SAAgB,qBAAqB,CAAC,EAAyB;IAE7D,OAAO,CAAC,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,oBAAoB,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;AACpI,CAAC;AAED,SAAgB,mBAAmB,CAAC,EAAyB;IAE3D,OAAO,CAAC,gBAAgB,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;AACnF,CAAC"}
|
{"version":3,"file":"workflow-diff.js","sourceRoot":"","sources":["../../src/types/workflow-diff.ts"],"names":[],"mappings":";;AAkNA,0CAIC;AAED,sDAGC;AAED,kDAGC;AAdD,SAAgB,eAAe,CAAC,EAAyB;IAGvD,OAAO,CAAC,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;AAC5G,CAAC;AAED,SAAgB,qBAAqB,CAAC,EAAyB;IAE7D,OAAO,CAAC,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,oBAAoB,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;AACpI,CAAC;AAED,SAAgB,mBAAmB,CAAC,EAAyB;IAE3D,OAAO,CAAC,gBAAgB,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;AACnF,CAAC"}
|
||||||
10
dist/utils/node-type-utils.js
vendored
10
dist/utils/node-type-utils.js
vendored
@@ -87,12 +87,10 @@ function isTriggerNode(nodeType) {
|
|||||||
if (lowerType.includes('webhook') && !lowerType.includes('respond')) {
|
if (lowerType.includes('webhook') && !lowerType.includes('respond')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const specificTriggers = [
|
if (lowerType.includes('emailread') || lowerType.includes('emailreadimap')) {
|
||||||
'nodes-base.start',
|
return true;
|
||||||
'nodes-base.manualTrigger',
|
}
|
||||||
'nodes-base.formTrigger'
|
return normalized === 'nodes-base.start';
|
||||||
];
|
|
||||||
return specificTriggers.includes(normalized);
|
|
||||||
}
|
}
|
||||||
function isActivatableTrigger(nodeType) {
|
function isActivatableTrigger(nodeType) {
|
||||||
return isTriggerNode(nodeType);
|
return isTriggerNode(nodeType);
|
||||||
|
|||||||
2
dist/utils/node-type-utils.js.map
vendored
2
dist/utils/node-type-utils.js.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"node-type-utils.js","sourceRoot":"","sources":["../../src/utils/node-type-utils.ts"],"names":[],"mappings":"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"}
|
{"version":3,"file":"node-type-utils.js","sourceRoot":"","sources":["../../src/utils/node-type-utils.ts"],"names":[],"mappings":"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"}
|
||||||
5333
package-lock.json
generated
5333
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "n8n-mcp",
|
"name": "n8n-mcp",
|
||||||
"version": "2.37.1",
|
"version": "2.38.0",
|
||||||
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
|
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
@@ -153,16 +153,16 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.27.1",
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
||||||
"@n8n/n8n-nodes-langchain": "^2.11.2",
|
"@n8n/n8n-nodes-langchain": "^2.12.0",
|
||||||
"@supabase/supabase-js": "^2.57.4",
|
"@supabase/supabase-js": "^2.57.4",
|
||||||
"dotenv": "^16.5.0",
|
"dotenv": "^16.5.0",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"express-rate-limit": "^7.1.5",
|
"express-rate-limit": "^7.1.5",
|
||||||
"form-data": "^4.0.5",
|
"form-data": "^4.0.5",
|
||||||
"lru-cache": "^11.2.1",
|
"lru-cache": "^11.2.1",
|
||||||
"n8n": "^2.11.4",
|
"n8n": "^2.12.3",
|
||||||
"n8n-core": "^2.11.1",
|
"n8n-core": "^2.12.0",
|
||||||
"n8n-workflow": "^2.11.1",
|
"n8n-workflow": "^2.12.0",
|
||||||
"openai": "^4.77.0",
|
"openai": "^4.77.0",
|
||||||
"sql.js": "^1.13.0",
|
"sql.js": "^1.13.0",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.6.2",
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ function getValidator(repository: NodeRepository): WorkflowValidator {
|
|||||||
return cachedValidator;
|
return cachedValidator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Operation types that identify nodes by nodeId/nodeName
|
||||||
|
const NODE_TARGETING_OPERATIONS = new Set([
|
||||||
|
'updateNode', 'removeNode', 'moveNode', 'enableNode', 'disableNode'
|
||||||
|
]);
|
||||||
|
|
||||||
// Zod schema for the diff request
|
// Zod schema for the diff request
|
||||||
const workflowDiffSchema = z.object({
|
const workflowDiffSchema = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
@@ -63,6 +68,25 @@ const workflowDiffSchema = z.object({
|
|||||||
settings: z.any().optional(),
|
settings: z.any().optional(),
|
||||||
name: z.string().optional(),
|
name: z.string().optional(),
|
||||||
tag: z.string().optional(),
|
tag: z.string().optional(),
|
||||||
|
// Transfer operation
|
||||||
|
destinationProjectId: z.string().min(1).optional(),
|
||||||
|
// Aliases: LLMs often use "id" instead of "nodeId" — accept both
|
||||||
|
id: z.string().optional(),
|
||||||
|
}).transform((op) => {
|
||||||
|
// Normalize common field aliases for node-targeting operations:
|
||||||
|
// - "name" → "nodeName" (LLMs confuse the updateName "name" field with node identification)
|
||||||
|
// - "id" → "nodeId" (natural alias)
|
||||||
|
if (NODE_TARGETING_OPERATIONS.has(op.type)) {
|
||||||
|
if (!op.nodeName && !op.nodeId && op.name) {
|
||||||
|
op.nodeName = op.name;
|
||||||
|
op.name = undefined;
|
||||||
|
}
|
||||||
|
if (!op.nodeId && op.id) {
|
||||||
|
op.nodeId = op.id;
|
||||||
|
op.id = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return op;
|
||||||
})),
|
})),
|
||||||
validateOnly: z.boolean().optional(),
|
validateOnly: z.boolean().optional(),
|
||||||
continueOnError: z.boolean().optional(),
|
continueOnError: z.boolean().optional(),
|
||||||
@@ -348,6 +372,26 @@ export async function handleUpdatePartialWorkflow(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle project transfer if requested (before activation so workflow is in target project first)
|
||||||
|
let transferMessage = '';
|
||||||
|
if (diffResult.transferToProjectId) {
|
||||||
|
try {
|
||||||
|
await client.transferWorkflow(input.id, diffResult.transferToProjectId);
|
||||||
|
transferMessage = ` Workflow transferred to project ${diffResult.transferToProjectId}.`;
|
||||||
|
} catch (transferError) {
|
||||||
|
logger.error('Failed to transfer workflow to project', transferError);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
saved: true,
|
||||||
|
error: 'Workflow updated successfully but project transfer failed',
|
||||||
|
details: {
|
||||||
|
workflowUpdated: true,
|
||||||
|
transferError: transferError instanceof Error ? transferError.message : 'Unknown error'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle activation/deactivation if requested
|
// Handle activation/deactivation if requested
|
||||||
let finalWorkflow = updatedWorkflow;
|
let finalWorkflow = updatedWorkflow;
|
||||||
let activationMessage = '';
|
let activationMessage = '';
|
||||||
@@ -432,7 +476,7 @@ export async function handleUpdatePartialWorkflow(
|
|||||||
nodeCount: finalWorkflow.nodes?.length || 0,
|
nodeCount: finalWorkflow.nodes?.length || 0,
|
||||||
operationsApplied: diffResult.operationsApplied
|
operationsApplied: diffResult.operationsApplied
|
||||||
},
|
},
|
||||||
message: `Workflow "${finalWorkflow.name}" updated successfully. Applied ${diffResult.operationsApplied} operations.${activationMessage} Use n8n_get_workflow with mode 'structure' to verify current state.`,
|
message: `Workflow "${finalWorkflow.name}" updated successfully. Applied ${diffResult.operationsApplied} operations.${transferMessage}${activationMessage} Use n8n_get_workflow with mode 'structure' to verify current state.`,
|
||||||
details: {
|
details: {
|
||||||
applied: diffResult.applied,
|
applied: diffResult.applied,
|
||||||
failed: diffResult.failed,
|
failed: diffResult.failed,
|
||||||
@@ -537,6 +581,8 @@ function inferIntentFromOperations(operations: any[]): string {
|
|||||||
return 'Activate workflow';
|
return 'Activate workflow';
|
||||||
case 'deactivateWorkflow':
|
case 'deactivateWorkflow':
|
||||||
return 'Deactivate workflow';
|
return 'Deactivate workflow';
|
||||||
|
case 'transferWorkflow':
|
||||||
|
return `Transfer workflow to project ${op.destinationProjectId || ''}`.trim();
|
||||||
default:
|
default:
|
||||||
return `Workflow ${op.type}`;
|
return `Workflow ${op.type}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export const n8nUpdatePartialWorkflowDoc: ToolDocumentation = {
|
|||||||
name: 'n8n_update_partial_workflow',
|
name: 'n8n_update_partial_workflow',
|
||||||
category: 'workflow_management',
|
category: 'workflow_management',
|
||||||
essentials: {
|
essentials: {
|
||||||
description: 'Update workflow incrementally with diff operations. Types: addNode, removeNode, updateNode, moveNode, enable/disableNode, addConnection, removeConnection, rewireConnection, cleanStaleConnections, replaceConnections, updateSettings, updateName, add/removeTag, activateWorkflow, deactivateWorkflow. Supports smart parameters (branch, case) for multi-output nodes. Full support for AI connections (ai_languageModel, ai_tool, ai_memory, ai_embedding, ai_vectorStore, ai_document, ai_textSplitter, ai_outputParser).',
|
description: 'Update workflow incrementally with diff operations. Types: addNode, removeNode, updateNode, moveNode, enable/disableNode, addConnection, removeConnection, rewireConnection, cleanStaleConnections, replaceConnections, updateSettings, updateName, add/removeTag, activateWorkflow, deactivateWorkflow, transferWorkflow. Supports smart parameters (branch, case) for multi-output nodes. Full support for AI connections (ai_languageModel, ai_tool, ai_memory, ai_embedding, ai_vectorStore, ai_document, ai_textSplitter, ai_outputParser).',
|
||||||
keyParameters: ['id', 'operations', 'continueOnError'],
|
keyParameters: ['id', 'operations', 'continueOnError'],
|
||||||
example: 'n8n_update_partial_workflow({id: "wf_123", operations: [{type: "rewireConnection", source: "IF", from: "Old", to: "New", branch: "true"}]})',
|
example: 'n8n_update_partial_workflow({id: "wf_123", operations: [{type: "rewireConnection", source: "IF", from: "Old", to: "New", branch: "true"}]})',
|
||||||
performance: 'Fast (50-200ms)',
|
performance: 'Fast (50-200ms)',
|
||||||
@@ -22,7 +22,8 @@ export const n8nUpdatePartialWorkflowDoc: ToolDocumentation = {
|
|||||||
'Batch AI component connections for atomic updates',
|
'Batch AI component connections for atomic updates',
|
||||||
'Auto-sanitization: ALL nodes auto-fixed during updates (operator structures, missing metadata)',
|
'Auto-sanitization: ALL nodes auto-fixed during updates (operator structures, missing metadata)',
|
||||||
'Node renames automatically update all connection references - no manual connection operations needed',
|
'Node renames automatically update all connection references - no manual connection operations needed',
|
||||||
'Activate/deactivate workflows: Use activateWorkflow/deactivateWorkflow operations (requires activatable triggers like webhook/schedule)'
|
'Activate/deactivate workflows: Use activateWorkflow/deactivateWorkflow operations (requires activatable triggers like webhook/schedule)',
|
||||||
|
'Transfer workflows between projects: Use transferWorkflow with destinationProjectId (enterprise feature)'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
full: {
|
full: {
|
||||||
@@ -55,6 +56,9 @@ export const n8nUpdatePartialWorkflowDoc: ToolDocumentation = {
|
|||||||
- **activateWorkflow**: Activate the workflow to enable automatic execution via triggers
|
- **activateWorkflow**: Activate the workflow to enable automatic execution via triggers
|
||||||
- **deactivateWorkflow**: Deactivate the workflow to prevent automatic execution
|
- **deactivateWorkflow**: Deactivate the workflow to prevent automatic execution
|
||||||
|
|
||||||
|
### Project Management Operations (1 type):
|
||||||
|
- **transferWorkflow**: Transfer the workflow to a different project. Requires \`destinationProjectId\`. Enterprise/cloud feature.
|
||||||
|
|
||||||
## Smart Parameters for Multi-Output Nodes
|
## Smart Parameters for Multi-Output Nodes
|
||||||
|
|
||||||
For **IF nodes**, use semantic 'branch' parameter instead of technical sourceIndex:
|
For **IF nodes**, use semantic 'branch' parameter instead of technical sourceIndex:
|
||||||
@@ -194,12 +198,12 @@ Please choose a different name.
|
|||||||
- Can rename a node and add/remove connections using the new name in the same batch
|
- Can rename a node and add/remove connections using the new name in the same batch
|
||||||
- Use \`validateOnly: true\` to preview effects before applying
|
- Use \`validateOnly: true\` to preview effects before applying
|
||||||
|
|
||||||
## Removing Properties with undefined
|
## Removing Properties with null
|
||||||
|
|
||||||
To remove a property from a node, set its value to \`undefined\` in the updates object. This is essential when migrating from deprecated properties or cleaning up optional configuration fields.
|
To remove a property from a node, set its value to \`null\` in the updates object. This is essential when migrating from deprecated properties or cleaning up optional configuration fields.
|
||||||
|
|
||||||
### Why Use undefined?
|
### Why Use null?
|
||||||
- **Property removal vs. null**: Setting a property to \`undefined\` removes it completely from the node object, while \`null\` sets the property to a null value
|
- **Property removal**: Setting a property to \`null\` removes it completely from the node object
|
||||||
- **Validation constraints**: Some properties are mutually exclusive (e.g., \`continueOnFail\` and \`onError\`). Simply setting one without removing the other will fail validation
|
- **Validation constraints**: Some properties are mutually exclusive (e.g., \`continueOnFail\` and \`onError\`). Simply setting one without removing the other will fail validation
|
||||||
- **Deprecated property migration**: When n8n deprecates properties, you must remove the old property before the new one will work
|
- **Deprecated property migration**: When n8n deprecates properties, you must remove the old property before the new one will work
|
||||||
|
|
||||||
@@ -211,7 +215,7 @@ n8n_update_partial_workflow({
|
|||||||
operations: [{
|
operations: [{
|
||||||
type: "updateNode",
|
type: "updateNode",
|
||||||
nodeName: "HTTP Request",
|
nodeName: "HTTP Request",
|
||||||
updates: { onError: undefined }
|
updates: { onError: null }
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -221,7 +225,7 @@ n8n_update_partial_workflow({
|
|||||||
operations: [{
|
operations: [{
|
||||||
type: "updateNode",
|
type: "updateNode",
|
||||||
nodeId: "node_abc",
|
nodeId: "node_abc",
|
||||||
updates: { disabled: undefined }
|
updates: { disabled: null }
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
\`\`\`
|
\`\`\`
|
||||||
@@ -235,7 +239,7 @@ n8n_update_partial_workflow({
|
|||||||
operations: [{
|
operations: [{
|
||||||
type: "updateNode",
|
type: "updateNode",
|
||||||
nodeName: "API Request",
|
nodeName: "API Request",
|
||||||
updates: { "parameters.authentication": undefined }
|
updates: { "parameters.authentication": null }
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -245,7 +249,7 @@ n8n_update_partial_workflow({
|
|||||||
operations: [{
|
operations: [{
|
||||||
type: "updateNode",
|
type: "updateNode",
|
||||||
nodeName: "HTTP Request",
|
nodeName: "HTTP Request",
|
||||||
updates: { "parameters.headers": undefined }
|
updates: { "parameters.headers": null }
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
\`\`\`
|
\`\`\`
|
||||||
@@ -271,7 +275,7 @@ n8n_update_partial_workflow({
|
|||||||
type: "updateNode",
|
type: "updateNode",
|
||||||
nodeName: "HTTP Request",
|
nodeName: "HTTP Request",
|
||||||
updates: {
|
updates: {
|
||||||
continueOnFail: undefined,
|
continueOnFail: null,
|
||||||
onError: "continueErrorOutput"
|
onError: "continueErrorOutput"
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
@@ -287,15 +291,15 @@ n8n_update_partial_workflow({
|
|||||||
type: "updateNode",
|
type: "updateNode",
|
||||||
nodeName: "Data Processor",
|
nodeName: "Data Processor",
|
||||||
updates: {
|
updates: {
|
||||||
continueOnFail: undefined,
|
continueOnFail: null,
|
||||||
alwaysOutputData: undefined,
|
alwaysOutputData: null,
|
||||||
"parameters.legacy_option": undefined
|
"parameters.legacy_option": null
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
|
||||||
### When to Use undefined
|
### When to Use null
|
||||||
- Removing deprecated properties during migration
|
- Removing deprecated properties during migration
|
||||||
- Cleaning up optional configuration flags
|
- Cleaning up optional configuration flags
|
||||||
- Resolving mutual exclusivity validation errors
|
- Resolving mutual exclusivity validation errors
|
||||||
@@ -341,11 +345,14 @@ n8n_update_partial_workflow({
|
|||||||
'// Rewire AI Agent to use different language model\nn8n_update_partial_workflow({id: "ai9", operations: [{type: "rewireConnection", source: "AI Agent", from: "OpenAI Chat Model", to: "Anthropic Chat Model", sourceOutput: "ai_languageModel"}]})',
|
'// Rewire AI Agent to use different language model\nn8n_update_partial_workflow({id: "ai9", operations: [{type: "rewireConnection", source: "AI Agent", from: "OpenAI Chat Model", to: "Anthropic Chat Model", sourceOutput: "ai_languageModel"}]})',
|
||||||
'// Replace all AI tools for an agent\nn8n_update_partial_workflow({id: "ai10", operations: [\n {type: "removeConnection", source: "Old Tool 1", target: "AI Agent", sourceOutput: "ai_tool"},\n {type: "removeConnection", source: "Old Tool 2", target: "AI Agent", sourceOutput: "ai_tool"},\n {type: "addConnection", source: "New HTTP Tool", target: "AI Agent", sourceOutput: "ai_tool"},\n {type: "addConnection", source: "New Code Tool", target: "AI Agent", sourceOutput: "ai_tool"}\n]})',
|
'// Replace all AI tools for an agent\nn8n_update_partial_workflow({id: "ai10", operations: [\n {type: "removeConnection", source: "Old Tool 1", target: "AI Agent", sourceOutput: "ai_tool"},\n {type: "removeConnection", source: "Old Tool 2", target: "AI Agent", sourceOutput: "ai_tool"},\n {type: "addConnection", source: "New HTTP Tool", target: "AI Agent", sourceOutput: "ai_tool"},\n {type: "addConnection", source: "New Code Tool", target: "AI Agent", sourceOutput: "ai_tool"}\n]})',
|
||||||
'\n// ============ REMOVING PROPERTIES EXAMPLES ============',
|
'\n// ============ REMOVING PROPERTIES EXAMPLES ============',
|
||||||
'// Remove a simple property\nn8n_update_partial_workflow({id: "rm1", operations: [{type: "updateNode", nodeName: "HTTP Request", updates: {onError: undefined}}]})',
|
'// Remove a simple property\nn8n_update_partial_workflow({id: "rm1", operations: [{type: "updateNode", nodeName: "HTTP Request", updates: {onError: null}}]})',
|
||||||
'// Migrate from deprecated continueOnFail to onError\nn8n_update_partial_workflow({id: "rm2", operations: [{type: "updateNode", nodeName: "HTTP Request", updates: {continueOnFail: undefined, onError: "continueErrorOutput"}}]})',
|
'// Migrate from deprecated continueOnFail to onError\nn8n_update_partial_workflow({id: "rm2", operations: [{type: "updateNode", nodeName: "HTTP Request", updates: {continueOnFail: null, onError: "continueErrorOutput"}}]})',
|
||||||
'// Remove nested property\nn8n_update_partial_workflow({id: "rm3", operations: [{type: "updateNode", nodeName: "API Request", updates: {"parameters.authentication": undefined}}]})',
|
'// Remove nested property\nn8n_update_partial_workflow({id: "rm3", operations: [{type: "updateNode", nodeName: "API Request", updates: {"parameters.authentication": null}}]})',
|
||||||
'// Remove multiple properties\nn8n_update_partial_workflow({id: "rm4", operations: [{type: "updateNode", nodeName: "Data Processor", updates: {continueOnFail: undefined, alwaysOutputData: undefined, "parameters.legacy_option": undefined}}]})',
|
'// Remove multiple properties\nn8n_update_partial_workflow({id: "rm4", operations: [{type: "updateNode", nodeName: "Data Processor", updates: {continueOnFail: null, alwaysOutputData: null, "parameters.legacy_option": null}}]})',
|
||||||
'// Remove entire array property\nn8n_update_partial_workflow({id: "rm5", operations: [{type: "updateNode", nodeName: "HTTP Request", updates: {"parameters.headers": undefined}}]})'
|
'// Remove entire array property\nn8n_update_partial_workflow({id: "rm5", operations: [{type: "updateNode", nodeName: "HTTP Request", updates: {"parameters.headers": null}}]})',
|
||||||
|
'\n// ============ PROJECT TRANSFER EXAMPLES ============',
|
||||||
|
'// Transfer workflow to a different project\nn8n_update_partial_workflow({id: "tf1", operations: [{type: "transferWorkflow", destinationProjectId: "project-abc-123"}]})',
|
||||||
|
'// Transfer and activate in one call\nn8n_update_partial_workflow({id: "tf2", operations: [{type: "transferWorkflow", destinationProjectId: "project-abc-123"}, {type: "activateWorkflow"}]})'
|
||||||
],
|
],
|
||||||
useCases: [
|
useCases: [
|
||||||
'Rewire connections when replacing nodes',
|
'Rewire connections when replacing nodes',
|
||||||
@@ -363,7 +370,8 @@ n8n_update_partial_workflow({
|
|||||||
'Add fallback language models to AI Agents',
|
'Add fallback language models to AI Agents',
|
||||||
'Configure Vector Store retrieval systems',
|
'Configure Vector Store retrieval systems',
|
||||||
'Swap language models in existing AI workflows',
|
'Swap language models in existing AI workflows',
|
||||||
'Batch-update AI tool connections'
|
'Batch-update AI tool connections',
|
||||||
|
'Transfer workflows between team projects (enterprise)'
|
||||||
],
|
],
|
||||||
performance: 'Very fast - typically 50-200ms. Much faster than full updates as only changes are processed.',
|
performance: 'Very fast - typically 50-200ms. Much faster than full updates as only changes are processed.',
|
||||||
bestPractices: [
|
bestPractices: [
|
||||||
@@ -383,9 +391,9 @@ n8n_update_partial_workflow({
|
|||||||
'Use targetIndex for fallback models (primary=0, fallback=1)',
|
'Use targetIndex for fallback models (primary=0, fallback=1)',
|
||||||
'Batch AI component connections in a single operation for atomicity',
|
'Batch AI component connections in a single operation for atomicity',
|
||||||
'Validate AI workflows after connection changes to catch configuration errors',
|
'Validate AI workflows after connection changes to catch configuration errors',
|
||||||
'To remove properties, set them to undefined (not null) in the updates object',
|
'To remove properties, set them to null in the updates object',
|
||||||
'When migrating from deprecated properties, remove the old property and add the new one in the same operation',
|
'When migrating from deprecated properties, remove the old property and add the new one in the same operation',
|
||||||
'Use undefined to resolve mutual exclusivity validation errors between properties',
|
'Use null to resolve mutual exclusivity validation errors between properties',
|
||||||
'Batch multiple property removals in a single updateNode operation for efficiency'
|
'Batch multiple property removals in a single updateNode operation for efficiency'
|
||||||
],
|
],
|
||||||
pitfalls: [
|
pitfalls: [
|
||||||
@@ -407,8 +415,8 @@ n8n_update_partial_workflow({
|
|||||||
'**Auto-sanitization runs on ALL nodes**: When ANY update is made, ALL nodes in the workflow are sanitized (not just modified ones)',
|
'**Auto-sanitization runs on ALL nodes**: When ANY update is made, ALL nodes in the workflow are sanitized (not just modified ones)',
|
||||||
'**Auto-sanitization cannot fix everything**: It fixes operator structures and missing metadata, but cannot fix broken connections or branch mismatches',
|
'**Auto-sanitization cannot fix everything**: It fixes operator structures and missing metadata, but cannot fix broken connections or branch mismatches',
|
||||||
'**Corrupted workflows beyond repair**: Workflows in paradoxical states (API returns corrupt, API rejects updates) cannot be fixed via API - must be recreated',
|
'**Corrupted workflows beyond repair**: Workflows in paradoxical states (API returns corrupt, API rejects updates) cannot be fixed via API - must be recreated',
|
||||||
'Setting a property to null does NOT remove it - use undefined instead',
|
'To remove a property, set it to null in the updates object',
|
||||||
'When properties are mutually exclusive (e.g., continueOnFail and onError), setting only the new property will fail - you must remove the old one with undefined',
|
'When properties are mutually exclusive (e.g., continueOnFail and onError), setting only the new property will fail - you must remove the old one with null',
|
||||||
'Removing a required property may cause validation errors - check node documentation first',
|
'Removing a required property may cause validation errors - check node documentation first',
|
||||||
'Nested property removal with dot notation only removes the specific nested field, not the entire parent object',
|
'Nested property removal with dot notation only removes the specific nested field, not the entire parent object',
|
||||||
'Array index notation (e.g., "parameters.headers[0]") is not supported - remove the entire array property instead'
|
'Array index notation (e.g., "parameters.headers[0]") is not supported - remove the entire array property instead'
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ export const n8nManagementTools: ToolDefinition[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'n8n_update_partial_workflow',
|
name: 'n8n_update_partial_workflow',
|
||||||
description: `Update workflow incrementally with diff operations. Types: addNode, removeNode, updateNode, moveNode, enable/disableNode, addConnection, removeConnection, updateSettings, updateName, add/removeTag. See tools_documentation("n8n_update_partial_workflow", "full") for details.`,
|
description: `Update workflow incrementally with diff operations. Types: addNode, removeNode, updateNode, moveNode, enable/disableNode, addConnection, removeConnection, updateSettings, updateName, add/removeTag, activate/deactivateWorkflow, transferWorkflow. See tools_documentation("n8n_update_partial_workflow", "full") for details.`,
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
additionalProperties: true, // Allow any extra properties Claude Desktop might add
|
additionalProperties: true, // Allow any extra properties Claude Desktop might add
|
||||||
|
|||||||
@@ -252,6 +252,14 @@ export class N8nApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async transferWorkflow(id: string, destinationProjectId: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
await this.client.put(`/workflows/${id}/transfer`, { destinationProjectId });
|
||||||
|
} catch (error) {
|
||||||
|
throw handleN8nApiError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async activateWorkflow(id: string): Promise<Workflow> {
|
async activateWorkflow(id: string): Promise<Workflow> {
|
||||||
try {
|
try {
|
||||||
const response = await this.client.post(`/workflows/${id}/activate`, {});
|
const response = await this.client.post(`/workflows/${id}/activate`, {});
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export const workflowConnectionSchema = z.record(
|
|||||||
ai_memory: connectionArraySchema.optional(),
|
ai_memory: connectionArraySchema.optional(),
|
||||||
ai_embedding: connectionArraySchema.optional(),
|
ai_embedding: connectionArraySchema.optional(),
|
||||||
ai_vectorStore: connectionArraySchema.optional(),
|
ai_vectorStore: connectionArraySchema.optional(),
|
||||||
})
|
}).catchall(connectionArraySchema) // Allow additional AI connection types (ai_outputParser, ai_document, ai_textSplitter, etc.)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const workflowSettingsSchema = z.object({
|
export const workflowSettingsSchema = z.object({
|
||||||
@@ -248,15 +248,15 @@ export function validateWorkflowStructure(workflow: Partial<Workflow>): string[]
|
|||||||
const connectedNodes = new Set<string>();
|
const connectedNodes = new Set<string>();
|
||||||
|
|
||||||
// Collect all nodes that appear in connections (as source or target)
|
// Collect all nodes that appear in connections (as source or target)
|
||||||
// Check ALL connection types, not just 'main' - AI workflows use ai_tool, ai_languageModel, etc.
|
// Iterate over ALL connection types present in the data — not a hardcoded list —
|
||||||
const ALL_CONNECTION_TYPES = ['main', 'error', 'ai_tool', 'ai_languageModel', 'ai_memory', 'ai_embedding', 'ai_vectorStore'] as const;
|
// so that every AI connection type (ai_outputParser, ai_document, ai_textSplitter,
|
||||||
|
// ai_agent, ai_chain, ai_retriever, etc.) is covered automatically.
|
||||||
Object.entries(workflow.connections).forEach(([sourceName, connection]) => {
|
Object.entries(workflow.connections).forEach(([sourceName, connection]) => {
|
||||||
connectedNodes.add(sourceName); // Node has outgoing connection
|
connectedNodes.add(sourceName); // Node has outgoing connection
|
||||||
|
|
||||||
// Check all connection types for target nodes
|
// Check every connection type key present on this source node
|
||||||
ALL_CONNECTION_TYPES.forEach(connType => {
|
const connectionRecord = connection as Record<string, unknown>;
|
||||||
const connData = (connection as Record<string, unknown>)[connType];
|
Object.values(connectionRecord).forEach((connData) => {
|
||||||
if (connData && Array.isArray(connData)) {
|
if (connData && Array.isArray(connData)) {
|
||||||
connData.forEach((outputs) => {
|
connData.forEach((outputs) => {
|
||||||
if (Array.isArray(outputs)) {
|
if (Array.isArray(outputs)) {
|
||||||
@@ -429,24 +429,29 @@ export function validateWorkflowStructure(workflow: Partial<Workflow>): string[]
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connection.main && Array.isArray(connection.main)) {
|
// Check all connection types (main, error, ai_tool, ai_languageModel, etc.)
|
||||||
connection.main.forEach((outputs, outputIndex) => {
|
const connectionRecord = connection as Record<string, unknown>;
|
||||||
if (Array.isArray(outputs)) {
|
Object.values(connectionRecord).forEach((connData) => {
|
||||||
outputs.forEach((target, targetIndex) => {
|
if (connData && Array.isArray(connData)) {
|
||||||
// Check if target exists by name (correct)
|
connData.forEach((outputs: any, outputIndex: number) => {
|
||||||
if (!nodeNames.has(target.node)) {
|
if (Array.isArray(outputs)) {
|
||||||
// Check if they're using an ID instead of name
|
outputs.forEach((target: any, targetIndex: number) => {
|
||||||
if (nodeIds.has(target.node)) {
|
if (!target?.node) return;
|
||||||
const correctName = nodeIdToName.get(target.node);
|
// Check if target exists by name (correct)
|
||||||
errors.push(`Connection target uses node ID '${target.node}' but must use node name '${correctName}' (from ${sourceName}[${outputIndex}][${targetIndex}])`);
|
if (!nodeNames.has(target.node)) {
|
||||||
} else {
|
// Check if they're using an ID instead of name
|
||||||
errors.push(`Connection references non-existent target node: ${target.node} (from ${sourceName}[${outputIndex}][${targetIndex}])`);
|
if (nodeIds.has(target.node)) {
|
||||||
|
const correctName = nodeIdToName.get(target.node);
|
||||||
|
errors.push(`Connection target uses node ID '${target.node}' but must use node name '${correctName}' (from ${sourceName}[${outputIndex}][${targetIndex}])`);
|
||||||
|
} else {
|
||||||
|
errors.push(`Connection references non-existent target node: ${target.node} (from ${sourceName}[${outputIndex}][${targetIndex}])`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1374,8 +1374,11 @@ export class NodeSpecificValidators {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for primitive return
|
// Skip primitive return check when helper functions are present,
|
||||||
if (/return\s+(true|false|null|undefined|\d+|['"`])/m.test(code)) {
|
// since we can't distinguish top-level vs nested returns without AST.
|
||||||
|
// Matches: function name(), const/let/var name = [async] function/arrow
|
||||||
|
const hasHelperFunctions = /(?:function\s+\w+\s*\(|(?:const|let|var)\s+\w+\s*=\s*(?:async\s+)?(?:function|\([^)]*\)\s*=>|\w+\s*=>))/.test(code);
|
||||||
|
if (!hasHelperFunctions && /return\s+(true|false|null|undefined|\d+|['"`])/m.test(code)) {
|
||||||
errors.push({
|
errors.push({
|
||||||
type: 'invalid_value',
|
type: 'invalid_value',
|
||||||
property: 'jsCode',
|
property: 'jsCode',
|
||||||
@@ -1487,7 +1490,7 @@ export class NodeSpecificValidators {
|
|||||||
// Check for common variable mistakes
|
// Check for common variable mistakes
|
||||||
if (language === 'javaScript') {
|
if (language === 'javaScript') {
|
||||||
// Using $ without proper variable
|
// Using $ without proper variable
|
||||||
if (/\$(?![a-zA-Z])/.test(code) && !code.includes('${')) {
|
if (/\$(?![a-zA-Z_(])/.test(code) && !code.includes('${')) {
|
||||||
warnings.push({
|
warnings.push({
|
||||||
type: 'best_practice',
|
type: 'best_practice',
|
||||||
message: 'Invalid $ usage detected',
|
message: 'Invalid $ usage detected',
|
||||||
|
|||||||
@@ -671,11 +671,13 @@ export class WorkflowAutoFixer {
|
|||||||
filteredFixes: FixOperation[],
|
filteredFixes: FixOperation[],
|
||||||
allFixes: FixOperation[]
|
allFixes: FixOperation[]
|
||||||
): WorkflowDiffOperation[] {
|
): WorkflowDiffOperation[] {
|
||||||
|
// fixedNodes contains node names (FixOperation.node = node.name)
|
||||||
const fixedNodes = new Set(filteredFixes.map(f => f.node));
|
const fixedNodes = new Set(filteredFixes.map(f => f.node));
|
||||||
const hasConnectionFixes = filteredFixes.some(f => CONNECTION_FIX_TYPES.includes(f.type));
|
const hasConnectionFixes = filteredFixes.some(f => CONNECTION_FIX_TYPES.includes(f.type));
|
||||||
return operations.filter(op => {
|
return operations.filter(op => {
|
||||||
if (op.type === 'updateNode') {
|
if (op.type === 'updateNode') {
|
||||||
return fixedNodes.has(op.nodeId || '');
|
// Check both nodeName and nodeId — operations may use either
|
||||||
|
return fixedNodes.has(op.nodeName || '') || fixedNodes.has(op.nodeId || '');
|
||||||
}
|
}
|
||||||
if (op.type === 'replaceConnections') {
|
if (op.type === 'replaceConnections') {
|
||||||
return hasConnectionFixes;
|
return hasConnectionFixes;
|
||||||
@@ -1186,10 +1188,11 @@ export class WorkflowAutoFixer {
|
|||||||
description: `Upgrade ${node.name} from v${currentVersion} to v${latestVersion}. ${analysis.reason}`
|
description: `Upgrade ${node.name} from v${currentVersion} to v${latestVersion}. ${analysis.reason}`
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create update operation
|
// Create update operation — both nodeId and nodeName needed for fix filtering
|
||||||
const operation: UpdateNodeOperation = {
|
const operation: UpdateNodeOperation = {
|
||||||
type: 'updateNode',
|
type: 'updateNode',
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
|
nodeName: node.name,
|
||||||
updates: {
|
updates: {
|
||||||
typeVersion: parseFloat(latestVersion),
|
typeVersion: parseFloat(latestVersion),
|
||||||
parameters: migrationResult.updatedNode.parameters,
|
parameters: migrationResult.updatedNode.parameters,
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ import {
|
|||||||
ActivateWorkflowOperation,
|
ActivateWorkflowOperation,
|
||||||
DeactivateWorkflowOperation,
|
DeactivateWorkflowOperation,
|
||||||
CleanStaleConnectionsOperation,
|
CleanStaleConnectionsOperation,
|
||||||
ReplaceConnectionsOperation
|
ReplaceConnectionsOperation,
|
||||||
|
TransferWorkflowOperation
|
||||||
} from '../types/workflow-diff';
|
} from '../types/workflow-diff';
|
||||||
import { Workflow, WorkflowNode, WorkflowConnection } from '../types/n8n-api';
|
import { Workflow, WorkflowNode, WorkflowConnection } from '../types/n8n-api';
|
||||||
import { Logger } from '../utils/logger';
|
import { Logger } from '../utils/logger';
|
||||||
@@ -54,6 +55,8 @@ export class WorkflowDiffEngine {
|
|||||||
// Track tag operations for dedicated API calls
|
// Track tag operations for dedicated API calls
|
||||||
private tagsToAdd: string[] = [];
|
private tagsToAdd: string[] = [];
|
||||||
private tagsToRemove: string[] = [];
|
private tagsToRemove: string[] = [];
|
||||||
|
// Track transfer operation for dedicated API call
|
||||||
|
private transferToProjectId: string | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply diff operations to a workflow
|
* Apply diff operations to a workflow
|
||||||
@@ -70,6 +73,7 @@ export class WorkflowDiffEngine {
|
|||||||
this.removedNodeNames.clear();
|
this.removedNodeNames.clear();
|
||||||
this.tagsToAdd = [];
|
this.tagsToAdd = [];
|
||||||
this.tagsToRemove = [];
|
this.tagsToRemove = [];
|
||||||
|
this.transferToProjectId = undefined;
|
||||||
|
|
||||||
// Clone workflow to avoid modifying original
|
// Clone workflow to avoid modifying original
|
||||||
const workflowCopy = JSON.parse(JSON.stringify(workflow));
|
const workflowCopy = JSON.parse(JSON.stringify(workflow));
|
||||||
@@ -141,6 +145,12 @@ export class WorkflowDiffEngine {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract and clean up activation flags (same as atomic mode)
|
||||||
|
const shouldActivate = (workflowCopy as any)._shouldActivate === true;
|
||||||
|
const shouldDeactivate = (workflowCopy as any)._shouldDeactivate === true;
|
||||||
|
delete (workflowCopy as any)._shouldActivate;
|
||||||
|
delete (workflowCopy as any)._shouldDeactivate;
|
||||||
|
|
||||||
const success = appliedIndices.length > 0;
|
const success = appliedIndices.length > 0;
|
||||||
return {
|
return {
|
||||||
success,
|
success,
|
||||||
@@ -151,8 +161,11 @@ export class WorkflowDiffEngine {
|
|||||||
warnings: this.warnings.length > 0 ? this.warnings : undefined,
|
warnings: this.warnings.length > 0 ? this.warnings : undefined,
|
||||||
applied: appliedIndices,
|
applied: appliedIndices,
|
||||||
failed: failedIndices,
|
failed: failedIndices,
|
||||||
|
shouldActivate: shouldActivate || undefined,
|
||||||
|
shouldDeactivate: shouldDeactivate || undefined,
|
||||||
tagsToAdd: this.tagsToAdd.length > 0 ? this.tagsToAdd : undefined,
|
tagsToAdd: this.tagsToAdd.length > 0 ? this.tagsToAdd : undefined,
|
||||||
tagsToRemove: this.tagsToRemove.length > 0 ? this.tagsToRemove : undefined
|
tagsToRemove: this.tagsToRemove.length > 0 ? this.tagsToRemove : undefined,
|
||||||
|
transferToProjectId: this.transferToProjectId || undefined
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Atomic mode: all operations must succeed
|
// Atomic mode: all operations must succeed
|
||||||
@@ -256,7 +269,8 @@ export class WorkflowDiffEngine {
|
|||||||
shouldActivate: shouldActivate || undefined,
|
shouldActivate: shouldActivate || undefined,
|
||||||
shouldDeactivate: shouldDeactivate || undefined,
|
shouldDeactivate: shouldDeactivate || undefined,
|
||||||
tagsToAdd: this.tagsToAdd.length > 0 ? this.tagsToAdd : undefined,
|
tagsToAdd: this.tagsToAdd.length > 0 ? this.tagsToAdd : undefined,
|
||||||
tagsToRemove: this.tagsToRemove.length > 0 ? this.tagsToRemove : undefined
|
tagsToRemove: this.tagsToRemove.length > 0 ? this.tagsToRemove : undefined,
|
||||||
|
transferToProjectId: this.transferToProjectId || undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -298,6 +312,8 @@ export class WorkflowDiffEngine {
|
|||||||
case 'addTag':
|
case 'addTag':
|
||||||
case 'removeTag':
|
case 'removeTag':
|
||||||
return null; // These are always valid
|
return null; // These are always valid
|
||||||
|
case 'transferWorkflow':
|
||||||
|
return this.validateTransferWorkflow(workflow, operation as TransferWorkflowOperation);
|
||||||
case 'activateWorkflow':
|
case 'activateWorkflow':
|
||||||
return this.validateActivateWorkflow(workflow, operation);
|
return this.validateActivateWorkflow(workflow, operation);
|
||||||
case 'deactivateWorkflow':
|
case 'deactivateWorkflow':
|
||||||
@@ -367,6 +383,9 @@ export class WorkflowDiffEngine {
|
|||||||
case 'replaceConnections':
|
case 'replaceConnections':
|
||||||
this.applyReplaceConnections(workflow, operation);
|
this.applyReplaceConnections(workflow, operation);
|
||||||
break;
|
break;
|
||||||
|
case 'transferWorkflow':
|
||||||
|
this.applyTransferWorkflow(workflow, operation as TransferWorkflowOperation);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -975,6 +994,18 @@ export class WorkflowDiffEngine {
|
|||||||
(workflow as any)._shouldDeactivate = true;
|
(workflow as any)._shouldDeactivate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transfer operation — uses dedicated API call (PUT /workflows/{id}/transfer)
|
||||||
|
private validateTransferWorkflow(_workflow: Workflow, operation: TransferWorkflowOperation): string | null {
|
||||||
|
if (!operation.destinationProjectId) {
|
||||||
|
return 'transferWorkflow requires a non-empty destinationProjectId string';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyTransferWorkflow(_workflow: Workflow, operation: TransferWorkflowOperation): void {
|
||||||
|
this.transferToProjectId = operation.destinationProjectId;
|
||||||
|
}
|
||||||
|
|
||||||
// Connection cleanup operation validators
|
// Connection cleanup operation validators
|
||||||
private validateCleanStaleConnections(workflow: Workflow, operation: CleanStaleConnectionsOperation): string | null {
|
private validateCleanStaleConnections(workflow: Workflow, operation: CleanStaleConnectionsOperation): string | null {
|
||||||
// This operation is always valid - it just cleans up what it finds
|
// This operation is always valid - it just cleans up what it finds
|
||||||
@@ -1128,9 +1159,10 @@ export class WorkflowDiffEngine {
|
|||||||
const connection = connectionsAtIndex[connIndex];
|
const connection = connectionsAtIndex[connIndex];
|
||||||
// Check if target node was renamed
|
// Check if target node was renamed
|
||||||
if (renames.has(connection.node)) {
|
if (renames.has(connection.node)) {
|
||||||
|
const oldTargetName = connection.node;
|
||||||
const newTargetName = renames.get(connection.node)!;
|
const newTargetName = renames.get(connection.node)!;
|
||||||
connection.node = newTargetName;
|
connection.node = newTargetName;
|
||||||
logger.debug(`Updated connection: ${sourceName}[${outputType}][${outputIndex}][${connIndex}].node: "${connection.node}" → "${newTargetName}"`);
|
logger.debug(`Updated connection: ${sourceName}[${outputType}][${outputIndex}][${connIndex}].node: "${oldTargetName}" → "${newTargetName}"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1240,12 +1272,18 @@ export class WorkflowDiffEngine {
|
|||||||
|
|
||||||
for (let i = 0; i < keys.length - 1; i++) {
|
for (let i = 0; i < keys.length - 1; i++) {
|
||||||
const key = keys[i];
|
const key = keys[i];
|
||||||
if (!(key in current) || typeof current[key] !== 'object') {
|
if (!(key in current) || typeof current[key] !== 'object' || current[key] === null) {
|
||||||
|
if (value === null) return; // parent path doesn't exist, nothing to delete
|
||||||
current[key] = {};
|
current[key] = {};
|
||||||
}
|
}
|
||||||
current = current[key];
|
current = current[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
current[keys[keys.length - 1]] = value;
|
const finalKey = keys[keys.length - 1];
|
||||||
|
if (value === null) {
|
||||||
|
delete current[finalKey];
|
||||||
|
} else {
|
||||||
|
current[finalKey] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,6 +124,11 @@ export interface DeactivateWorkflowOperation extends DiffOperation {
|
|||||||
// No additional properties needed - just deactivates the workflow
|
// No additional properties needed - just deactivates the workflow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TransferWorkflowOperation extends DiffOperation {
|
||||||
|
type: 'transferWorkflow';
|
||||||
|
destinationProjectId: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Connection Cleanup Operations
|
// Connection Cleanup Operations
|
||||||
export interface CleanStaleConnectionsOperation extends DiffOperation {
|
export interface CleanStaleConnectionsOperation extends DiffOperation {
|
||||||
type: 'cleanStaleConnections';
|
type: 'cleanStaleConnections';
|
||||||
@@ -161,7 +166,8 @@ export type WorkflowDiffOperation =
|
|||||||
| ActivateWorkflowOperation
|
| ActivateWorkflowOperation
|
||||||
| DeactivateWorkflowOperation
|
| DeactivateWorkflowOperation
|
||||||
| CleanStaleConnectionsOperation
|
| CleanStaleConnectionsOperation
|
||||||
| ReplaceConnectionsOperation;
|
| ReplaceConnectionsOperation
|
||||||
|
| TransferWorkflowOperation;
|
||||||
|
|
||||||
// Main diff request structure
|
// Main diff request structure
|
||||||
export interface WorkflowDiffRequest {
|
export interface WorkflowDiffRequest {
|
||||||
@@ -192,6 +198,7 @@ export interface WorkflowDiffResult {
|
|||||||
shouldDeactivate?: boolean; // Flag to deactivate workflow after update (for deactivateWorkflow operation)
|
shouldDeactivate?: boolean; // Flag to deactivate workflow after update (for deactivateWorkflow operation)
|
||||||
tagsToAdd?: string[];
|
tagsToAdd?: string[];
|
||||||
tagsToRemove?: string[];
|
tagsToRemove?: string[];
|
||||||
|
transferToProjectId?: string; // For transferWorkflow operation - uses dedicated API call
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper type for node reference (supports both ID and name)
|
// Helper type for node reference (supports both ID and name)
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ describe('handlers-workflow-diff', () => {
|
|||||||
listTags: vi.fn().mockResolvedValue({ data: [] }),
|
listTags: vi.fn().mockResolvedValue({ data: [] }),
|
||||||
createTag: vi.fn(),
|
createTag: vi.fn(),
|
||||||
updateWorkflowTags: vi.fn().mockResolvedValue([]),
|
updateWorkflowTags: vi.fn().mockResolvedValue([]),
|
||||||
|
transferWorkflow: vi.fn().mockResolvedValue(undefined),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Setup mock diff engine
|
// Setup mock diff engine
|
||||||
@@ -1001,5 +1002,302 @@ describe('handlers-workflow-diff', () => {
|
|||||||
expect(mockApiClient.updateWorkflowTags).not.toHaveBeenCalled();
|
expect(mockApiClient.updateWorkflowTags).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Project Transfer via Dedicated API', () => {
|
||||||
|
it('should call transferWorkflow when diffResult has transferToProjectId', async () => {
|
||||||
|
const testWorkflow = createTestWorkflow();
|
||||||
|
const updatedWorkflow = { ...testWorkflow };
|
||||||
|
|
||||||
|
mockApiClient.getWorkflow.mockResolvedValue(testWorkflow);
|
||||||
|
mockDiffEngine.applyDiff.mockResolvedValue({
|
||||||
|
success: true,
|
||||||
|
workflow: updatedWorkflow,
|
||||||
|
operationsApplied: 1,
|
||||||
|
message: 'Success',
|
||||||
|
errors: [],
|
||||||
|
transferToProjectId: 'project-abc-123',
|
||||||
|
});
|
||||||
|
mockApiClient.updateWorkflow.mockResolvedValue(updatedWorkflow);
|
||||||
|
|
||||||
|
const result = await handleUpdatePartialWorkflow({
|
||||||
|
id: 'test-workflow-id',
|
||||||
|
operations: [{ type: 'transferWorkflow', destinationProjectId: 'project-abc-123' }],
|
||||||
|
}, mockRepository);
|
||||||
|
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(mockApiClient.transferWorkflow).toHaveBeenCalledWith('test-workflow-id', 'project-abc-123');
|
||||||
|
expect(result.message).toContain('transferred to project');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT call transferWorkflow when transferToProjectId is absent', async () => {
|
||||||
|
const testWorkflow = createTestWorkflow();
|
||||||
|
const updatedWorkflow = { ...testWorkflow };
|
||||||
|
|
||||||
|
mockApiClient.getWorkflow.mockResolvedValue(testWorkflow);
|
||||||
|
mockDiffEngine.applyDiff.mockResolvedValue({
|
||||||
|
success: true,
|
||||||
|
workflow: updatedWorkflow,
|
||||||
|
operationsApplied: 1,
|
||||||
|
message: 'Success',
|
||||||
|
errors: [],
|
||||||
|
});
|
||||||
|
mockApiClient.updateWorkflow.mockResolvedValue(updatedWorkflow);
|
||||||
|
|
||||||
|
await handleUpdatePartialWorkflow({
|
||||||
|
id: 'test-workflow-id',
|
||||||
|
operations: [{ type: 'updateName', name: 'New Name' }],
|
||||||
|
}, mockRepository);
|
||||||
|
|
||||||
|
expect(mockApiClient.transferWorkflow).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return success false with saved true when transfer fails', async () => {
|
||||||
|
const testWorkflow = createTestWorkflow();
|
||||||
|
const updatedWorkflow = { ...testWorkflow };
|
||||||
|
|
||||||
|
mockApiClient.getWorkflow.mockResolvedValue(testWorkflow);
|
||||||
|
mockDiffEngine.applyDiff.mockResolvedValue({
|
||||||
|
success: true,
|
||||||
|
workflow: updatedWorkflow,
|
||||||
|
operationsApplied: 1,
|
||||||
|
message: 'Success',
|
||||||
|
errors: [],
|
||||||
|
transferToProjectId: 'project-bad-id',
|
||||||
|
});
|
||||||
|
mockApiClient.updateWorkflow.mockResolvedValue(updatedWorkflow);
|
||||||
|
mockApiClient.transferWorkflow.mockRejectedValue(new Error('Project not found'));
|
||||||
|
|
||||||
|
const result = await handleUpdatePartialWorkflow({
|
||||||
|
id: 'test-workflow-id',
|
||||||
|
operations: [{ type: 'transferWorkflow', destinationProjectId: 'project-bad-id' }],
|
||||||
|
}, mockRepository);
|
||||||
|
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
expect(result.saved).toBe(true);
|
||||||
|
expect(result.error).toBe('Workflow updated successfully but project transfer failed');
|
||||||
|
expect(result.details).toEqual({
|
||||||
|
workflowUpdated: true,
|
||||||
|
transferError: 'Project not found',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return Unknown error when non-Error value is thrown during transfer', async () => {
|
||||||
|
const testWorkflow = createTestWorkflow();
|
||||||
|
const updatedWorkflow = { ...testWorkflow };
|
||||||
|
|
||||||
|
mockApiClient.getWorkflow.mockResolvedValue(testWorkflow);
|
||||||
|
mockDiffEngine.applyDiff.mockResolvedValue({
|
||||||
|
success: true,
|
||||||
|
workflow: updatedWorkflow,
|
||||||
|
operationsApplied: 1,
|
||||||
|
message: 'Success',
|
||||||
|
errors: [],
|
||||||
|
transferToProjectId: 'project-unknown',
|
||||||
|
});
|
||||||
|
mockApiClient.updateWorkflow.mockResolvedValue(updatedWorkflow);
|
||||||
|
mockApiClient.transferWorkflow.mockRejectedValue('string error');
|
||||||
|
|
||||||
|
const result = await handleUpdatePartialWorkflow({
|
||||||
|
id: 'test-workflow-id',
|
||||||
|
operations: [{ type: 'transferWorkflow', destinationProjectId: 'project-unknown' }],
|
||||||
|
}, mockRepository);
|
||||||
|
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
expect(result.saved).toBe(true);
|
||||||
|
expect(result.details).toEqual({
|
||||||
|
workflowUpdated: true,
|
||||||
|
transferError: 'Unknown error',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call transferWorkflow BEFORE activateWorkflow', async () => {
|
||||||
|
const testWorkflow = createTestWorkflow({ active: false });
|
||||||
|
const updatedWorkflow = { ...testWorkflow, active: false };
|
||||||
|
const activatedWorkflow = { ...testWorkflow, active: true };
|
||||||
|
|
||||||
|
const callOrder: string[] = [];
|
||||||
|
|
||||||
|
mockApiClient.getWorkflow.mockResolvedValue(testWorkflow);
|
||||||
|
mockDiffEngine.applyDiff.mockResolvedValue({
|
||||||
|
success: true,
|
||||||
|
workflow: updatedWorkflow,
|
||||||
|
operationsApplied: 2,
|
||||||
|
message: 'Success',
|
||||||
|
errors: [],
|
||||||
|
transferToProjectId: 'project-target',
|
||||||
|
shouldActivate: true,
|
||||||
|
});
|
||||||
|
mockApiClient.updateWorkflow.mockResolvedValue(updatedWorkflow);
|
||||||
|
mockApiClient.transferWorkflow.mockImplementation(async () => {
|
||||||
|
callOrder.push('transfer');
|
||||||
|
});
|
||||||
|
mockApiClient.activateWorkflow = vi.fn().mockImplementation(async () => {
|
||||||
|
callOrder.push('activate');
|
||||||
|
return activatedWorkflow;
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await handleUpdatePartialWorkflow({
|
||||||
|
id: 'test-workflow-id',
|
||||||
|
operations: [
|
||||||
|
{ type: 'transferWorkflow', destinationProjectId: 'project-target' },
|
||||||
|
{ type: 'activateWorkflow' },
|
||||||
|
],
|
||||||
|
}, mockRepository);
|
||||||
|
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(mockApiClient.transferWorkflow).toHaveBeenCalledWith('test-workflow-id', 'project-target');
|
||||||
|
expect(mockApiClient.activateWorkflow).toHaveBeenCalledWith('test-workflow-id');
|
||||||
|
expect(callOrder).toEqual(['transfer', 'activate']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip activation when transfer fails', async () => {
|
||||||
|
const testWorkflow = createTestWorkflow({ active: false });
|
||||||
|
const updatedWorkflow = { ...testWorkflow, active: false };
|
||||||
|
|
||||||
|
mockApiClient.getWorkflow.mockResolvedValue(testWorkflow);
|
||||||
|
mockDiffEngine.applyDiff.mockResolvedValue({
|
||||||
|
success: true,
|
||||||
|
workflow: updatedWorkflow,
|
||||||
|
operationsApplied: 2,
|
||||||
|
message: 'Success',
|
||||||
|
errors: [],
|
||||||
|
transferToProjectId: 'project-fail',
|
||||||
|
shouldActivate: true,
|
||||||
|
});
|
||||||
|
mockApiClient.updateWorkflow.mockResolvedValue(updatedWorkflow);
|
||||||
|
mockApiClient.transferWorkflow.mockRejectedValue(new Error('Transfer denied'));
|
||||||
|
mockApiClient.activateWorkflow = vi.fn();
|
||||||
|
|
||||||
|
const result = await handleUpdatePartialWorkflow({
|
||||||
|
id: 'test-workflow-id',
|
||||||
|
operations: [
|
||||||
|
{ type: 'transferWorkflow', destinationProjectId: 'project-fail' },
|
||||||
|
{ type: 'activateWorkflow' },
|
||||||
|
],
|
||||||
|
}, mockRepository);
|
||||||
|
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
expect(result.saved).toBe(true);
|
||||||
|
expect(result.error).toBe('Workflow updated successfully but project transfer failed');
|
||||||
|
expect(mockApiClient.activateWorkflow).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('field name normalization', () => {
|
||||||
|
it('should normalize "name" to "nodeName" for updateNode operations', async () => {
|
||||||
|
const testWorkflow = createTestWorkflow();
|
||||||
|
const updatedWorkflow = { ...testWorkflow };
|
||||||
|
|
||||||
|
mockApiClient.getWorkflow.mockResolvedValue(testWorkflow);
|
||||||
|
mockDiffEngine.applyDiff.mockResolvedValue({
|
||||||
|
success: true,
|
||||||
|
workflow: updatedWorkflow,
|
||||||
|
operationsApplied: 1,
|
||||||
|
message: 'Success',
|
||||||
|
errors: [],
|
||||||
|
});
|
||||||
|
mockApiClient.updateWorkflow.mockResolvedValue(updatedWorkflow);
|
||||||
|
|
||||||
|
await handleUpdatePartialWorkflow({
|
||||||
|
id: 'test-workflow-id',
|
||||||
|
operations: [{
|
||||||
|
type: 'updateNode',
|
||||||
|
name: 'HTTP Request', // LLMs often use "name" instead of "nodeName"
|
||||||
|
updates: { 'parameters.url': 'https://new-url.com' },
|
||||||
|
}],
|
||||||
|
}, mockRepository);
|
||||||
|
|
||||||
|
// Verify the diff engine received nodeName (normalized from name)
|
||||||
|
expect(mockDiffEngine.applyDiff).toHaveBeenCalled();
|
||||||
|
const diffArgs = mockDiffEngine.applyDiff.mock.calls[0][1];
|
||||||
|
expect(diffArgs.operations[0].nodeName).toBe('HTTP Request');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should normalize "id" to "nodeId" for removeNode operations', async () => {
|
||||||
|
const testWorkflow = createTestWorkflow();
|
||||||
|
const updatedWorkflow = { ...testWorkflow };
|
||||||
|
|
||||||
|
mockApiClient.getWorkflow.mockResolvedValue(testWorkflow);
|
||||||
|
mockDiffEngine.applyDiff.mockResolvedValue({
|
||||||
|
success: true,
|
||||||
|
workflow: updatedWorkflow,
|
||||||
|
operationsApplied: 1,
|
||||||
|
message: 'Success',
|
||||||
|
errors: [],
|
||||||
|
});
|
||||||
|
mockApiClient.updateWorkflow.mockResolvedValue(updatedWorkflow);
|
||||||
|
|
||||||
|
await handleUpdatePartialWorkflow({
|
||||||
|
id: 'test-workflow-id',
|
||||||
|
operations: [{
|
||||||
|
type: 'removeNode',
|
||||||
|
id: 'node2', // LLMs may use "id" instead of "nodeId"
|
||||||
|
}],
|
||||||
|
}, mockRepository);
|
||||||
|
|
||||||
|
// Verify the diff engine received nodeId (normalized from id)
|
||||||
|
expect(mockDiffEngine.applyDiff).toHaveBeenCalled();
|
||||||
|
const diffArgs = mockDiffEngine.applyDiff.mock.calls[0][1];
|
||||||
|
expect(diffArgs.operations[0].nodeId).toBe('node2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT normalize "name" for updateName operations', async () => {
|
||||||
|
const testWorkflow = createTestWorkflow();
|
||||||
|
const updatedWorkflow = { ...testWorkflow };
|
||||||
|
|
||||||
|
mockApiClient.getWorkflow.mockResolvedValue(testWorkflow);
|
||||||
|
mockDiffEngine.applyDiff.mockResolvedValue({
|
||||||
|
success: true,
|
||||||
|
workflow: updatedWorkflow,
|
||||||
|
operationsApplied: 1,
|
||||||
|
message: 'Success',
|
||||||
|
errors: [],
|
||||||
|
});
|
||||||
|
mockApiClient.updateWorkflow.mockResolvedValue(updatedWorkflow);
|
||||||
|
|
||||||
|
await handleUpdatePartialWorkflow({
|
||||||
|
id: 'test-workflow-id',
|
||||||
|
operations: [{
|
||||||
|
type: 'updateName',
|
||||||
|
name: 'New Workflow Name', // This is the correct field for updateName
|
||||||
|
}],
|
||||||
|
}, mockRepository);
|
||||||
|
|
||||||
|
// Verify "name" stays as "name" (not moved to nodeName) for updateName
|
||||||
|
expect(mockDiffEngine.applyDiff).toHaveBeenCalled();
|
||||||
|
const diffArgs = mockDiffEngine.applyDiff.mock.calls[0][1];
|
||||||
|
expect(diffArgs.operations[0].name).toBe('New Workflow Name');
|
||||||
|
expect(diffArgs.operations[0].nodeName).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prefer explicit "nodeName" over "name" alias', async () => {
|
||||||
|
const testWorkflow = createTestWorkflow();
|
||||||
|
const updatedWorkflow = { ...testWorkflow };
|
||||||
|
|
||||||
|
mockApiClient.getWorkflow.mockResolvedValue(testWorkflow);
|
||||||
|
mockDiffEngine.applyDiff.mockResolvedValue({
|
||||||
|
success: true,
|
||||||
|
workflow: updatedWorkflow,
|
||||||
|
operationsApplied: 1,
|
||||||
|
message: 'Success',
|
||||||
|
errors: [],
|
||||||
|
});
|
||||||
|
mockApiClient.updateWorkflow.mockResolvedValue(updatedWorkflow);
|
||||||
|
|
||||||
|
await handleUpdatePartialWorkflow({
|
||||||
|
id: 'test-workflow-id',
|
||||||
|
operations: [{
|
||||||
|
type: 'updateNode',
|
||||||
|
nodeName: 'HTTP Request', // Explicit nodeName provided
|
||||||
|
name: 'Should Be Ignored', // Should NOT override nodeName
|
||||||
|
updates: { 'parameters.url': 'https://new-url.com' },
|
||||||
|
}],
|
||||||
|
}, mockRepository);
|
||||||
|
|
||||||
|
expect(mockDiffEngine.applyDiff).toHaveBeenCalled();
|
||||||
|
const diffArgs = mockDiffEngine.applyDiff.mock.calls[0][1];
|
||||||
|
expect(diffArgs.operations[0].nodeName).toBe('HTTP Request');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1250,6 +1250,56 @@ describe('N8nApiClient', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('transferWorkflow', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
client = new N8nApiClient(defaultConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should transfer workflow successfully via PUT', async () => {
|
||||||
|
mockAxiosInstance.put.mockResolvedValue({ data: undefined });
|
||||||
|
|
||||||
|
await client.transferWorkflow('123', 'project-456');
|
||||||
|
|
||||||
|
expect(mockAxiosInstance.put).toHaveBeenCalledWith(
|
||||||
|
'/workflows/123/transfer',
|
||||||
|
{ destinationProjectId: 'project-456' }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw N8nNotFoundError on 404', async () => {
|
||||||
|
const error = {
|
||||||
|
message: 'Request failed',
|
||||||
|
response: { status: 404, data: { message: 'Workflow not found' } }
|
||||||
|
};
|
||||||
|
await mockAxiosInstance.simulateError('put', error);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await client.transferWorkflow('123', 'project-456');
|
||||||
|
expect.fail('Should have thrown an error');
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).toBeInstanceOf(N8nNotFoundError);
|
||||||
|
expect((err as N8nNotFoundError).message).toContain('not found');
|
||||||
|
expect((err as N8nNotFoundError).statusCode).toBe(404);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw appropriate error on 403 forbidden', async () => {
|
||||||
|
const error = {
|
||||||
|
message: 'Request failed',
|
||||||
|
response: { status: 403, data: { message: 'Forbidden' } }
|
||||||
|
};
|
||||||
|
await mockAxiosInstance.simulateError('put', error);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await client.transferWorkflow('123', 'project-456');
|
||||||
|
expect.fail('Should have thrown an error');
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).toBeInstanceOf(N8nApiError);
|
||||||
|
expect((err as N8nApiError).statusCode).toBe(403);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('interceptors', () => {
|
describe('interceptors', () => {
|
||||||
let requestInterceptor: any;
|
let requestInterceptor: any;
|
||||||
let responseInterceptor: any;
|
let responseInterceptor: any;
|
||||||
|
|||||||
@@ -1096,6 +1096,83 @@ describe('n8n-validation', () => {
|
|||||||
expect(disconnectedErrors).toHaveLength(0);
|
expect(disconnectedErrors).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should NOT flag nodes as disconnected when connected via ai_outputParser', () => {
|
||||||
|
const workflow = {
|
||||||
|
name: 'AI Output Parser Workflow',
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'agent-1',
|
||||||
|
name: 'AI Agent',
|
||||||
|
type: '@n8n/n8n-nodes-langchain.agent',
|
||||||
|
typeVersion: 1.6,
|
||||||
|
position: [500, 300] as [number, number],
|
||||||
|
parameters: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'parser-1',
|
||||||
|
name: 'Structured Output Parser',
|
||||||
|
type: '@n8n/n8n-nodes-langchain.outputParserStructured',
|
||||||
|
typeVersion: 1,
|
||||||
|
position: [300, 400] as [number, number],
|
||||||
|
parameters: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
connections: {
|
||||||
|
'Structured Output Parser': {
|
||||||
|
ai_outputParser: [[{ node: 'AI Agent', type: 'ai_outputParser', index: 0 }]],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const errors = validateWorkflowStructure(workflow);
|
||||||
|
const disconnectedErrors = errors.filter(e => e.includes('Disconnected'));
|
||||||
|
expect(disconnectedErrors).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT flag nodes as disconnected when connected via ai_document or ai_textSplitter', () => {
|
||||||
|
const workflow = {
|
||||||
|
name: 'Document Processing Workflow',
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'vs-1',
|
||||||
|
name: 'Pinecone Vector Store',
|
||||||
|
type: '@n8n/n8n-nodes-langchain.vectorStorePinecone',
|
||||||
|
typeVersion: 1,
|
||||||
|
position: [500, 300] as [number, number],
|
||||||
|
parameters: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'doc-1',
|
||||||
|
name: 'Default Data Loader',
|
||||||
|
type: '@n8n/n8n-nodes-langchain.documentDefaultDataLoader',
|
||||||
|
typeVersion: 1,
|
||||||
|
position: [300, 400] as [number, number],
|
||||||
|
parameters: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'splitter-1',
|
||||||
|
name: 'Text Splitter',
|
||||||
|
type: '@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter',
|
||||||
|
typeVersion: 1,
|
||||||
|
position: [100, 400] as [number, number],
|
||||||
|
parameters: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
connections: {
|
||||||
|
'Default Data Loader': {
|
||||||
|
ai_document: [[{ node: 'Pinecone Vector Store', type: 'ai_document', index: 0 }]],
|
||||||
|
},
|
||||||
|
'Text Splitter': {
|
||||||
|
ai_textSplitter: [[{ node: 'Default Data Loader', type: 'ai_textSplitter', index: 0 }]],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const errors = validateWorkflowStructure(workflow);
|
||||||
|
const disconnectedErrors = errors.filter(e => e.includes('Disconnected'));
|
||||||
|
expect(disconnectedErrors).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
it('should still flag truly disconnected nodes in AI workflows', () => {
|
it('should still flag truly disconnected nodes in AI workflows', () => {
|
||||||
const workflow = {
|
const workflow = {
|
||||||
name: 'AI Workflow with Disconnected Node',
|
name: 'AI Workflow with Disconnected Node',
|
||||||
|
|||||||
@@ -1912,6 +1912,55 @@ return [{"json": {"result": result}}]
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not error on primitive return inside helper functions', () => {
|
||||||
|
context.config = {
|
||||||
|
language: 'javaScript',
|
||||||
|
jsCode: 'function isValid(item) { return false; }\nconst items = $input.all();\nreturn items.filter(isValid).map(i => ({json: i.json}));'
|
||||||
|
};
|
||||||
|
|
||||||
|
NodeSpecificValidators.validateCode(context);
|
||||||
|
|
||||||
|
const primitiveErrors = context.errors.filter(e => e.message === 'Cannot return primitive values directly');
|
||||||
|
expect(primitiveErrors).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not error on primitive return inside arrow function helpers', () => {
|
||||||
|
context.config = {
|
||||||
|
language: 'javaScript',
|
||||||
|
jsCode: 'const isValid = (item) => { return false; };\nreturn $input.all().filter(isValid).map(i => ({json: i.json}));'
|
||||||
|
};
|
||||||
|
|
||||||
|
NodeSpecificValidators.validateCode(context);
|
||||||
|
|
||||||
|
const primitiveErrors = context.errors.filter(e => e.message === 'Cannot return primitive values directly');
|
||||||
|
expect(primitiveErrors).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not error on primitive return inside async function helpers', () => {
|
||||||
|
context.config = {
|
||||||
|
language: 'javaScript',
|
||||||
|
jsCode: 'async function fetchData(url) { return null; }\nconst data = await fetchData("https://api.example.com");\nreturn [{json: {data}}];'
|
||||||
|
};
|
||||||
|
|
||||||
|
NodeSpecificValidators.validateCode(context);
|
||||||
|
|
||||||
|
const primitiveErrors = context.errors.filter(e => e.message === 'Cannot return primitive values directly');
|
||||||
|
expect(primitiveErrors).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should still error on primitive return without helper functions', () => {
|
||||||
|
context.config = {
|
||||||
|
language: 'javaScript',
|
||||||
|
jsCode: 'return "success";'
|
||||||
|
};
|
||||||
|
|
||||||
|
NodeSpecificValidators.validateCode(context);
|
||||||
|
|
||||||
|
expect(context.errors).toContainEqual(expect.objectContaining({
|
||||||
|
message: 'Cannot return primitive values directly'
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
it('should error on Python primitive return', () => {
|
it('should error on Python primitive return', () => {
|
||||||
context.config = {
|
context.config = {
|
||||||
language: 'python',
|
language: 'python',
|
||||||
@@ -2038,6 +2087,30 @@ return [{"json": {"result": result}}]
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not warn about $() node reference syntax', () => {
|
||||||
|
context.config = {
|
||||||
|
language: 'javaScript',
|
||||||
|
jsCode: 'const data = $("Previous Node").first().json;\nreturn [{json: data}];'
|
||||||
|
};
|
||||||
|
|
||||||
|
NodeSpecificValidators.validateCode(context);
|
||||||
|
|
||||||
|
const dollarWarnings = context.warnings.filter(w => w.message === 'Invalid $ usage detected');
|
||||||
|
expect(dollarWarnings).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not warn about $_ variables', () => {
|
||||||
|
context.config = {
|
||||||
|
language: 'javaScript',
|
||||||
|
jsCode: 'const $_temp = 1;\nreturn [{json: {value: $_temp}}];'
|
||||||
|
};
|
||||||
|
|
||||||
|
NodeSpecificValidators.validateCode(context);
|
||||||
|
|
||||||
|
const dollarWarnings = context.warnings.filter(w => w.message === 'Invalid $ usage detected');
|
||||||
|
expect(dollarWarnings).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
it('should correct helpers usage', () => {
|
it('should correct helpers usage', () => {
|
||||||
context.config = {
|
context.config = {
|
||||||
language: 'javaScript',
|
language: 'javaScript',
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ import {
|
|||||||
AddTagOperation,
|
AddTagOperation,
|
||||||
RemoveTagOperation,
|
RemoveTagOperation,
|
||||||
CleanStaleConnectionsOperation,
|
CleanStaleConnectionsOperation,
|
||||||
ReplaceConnectionsOperation
|
ReplaceConnectionsOperation,
|
||||||
|
TransferWorkflowOperation
|
||||||
} from '@/types/workflow-diff';
|
} from '@/types/workflow-diff';
|
||||||
import { Workflow } from '@/types/n8n-api';
|
import { Workflow } from '@/types/n8n-api';
|
||||||
|
|
||||||
@@ -4882,4 +4883,258 @@ describe('WorkflowDiffEngine', () => {
|
|||||||
expect(result.workflow.connections['Source Node']['main'][0][0].type).toBe('main');
|
expect(result.workflow.connections['Source Node']['main'][0][0].type).toBe('main');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('null value property deletion', () => {
|
||||||
|
it('should delete a property when value is null', async () => {
|
||||||
|
const node = baseWorkflow.nodes.find((n: any) => n.name === 'HTTP Request')!;
|
||||||
|
(node as any).continueOnFail = true;
|
||||||
|
|
||||||
|
const operation: UpdateNodeOperation = {
|
||||||
|
type: 'updateNode',
|
||||||
|
nodeName: 'HTTP Request',
|
||||||
|
updates: { continueOnFail: null }
|
||||||
|
};
|
||||||
|
|
||||||
|
const request: WorkflowDiffRequest = {
|
||||||
|
id: 'test-workflow',
|
||||||
|
operations: [operation]
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await diffEngine.applyDiff(baseWorkflow, request);
|
||||||
|
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
const updatedNode = result.workflow.nodes.find((n: any) => n.name === 'HTTP Request')!;
|
||||||
|
expect('continueOnFail' in updatedNode).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete a nested property when value is null', async () => {
|
||||||
|
const node = baseWorkflow.nodes.find((n: any) => n.name === 'HTTP Request')!;
|
||||||
|
(node as any).parameters = { url: 'https://example.com', authentication: 'basic' };
|
||||||
|
|
||||||
|
const operation: UpdateNodeOperation = {
|
||||||
|
type: 'updateNode',
|
||||||
|
nodeName: 'HTTP Request',
|
||||||
|
updates: { 'parameters.authentication': null }
|
||||||
|
};
|
||||||
|
|
||||||
|
const request: WorkflowDiffRequest = {
|
||||||
|
id: 'test-workflow',
|
||||||
|
operations: [operation]
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await diffEngine.applyDiff(baseWorkflow, request);
|
||||||
|
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
const updatedNode = result.workflow.nodes.find((n: any) => n.name === 'HTTP Request')!;
|
||||||
|
expect((updatedNode as any).parameters.url).toBe('https://example.com');
|
||||||
|
expect('authentication' in (updatedNode as any).parameters).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set property normally when value is not null', async () => {
|
||||||
|
const operation: UpdateNodeOperation = {
|
||||||
|
type: 'updateNode',
|
||||||
|
nodeName: 'HTTP Request',
|
||||||
|
updates: { continueOnFail: true }
|
||||||
|
};
|
||||||
|
|
||||||
|
const request: WorkflowDiffRequest = {
|
||||||
|
id: 'test-workflow',
|
||||||
|
operations: [operation]
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await diffEngine.applyDiff(baseWorkflow, request);
|
||||||
|
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
const updatedNode = result.workflow.nodes.find((n: any) => n.name === 'HTTP Request')!;
|
||||||
|
expect((updatedNode as any).continueOnFail).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be a no-op when deleting a non-existent property', async () => {
|
||||||
|
const node = baseWorkflow.nodes.find((n: any) => n.name === 'HTTP Request')!;
|
||||||
|
const originalKeys = Object.keys(node).sort();
|
||||||
|
|
||||||
|
const operation: UpdateNodeOperation = {
|
||||||
|
type: 'updateNode',
|
||||||
|
nodeName: 'HTTP Request',
|
||||||
|
updates: { nonExistentProp: null }
|
||||||
|
};
|
||||||
|
|
||||||
|
const request: WorkflowDiffRequest = {
|
||||||
|
id: 'test-workflow',
|
||||||
|
operations: [operation]
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await diffEngine.applyDiff(baseWorkflow, request);
|
||||||
|
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
const updatedNode = result.workflow.nodes.find((n: any) => n.name === 'HTTP Request')!;
|
||||||
|
expect('nonExistentProp' in updatedNode).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip intermediate object creation when deleting from non-existent parent path', async () => {
|
||||||
|
const operation: UpdateNodeOperation = {
|
||||||
|
type: 'updateNode',
|
||||||
|
nodeName: 'HTTP Request',
|
||||||
|
updates: { 'nonExistent.deeply.nested.prop': null }
|
||||||
|
};
|
||||||
|
|
||||||
|
const request: WorkflowDiffRequest = {
|
||||||
|
id: 'test-workflow',
|
||||||
|
operations: [operation]
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await diffEngine.applyDiff(baseWorkflow, request);
|
||||||
|
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
const updatedNode = result.workflow.nodes.find((n: any) => n.name === 'HTTP Request')!;
|
||||||
|
expect('nonExistent' in updatedNode).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('transferWorkflow operation', () => {
|
||||||
|
it('should set transferToProjectId in result for valid transferWorkflow', async () => {
|
||||||
|
const operation: TransferWorkflowOperation = {
|
||||||
|
type: 'transferWorkflow',
|
||||||
|
destinationProjectId: 'project-abc-123'
|
||||||
|
};
|
||||||
|
|
||||||
|
const request: WorkflowDiffRequest = {
|
||||||
|
id: 'test-workflow',
|
||||||
|
operations: [operation]
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await diffEngine.applyDiff(baseWorkflow, request);
|
||||||
|
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.transferToProjectId).toBe('project-abc-123');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail validation when destinationProjectId is empty', async () => {
|
||||||
|
const operation: TransferWorkflowOperation = {
|
||||||
|
type: 'transferWorkflow',
|
||||||
|
destinationProjectId: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
const request: WorkflowDiffRequest = {
|
||||||
|
id: 'test-workflow',
|
||||||
|
operations: [operation]
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await diffEngine.applyDiff(baseWorkflow, request);
|
||||||
|
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
expect(result.errors).toBeDefined();
|
||||||
|
expect(result.errors![0].message).toContain('destinationProjectId');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail validation when destinationProjectId is undefined', async () => {
|
||||||
|
const operation = {
|
||||||
|
type: 'transferWorkflow',
|
||||||
|
destinationProjectId: undefined
|
||||||
|
} as any as TransferWorkflowOperation;
|
||||||
|
|
||||||
|
const request: WorkflowDiffRequest = {
|
||||||
|
id: 'test-workflow',
|
||||||
|
operations: [operation]
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await diffEngine.applyDiff(baseWorkflow, request);
|
||||||
|
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
expect(result.errors).toBeDefined();
|
||||||
|
expect(result.errors![0].message).toContain('destinationProjectId');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not include transferToProjectId when no transferWorkflow operation is present', async () => {
|
||||||
|
const operation: UpdateNameOperation = {
|
||||||
|
type: 'updateName',
|
||||||
|
name: 'Renamed Workflow'
|
||||||
|
};
|
||||||
|
|
||||||
|
const request: WorkflowDiffRequest = {
|
||||||
|
id: 'test-workflow',
|
||||||
|
operations: [operation]
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await diffEngine.applyDiff(baseWorkflow, request);
|
||||||
|
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.transferToProjectId).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should combine updateName and transferWorkflow operations', async () => {
|
||||||
|
const operations: WorkflowDiffOperation[] = [
|
||||||
|
{
|
||||||
|
type: 'updateName',
|
||||||
|
name: 'Transferred Workflow'
|
||||||
|
} as UpdateNameOperation,
|
||||||
|
{
|
||||||
|
type: 'transferWorkflow',
|
||||||
|
destinationProjectId: 'project-xyz-789'
|
||||||
|
} as TransferWorkflowOperation
|
||||||
|
];
|
||||||
|
|
||||||
|
const request: WorkflowDiffRequest = {
|
||||||
|
id: 'test-workflow',
|
||||||
|
operations
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await diffEngine.applyDiff(baseWorkflow, request);
|
||||||
|
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.operationsApplied).toBe(2);
|
||||||
|
expect(result.workflow!.name).toBe('Transferred Workflow');
|
||||||
|
expect(result.transferToProjectId).toBe('project-xyz-789');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should combine removeTag and transferWorkflow in continueOnError mode', async () => {
|
||||||
|
const operations: WorkflowDiffOperation[] = [
|
||||||
|
{
|
||||||
|
type: 'removeTag',
|
||||||
|
tag: 'non-existent-tag'
|
||||||
|
} as RemoveTagOperation,
|
||||||
|
{
|
||||||
|
type: 'transferWorkflow',
|
||||||
|
destinationProjectId: 'project-target-456'
|
||||||
|
} as TransferWorkflowOperation
|
||||||
|
];
|
||||||
|
|
||||||
|
const request: WorkflowDiffRequest = {
|
||||||
|
id: 'test-workflow',
|
||||||
|
operations,
|
||||||
|
continueOnError: true
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await diffEngine.applyDiff(baseWorkflow, request);
|
||||||
|
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.transferToProjectId).toBe('project-target-456');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail entire batch in atomic mode when transferWorkflow has empty destinationProjectId alongside updateName', async () => {
|
||||||
|
const operations: WorkflowDiffOperation[] = [
|
||||||
|
{
|
||||||
|
type: 'updateName',
|
||||||
|
name: 'Should Not Apply'
|
||||||
|
} as UpdateNameOperation,
|
||||||
|
{
|
||||||
|
type: 'transferWorkflow',
|
||||||
|
destinationProjectId: ''
|
||||||
|
} as TransferWorkflowOperation
|
||||||
|
];
|
||||||
|
|
||||||
|
const request: WorkflowDiffRequest = {
|
||||||
|
id: 'test-workflow',
|
||||||
|
operations
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await diffEngine.applyDiff(baseWorkflow, request);
|
||||||
|
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
expect(result.errors).toBeDefined();
|
||||||
|
expect(result.errors![0].message).toContain('destinationProjectId');
|
||||||
|
// In atomic mode, the workflow should not be returned since the batch failed
|
||||||
|
expect(result.workflow).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user