fix: correct operator names, connection types, and implement __patch_find_replace (#665, #659, #642) (#672)

Three critical fixes in n8n_update_partial_workflow:

- **#665**: Replace incorrect `isNotEmpty`/`isEmpty` operator names with `notEmpty`/`empty`
  across validators, sanitizer, docs, and error messages. Add auto-correction in sanitizer.
  Unknown operators silently returned false in n8n's execution engine.

- **#659**: Remap numeric `targetInput` values (e.g., "0") to "main" in addConnection.
  Relax sourceOutput remapping guard for redundant sourceOutput+sourceIndex combinations.
  Also resolves #653 (dangling connections caused by malformed type:"0" connections).

- **#642**: Implement __patch_find_replace for surgical string edits in updateNode.
  Previously stored patch objects literally as jsCode, producing [object Object].
  Now reads current value, applies find/replace sequentially, writes back the string.

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Romuald Członkowski
2026-03-27 16:26:21 +01:00
committed by GitHub
parent de2abaf89d
commit 6be9ffa53e
28 changed files with 522 additions and 96 deletions

View File

@@ -120,8 +120,8 @@ When ANY workflow update is made, ALL nodes in the workflow are automatically sa
1. **Operator Structure Fixes**:
- Binary operators (equals, contains, greaterThan, etc.) automatically have \`singleValue\` removed
- Unary operators (isEmpty, isNotEmpty, true, false) automatically get \`singleValue: true\` added
- Invalid operator structures (e.g., \`{type: "isNotEmpty"}\`) are corrected to \`{type: "boolean", operation: "isNotEmpty"}\`
- Unary operators (empty, notEmpty, true, false) automatically get \`singleValue: true\` added
- Invalid operator structures (e.g., \`{type: "notEmpty"}\`) are corrected to \`{type: "object", operation: "notEmpty"}\`
2. **Missing Metadata Added**:
- IF nodes with conditions get complete \`conditions.options\` structure if missing
@@ -334,6 +334,8 @@ n8n_update_partial_workflow({
'// Best-effort mode: apply what works, report what fails\nn8n_update_partial_workflow({id: "vwx", operations: [\n {type: "updateName", name: "Fixed Workflow"},\n {type: "removeConnection", source: "Broken", target: "Node"},\n {type: "cleanStaleConnections"}\n], continueOnError: true})',
'// Update node parameter\nn8n_update_partial_workflow({id: "yza", operations: [{type: "updateNode", nodeName: "HTTP Request", updates: {"parameters.url": "https://api.example.com"}}]})',
'// Validate before applying\nn8n_update_partial_workflow({id: "bcd", operations: [{type: "removeNode", nodeName: "Old Process"}], validateOnly: true})',
'// Surgically edit code using __patch_find_replace (avoids replacing entire code block)\nn8n_update_partial_workflow({id: "pfr1", operations: [{type: "updateNode", nodeName: "Code", updates: {"parameters.jsCode": {"__patch_find_replace": [{"find": "const limit = 10;", "replace": "const limit = 50;"}]}}}]})',
'// Multiple sequential patches on the same property\nn8n_update_partial_workflow({id: "pfr2", operations: [{type: "updateNode", nodeName: "Code", updates: {"parameters.jsCode": {"__patch_find_replace": [{"find": "api.old-domain.com", "replace": "api.new-domain.com"}, {"find": "Authorization: Bearer old_token", "replace": "Authorization: Bearer new_token"}]}}}]})',
'\n// ============ AI CONNECTION EXAMPLES ============',
'// Connect language model to AI Agent\nn8n_update_partial_workflow({id: "ai1", operations: [{type: "addConnection", source: "OpenAI Chat Model", target: "AI Agent", sourceOutput: "ai_languageModel"}]})',
'// Connect tool to AI Agent\nn8n_update_partial_workflow({id: "ai2", operations: [{type: "addConnection", source: "HTTP Request Tool", target: "AI Agent", sourceOutput: "ai_tool"}]})',
@@ -412,10 +414,12 @@ n8n_update_partial_workflow({
'**CRITICAL**: For Switch nodes, ALWAYS use case=N instead of sourceIndex. Using same sourceIndex for multiple connections will put them on the same case output.',
'cleanStaleConnections removes ALL broken connections - cannot be selective',
'replaceConnections overwrites entire connections object - all previous connections lost',
'**Auto-sanitization behavior**: Binary operators (equals, contains) automatically have singleValue removed; unary operators (isEmpty, isNotEmpty) automatically get singleValue:true added',
'**Auto-sanitization behavior**: Binary operators (equals, contains) automatically have singleValue removed; unary operators (empty, notEmpty) automatically get singleValue:true added',
'**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',
'**Corrupted workflows beyond repair**: Workflows in paradoxical states (API returns corrupt, API rejects updates) cannot be fixed via API - must be recreated',
'**__patch_find_replace for code edits**: Instead of replacing entire code blocks, use `{"parameters.jsCode": {"__patch_find_replace": [{"find": "old text", "replace": "new text"}]}}` to surgically edit string properties',
'__patch_find_replace replaces the FIRST occurrence of each find string. Patches are applied sequentially — order matters',
'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 null',
'Removing a required property may cause validation errors - check node documentation first',