agentic-qe 2.5.5 → 2.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/n8n/n8n-base-agent.md +376 -0
- package/.claude/agents/n8n/n8n-bdd-scenario-tester.md +613 -0
- package/.claude/agents/n8n/n8n-chaos-tester.md +654 -0
- package/.claude/agents/n8n/n8n-ci-orchestrator.md +850 -0
- package/.claude/agents/n8n/n8n-compliance-validator.md +685 -0
- package/.claude/agents/n8n/n8n-expression-validator.md +560 -0
- package/.claude/agents/n8n/n8n-integration-test.md +602 -0
- package/.claude/agents/n8n/n8n-monitoring-validator.md +589 -0
- package/.claude/agents/n8n/n8n-node-validator.md +455 -0
- package/.claude/agents/n8n/n8n-performance-tester.md +630 -0
- package/.claude/agents/n8n/n8n-security-auditor.md +786 -0
- package/.claude/agents/n8n/n8n-trigger-test.md +500 -0
- package/.claude/agents/n8n/n8n-unit-tester.md +633 -0
- package/.claude/agents/n8n/n8n-version-comparator.md +567 -0
- package/.claude/agents/n8n/n8n-workflow-executor.md +392 -0
- package/.claude/skills/n8n-expression-testing/SKILL.md +434 -0
- package/.claude/skills/n8n-integration-testing-patterns/SKILL.md +540 -0
- package/.claude/skills/n8n-security-testing/SKILL.md +599 -0
- package/.claude/skills/n8n-trigger-testing-strategies/SKILL.md +541 -0
- package/.claude/skills/n8n-workflow-testing-fundamentals/SKILL.md +447 -0
- package/CHANGELOG.md +111 -0
- package/README.md +7 -4
- package/dist/adapters/MemoryStoreAdapter.d.ts +75 -123
- package/dist/adapters/MemoryStoreAdapter.d.ts.map +1 -1
- package/dist/adapters/MemoryStoreAdapter.js +204 -219
- package/dist/adapters/MemoryStoreAdapter.js.map +1 -1
- package/dist/agents/AccessibilityAllyAgent.d.ts.map +1 -1
- package/dist/agents/AccessibilityAllyAgent.js +17 -1
- package/dist/agents/AccessibilityAllyAgent.js.map +1 -1
- package/dist/agents/BaseAgent.d.ts +18 -250
- package/dist/agents/BaseAgent.d.ts.map +1 -1
- package/dist/agents/BaseAgent.js +122 -520
- package/dist/agents/BaseAgent.js.map +1 -1
- package/dist/agents/n8n/N8nAPIClient.d.ts +121 -0
- package/dist/agents/n8n/N8nAPIClient.d.ts.map +1 -0
- package/dist/agents/n8n/N8nAPIClient.js +367 -0
- package/dist/agents/n8n/N8nAPIClient.js.map +1 -0
- package/dist/agents/n8n/N8nAuditPersistence.d.ts +120 -0
- package/dist/agents/n8n/N8nAuditPersistence.d.ts.map +1 -0
- package/dist/agents/n8n/N8nAuditPersistence.js +473 -0
- package/dist/agents/n8n/N8nAuditPersistence.js.map +1 -0
- package/dist/agents/n8n/N8nBDDScenarioTesterAgent.d.ts +159 -0
- package/dist/agents/n8n/N8nBDDScenarioTesterAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nBDDScenarioTesterAgent.js +697 -0
- package/dist/agents/n8n/N8nBDDScenarioTesterAgent.js.map +1 -0
- package/dist/agents/n8n/N8nBaseAgent.d.ts +126 -0
- package/dist/agents/n8n/N8nBaseAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nBaseAgent.js +446 -0
- package/dist/agents/n8n/N8nBaseAgent.js.map +1 -0
- package/dist/agents/n8n/N8nCIOrchestratorAgent.d.ts +164 -0
- package/dist/agents/n8n/N8nCIOrchestratorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nCIOrchestratorAgent.js +610 -0
- package/dist/agents/n8n/N8nCIOrchestratorAgent.js.map +1 -0
- package/dist/agents/n8n/N8nChaosTesterAgent.d.ts +205 -0
- package/dist/agents/n8n/N8nChaosTesterAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nChaosTesterAgent.js +729 -0
- package/dist/agents/n8n/N8nChaosTesterAgent.js.map +1 -0
- package/dist/agents/n8n/N8nComplianceValidatorAgent.d.ts +228 -0
- package/dist/agents/n8n/N8nComplianceValidatorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nComplianceValidatorAgent.js +986 -0
- package/dist/agents/n8n/N8nComplianceValidatorAgent.js.map +1 -0
- package/dist/agents/n8n/N8nContractTesterAgent.d.ts +213 -0
- package/dist/agents/n8n/N8nContractTesterAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nContractTesterAgent.js +989 -0
- package/dist/agents/n8n/N8nContractTesterAgent.js.map +1 -0
- package/dist/agents/n8n/N8nExpressionValidatorAgent.d.ts +99 -0
- package/dist/agents/n8n/N8nExpressionValidatorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nExpressionValidatorAgent.js +632 -0
- package/dist/agents/n8n/N8nExpressionValidatorAgent.js.map +1 -0
- package/dist/agents/n8n/N8nFailureModeTesterAgent.d.ts +238 -0
- package/dist/agents/n8n/N8nFailureModeTesterAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nFailureModeTesterAgent.js +956 -0
- package/dist/agents/n8n/N8nFailureModeTesterAgent.js.map +1 -0
- package/dist/agents/n8n/N8nIdempotencyTesterAgent.d.ts +242 -0
- package/dist/agents/n8n/N8nIdempotencyTesterAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nIdempotencyTesterAgent.js +992 -0
- package/dist/agents/n8n/N8nIdempotencyTesterAgent.js.map +1 -0
- package/dist/agents/n8n/N8nIntegrationTestAgent.d.ts +104 -0
- package/dist/agents/n8n/N8nIntegrationTestAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nIntegrationTestAgent.js +653 -0
- package/dist/agents/n8n/N8nIntegrationTestAgent.js.map +1 -0
- package/dist/agents/n8n/N8nMonitoringValidatorAgent.d.ts +210 -0
- package/dist/agents/n8n/N8nMonitoringValidatorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nMonitoringValidatorAgent.js +669 -0
- package/dist/agents/n8n/N8nMonitoringValidatorAgent.js.map +1 -0
- package/dist/agents/n8n/N8nNodeValidatorAgent.d.ts +142 -0
- package/dist/agents/n8n/N8nNodeValidatorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nNodeValidatorAgent.js +1090 -0
- package/dist/agents/n8n/N8nNodeValidatorAgent.js.map +1 -0
- package/dist/agents/n8n/N8nPerformanceTesterAgent.d.ts +198 -0
- package/dist/agents/n8n/N8nPerformanceTesterAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nPerformanceTesterAgent.js +653 -0
- package/dist/agents/n8n/N8nPerformanceTesterAgent.js.map +1 -0
- package/dist/agents/n8n/N8nReplayabilityTesterAgent.d.ts +245 -0
- package/dist/agents/n8n/N8nReplayabilityTesterAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nReplayabilityTesterAgent.js +952 -0
- package/dist/agents/n8n/N8nReplayabilityTesterAgent.js.map +1 -0
- package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.d.ts +325 -0
- package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.js +1187 -0
- package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.js.map +1 -0
- package/dist/agents/n8n/N8nSecurityAuditorAgent.d.ts +91 -0
- package/dist/agents/n8n/N8nSecurityAuditorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nSecurityAuditorAgent.js +825 -0
- package/dist/agents/n8n/N8nSecurityAuditorAgent.js.map +1 -0
- package/dist/agents/n8n/N8nTestHarness.d.ts +131 -0
- package/dist/agents/n8n/N8nTestHarness.d.ts.map +1 -0
- package/dist/agents/n8n/N8nTestHarness.js +456 -0
- package/dist/agents/n8n/N8nTestHarness.js.map +1 -0
- package/dist/agents/n8n/N8nTriggerTestAgent.d.ts +119 -0
- package/dist/agents/n8n/N8nTriggerTestAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nTriggerTestAgent.js +652 -0
- package/dist/agents/n8n/N8nTriggerTestAgent.js.map +1 -0
- package/dist/agents/n8n/N8nUnitTesterAgent.d.ts +130 -0
- package/dist/agents/n8n/N8nUnitTesterAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nUnitTesterAgent.js +522 -0
- package/dist/agents/n8n/N8nUnitTesterAgent.js.map +1 -0
- package/dist/agents/n8n/N8nVersionComparatorAgent.d.ts +201 -0
- package/dist/agents/n8n/N8nVersionComparatorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nVersionComparatorAgent.js +645 -0
- package/dist/agents/n8n/N8nVersionComparatorAgent.js.map +1 -0
- package/dist/agents/n8n/N8nWorkflowExecutorAgent.d.ts +120 -0
- package/dist/agents/n8n/N8nWorkflowExecutorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nWorkflowExecutorAgent.js +347 -0
- package/dist/agents/n8n/N8nWorkflowExecutorAgent.js.map +1 -0
- package/dist/agents/n8n/index.d.ts +119 -0
- package/dist/agents/n8n/index.d.ts.map +1 -0
- package/dist/agents/n8n/index.js +298 -0
- package/dist/agents/n8n/index.js.map +1 -0
- package/dist/agents/n8n/types.d.ts +486 -0
- package/dist/agents/n8n/types.d.ts.map +1 -0
- package/dist/agents/n8n/types.js +8 -0
- package/dist/agents/n8n/types.js.map +1 -0
- package/dist/agents/utils/generators.d.ts +30 -0
- package/dist/agents/utils/generators.d.ts.map +1 -0
- package/dist/agents/utils/generators.js +44 -0
- package/dist/agents/utils/generators.js.map +1 -0
- package/dist/agents/utils/index.d.ts +10 -0
- package/dist/agents/utils/index.d.ts.map +1 -0
- package/dist/agents/utils/index.js +19 -0
- package/dist/agents/utils/index.js.map +1 -0
- package/dist/agents/utils/validation.d.ts +72 -0
- package/dist/agents/utils/validation.d.ts.map +1 -0
- package/dist/agents/utils/validation.js +75 -0
- package/dist/agents/utils/validation.js.map +1 -0
- package/dist/cli/init/agents.d.ts.map +1 -1
- package/dist/cli/init/agents.js +29 -0
- package/dist/cli/init/agents.js.map +1 -1
- package/dist/cli/init/skills.d.ts.map +1 -1
- package/dist/cli/init/skills.js +7 -1
- package/dist/cli/init/skills.js.map +1 -1
- package/dist/core/memory/HNSWVectorMemory.js +1 -1
- package/dist/core/memory/SwarmMemoryManager.d.ts +114 -90
- package/dist/core/memory/SwarmMemoryManager.d.ts.map +1 -1
- package/dist/core/memory/SwarmMemoryManager.js +277 -235
- package/dist/core/memory/SwarmMemoryManager.js.map +1 -1
- package/dist/learning/baselines/StandardTaskSuite.d.ts.map +1 -1
- package/dist/learning/baselines/StandardTaskSuite.js +38 -0
- package/dist/learning/baselines/StandardTaskSuite.js.map +1 -1
- package/dist/mcp/server-instructions.d.ts +1 -1
- package/dist/mcp/server-instructions.js +1 -1
- package/dist/types/memory-interfaces.d.ts +76 -68
- package/dist/types/memory-interfaces.d.ts.map +1 -1
- package/dist/types/memory-interfaces.js +3 -0
- package/dist/types/memory-interfaces.js.map +1 -1
- package/docs/reference/agents.md +91 -2
- package/docs/reference/skills.md +97 -2
- package/package.json +2 -2
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: n8n-expression-testing
|
|
3
|
+
description: "n8n expression syntax validation, context-aware testing, common pitfalls detection, and performance optimization. Use when validating n8n expressions and data transformations."
|
|
4
|
+
category: n8n-testing
|
|
5
|
+
priority: high
|
|
6
|
+
tokenEstimate: 1000
|
|
7
|
+
agents: [n8n-expression-validator]
|
|
8
|
+
implementation_status: production
|
|
9
|
+
optimization_version: 1.0
|
|
10
|
+
last_optimized: 2025-12-15
|
|
11
|
+
dependencies: []
|
|
12
|
+
quick_reference_card: true
|
|
13
|
+
tags: [n8n, expressions, javascript, data-transformation, validation]
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# n8n Expression Testing
|
|
17
|
+
|
|
18
|
+
<default_to_action>
|
|
19
|
+
When testing n8n expressions:
|
|
20
|
+
1. VALIDATE syntax before execution
|
|
21
|
+
2. TEST with multiple context scenarios
|
|
22
|
+
3. CHECK for null/undefined handling
|
|
23
|
+
4. VERIFY type safety
|
|
24
|
+
5. SCAN for security vulnerabilities
|
|
25
|
+
|
|
26
|
+
**Quick Expression Checklist:**
|
|
27
|
+
- Valid JavaScript syntax
|
|
28
|
+
- Context variables properly referenced ($json, $node)
|
|
29
|
+
- Null-safe access patterns (?., ??)
|
|
30
|
+
- No dangerous functions (eval, Function)
|
|
31
|
+
- Efficient for large data sets
|
|
32
|
+
|
|
33
|
+
**Common Pitfalls:**
|
|
34
|
+
- Accessing nested properties without null checks
|
|
35
|
+
- Type coercion issues
|
|
36
|
+
- Missing fallback values
|
|
37
|
+
- Inefficient array operations
|
|
38
|
+
</default_to_action>
|
|
39
|
+
|
|
40
|
+
## Quick Reference Card
|
|
41
|
+
|
|
42
|
+
### n8n Expression Syntax
|
|
43
|
+
|
|
44
|
+
| Pattern | Example | Description |
|
|
45
|
+
|---------|---------|-------------|
|
|
46
|
+
| Basic access | `{{ $json.field }}` | Access JSON field |
|
|
47
|
+
| Nested access | `{{ $json.user.email }}` | Access nested property |
|
|
48
|
+
| Array access | `{{ $json.items[0] }}` | Access array element |
|
|
49
|
+
| Node reference | `{{ $node["Name"].json.id }}` | Access other node's data |
|
|
50
|
+
| Method call | `{{ $json.name.toLowerCase() }}` | Call string method |
|
|
51
|
+
| Conditional | `{{ $json.x ? "yes" : "no" }}` | Ternary expression |
|
|
52
|
+
|
|
53
|
+
### Context Variables
|
|
54
|
+
|
|
55
|
+
| Variable | Description | Example |
|
|
56
|
+
|----------|-------------|---------|
|
|
57
|
+
| `$json` | Current item data | `{{ $json.email }}` |
|
|
58
|
+
| `$node["Name"]` | Other node's data | `{{ $node["HTTP"].json.body }}` |
|
|
59
|
+
| `$items()` | Multiple items | `{{ $items("Node", 0, 0).json }}` |
|
|
60
|
+
| `$now` | Current timestamp | `{{ $now.toISO() }}` |
|
|
61
|
+
| `$today` | Today's date | `{{ $today }}` |
|
|
62
|
+
| `$runIndex` | Run iteration | `{{ $runIndex }}` |
|
|
63
|
+
| `$workflow` | Workflow info | `{{ $workflow.name }}` |
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Expression Syntax Patterns
|
|
68
|
+
|
|
69
|
+
### Safe Data Access
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
// BAD: Can fail if nested objects are null
|
|
73
|
+
{{ $json.user.profile.email }}
|
|
74
|
+
|
|
75
|
+
// GOOD: Optional chaining with fallback
|
|
76
|
+
{{ $json.user?.profile?.email ?? '' }}
|
|
77
|
+
|
|
78
|
+
// BAD: Array access without bounds check
|
|
79
|
+
{{ $json.items[0].name }}
|
|
80
|
+
|
|
81
|
+
// GOOD: Safe array access
|
|
82
|
+
{{ $json.items?.[0]?.name ?? 'No items' }}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Type Conversions
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
// String to Number
|
|
89
|
+
{{ parseInt($json.quantity, 10) }}
|
|
90
|
+
{{ parseFloat($json.price) }}
|
|
91
|
+
{{ Number($json.value) }}
|
|
92
|
+
|
|
93
|
+
// Number to String
|
|
94
|
+
{{ String($json.id) }}
|
|
95
|
+
{{ $json.amount.toString() }}
|
|
96
|
+
{{ $json.count.toFixed(2) }}
|
|
97
|
+
|
|
98
|
+
// Date handling
|
|
99
|
+
{{ new Date($json.timestamp).toISOString() }}
|
|
100
|
+
{{ DateTime.fromISO($json.date).toFormat('yyyy-MM-dd') }}
|
|
101
|
+
|
|
102
|
+
// Boolean conversion
|
|
103
|
+
{{ Boolean($json.active) }}
|
|
104
|
+
{{ $json.enabled === 'true' }}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### String Operations
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
// Case conversion
|
|
111
|
+
{{ $json.name.toLowerCase() }}
|
|
112
|
+
{{ $json.name.toUpperCase() }}
|
|
113
|
+
{{ $json.name.charAt(0).toUpperCase() + $json.name.slice(1) }}
|
|
114
|
+
|
|
115
|
+
// String manipulation
|
|
116
|
+
{{ $json.text.trim() }}
|
|
117
|
+
{{ $json.text.replace(/\s+/g, ' ') }}
|
|
118
|
+
{{ $json.text.substring(0, 100) }}
|
|
119
|
+
|
|
120
|
+
// Template strings
|
|
121
|
+
{{ `Hello, ${$json.firstName} ${$json.lastName}!` }}
|
|
122
|
+
{{ `Order #${$json.orderId} - ${$json.status}` }}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Array Operations
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
// Mapping
|
|
129
|
+
{{ $json.items.map(item => item.name) }}
|
|
130
|
+
{{ $json.items.map(item => ({ id: item.id, total: item.price * item.qty })) }}
|
|
131
|
+
|
|
132
|
+
// Filtering
|
|
133
|
+
{{ $json.items.filter(item => item.active) }}
|
|
134
|
+
{{ $json.items.filter(item => item.price > 100) }}
|
|
135
|
+
|
|
136
|
+
// Reducing
|
|
137
|
+
{{ $json.items.reduce((sum, item) => sum + item.price, 0) }}
|
|
138
|
+
{{ $json.items.reduce((acc, item) => ({ ...acc, [item.id]: item }), {}) }}
|
|
139
|
+
|
|
140
|
+
// Finding
|
|
141
|
+
{{ $json.items.find(item => item.id === $json.targetId) }}
|
|
142
|
+
{{ $json.items.findIndex(item => item.name === 'target') }}
|
|
143
|
+
|
|
144
|
+
// Joining
|
|
145
|
+
{{ $json.tags.join(', ') }}
|
|
146
|
+
{{ $json.items.map(i => i.name).join(' | ') }}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Validation Patterns
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
// Validate expression syntax
|
|
155
|
+
function validateExpressionSyntax(expression: string): ValidationResult {
|
|
156
|
+
// Remove n8n template markers
|
|
157
|
+
const code = expression.replace(/\{\{|\}\}/g, '').trim();
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
// Check if valid JavaScript
|
|
161
|
+
new Function(`return (${code})`);
|
|
162
|
+
return { valid: true };
|
|
163
|
+
} catch (error) {
|
|
164
|
+
return {
|
|
165
|
+
valid: false,
|
|
166
|
+
error: error.message,
|
|
167
|
+
suggestion: suggestFix(error.message, code)
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Validate context variables
|
|
173
|
+
function validateContextVariables(expression: string): string[] {
|
|
174
|
+
const contextVars = ['$json', '$node', '$items', '$now', '$today', '$runIndex', '$workflow'];
|
|
175
|
+
const usedVars = [];
|
|
176
|
+
const invalidVars = [];
|
|
177
|
+
|
|
178
|
+
// Find all $ prefixed variables
|
|
179
|
+
const varPattern = /\$\w+/g;
|
|
180
|
+
let match;
|
|
181
|
+
|
|
182
|
+
while ((match = varPattern.exec(expression)) !== null) {
|
|
183
|
+
const varName = match[0];
|
|
184
|
+
if (contextVars.some(cv => varName.startsWith(cv))) {
|
|
185
|
+
usedVars.push(varName);
|
|
186
|
+
} else {
|
|
187
|
+
invalidVars.push(varName);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return { usedVars, invalidVars };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Test expression with sample data
|
|
195
|
+
function testExpression(expression: string, context: any): TestResult {
|
|
196
|
+
const code = expression.replace(/\{\{|\}\}/g, '').trim();
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
// Create function with context
|
|
200
|
+
const fn = new Function('$json', '$node', '$items', '$now', '$today',
|
|
201
|
+
`return (${code})`);
|
|
202
|
+
|
|
203
|
+
const result = fn(
|
|
204
|
+
context.$json || {},
|
|
205
|
+
context.$node || {},
|
|
206
|
+
context.$items || (() => ({})),
|
|
207
|
+
context.$now || new Date(),
|
|
208
|
+
context.$today || new Date()
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
return { success: true, result };
|
|
212
|
+
} catch (error) {
|
|
213
|
+
return { success: false, error: error.message };
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Common Errors and Fixes
|
|
221
|
+
|
|
222
|
+
### Undefined Property Access
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
// ERROR: Cannot read property 'email' of undefined
|
|
226
|
+
{{ $json.user.email }}
|
|
227
|
+
|
|
228
|
+
// FIX 1: Optional chaining
|
|
229
|
+
{{ $json.user?.email }}
|
|
230
|
+
|
|
231
|
+
// FIX 2: With fallback
|
|
232
|
+
{{ $json.user?.email ?? 'no-email@example.com' }}
|
|
233
|
+
|
|
234
|
+
// FIX 3: Conditional
|
|
235
|
+
{{ $json.user ? $json.user.email : '' }}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Type Errors
|
|
239
|
+
|
|
240
|
+
```javascript
|
|
241
|
+
// ERROR: toLowerCase is not a function (when null)
|
|
242
|
+
{{ $json.name.toLowerCase() }}
|
|
243
|
+
|
|
244
|
+
// FIX: Null check first
|
|
245
|
+
{{ $json.name?.toLowerCase() ?? '' }}
|
|
246
|
+
|
|
247
|
+
// ERROR: toFixed is not a function (string instead of number)
|
|
248
|
+
{{ $json.price.toFixed(2) }}
|
|
249
|
+
|
|
250
|
+
// FIX: Parse as number first
|
|
251
|
+
{{ parseFloat($json.price).toFixed(2) }}
|
|
252
|
+
|
|
253
|
+
// ERROR: map is not a function (not an array)
|
|
254
|
+
{{ $json.items.map(i => i.name) }}
|
|
255
|
+
|
|
256
|
+
// FIX: Ensure array
|
|
257
|
+
{{ (Array.isArray($json.items) ? $json.items : []).map(i => i.name) }}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Node Reference Errors
|
|
261
|
+
|
|
262
|
+
```javascript
|
|
263
|
+
// ERROR: Node "Previous Node" not found
|
|
264
|
+
{{ $node["Previous Node"].json.data }}
|
|
265
|
+
|
|
266
|
+
// FIX: Use exact node name (case-sensitive)
|
|
267
|
+
{{ $node["Previous Node1"].json.data }}
|
|
268
|
+
|
|
269
|
+
// FIX: Add fallback for safety
|
|
270
|
+
{{ $node["Previous Node"]?.json?.data ?? {} }}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Security Patterns
|
|
276
|
+
|
|
277
|
+
### Dangerous Functions to Avoid
|
|
278
|
+
|
|
279
|
+
```javascript
|
|
280
|
+
// DANGEROUS: Never use eval
|
|
281
|
+
{{ eval($json.code) }}
|
|
282
|
+
|
|
283
|
+
// DANGEROUS: Dynamic function creation
|
|
284
|
+
{{ new Function($json.code)() }}
|
|
285
|
+
|
|
286
|
+
// DANGEROUS: setTimeout with string
|
|
287
|
+
{{ setTimeout($json.code, 1000) }}
|
|
288
|
+
|
|
289
|
+
// SAFE: Use explicit operations instead
|
|
290
|
+
{{ $json.value * 2 }}
|
|
291
|
+
{{ JSON.parse($json.jsonString) }}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Input Validation
|
|
295
|
+
|
|
296
|
+
```javascript
|
|
297
|
+
// Validate email format
|
|
298
|
+
{{ /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test($json.email) ? $json.email : '' }}
|
|
299
|
+
|
|
300
|
+
// Sanitize for HTML (basic)
|
|
301
|
+
{{ $json.text.replace(/[<>&"']/g, c => ({
|
|
302
|
+
'<': '<', '>': '>', '&': '&', '"': '"', "'": '''
|
|
303
|
+
}[c])) }}
|
|
304
|
+
|
|
305
|
+
// Limit string length
|
|
306
|
+
{{ $json.input.substring(0, 1000) }}
|
|
307
|
+
|
|
308
|
+
// Validate number range
|
|
309
|
+
{{ Math.min(Math.max(parseInt($json.value), 0), 100) }}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Performance Optimization
|
|
315
|
+
|
|
316
|
+
### Efficient Array Operations
|
|
317
|
+
|
|
318
|
+
```javascript
|
|
319
|
+
// SLOW: Multiple iterations
|
|
320
|
+
{{ $json.items.filter(i => i.active).map(i => i.name).join(', ') }}
|
|
321
|
+
|
|
322
|
+
// FASTER: Single reduce
|
|
323
|
+
{{ $json.items.reduce((acc, i) => i.active ? (acc ? `${acc}, ${i.name}` : i.name) : acc, '') }}
|
|
324
|
+
|
|
325
|
+
// SLOW: Nested loops
|
|
326
|
+
{{ $json.items.map(i => $json.categories.find(c => c.id === i.categoryId)) }}
|
|
327
|
+
|
|
328
|
+
// FASTER: Create lookup map first (in Code node)
|
|
329
|
+
const categoryMap = Object.fromEntries($json.categories.map(c => [c.id, c]));
|
|
330
|
+
return $json.items.map(i => categoryMap[i.categoryId]);
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Avoid in Expressions
|
|
334
|
+
|
|
335
|
+
```javascript
|
|
336
|
+
// AVOID: Complex logic in expressions
|
|
337
|
+
{{ $json.items.reduce((acc, item) => {
|
|
338
|
+
const category = $json.categories.find(c => c.id === item.catId);
|
|
339
|
+
if (category && category.active) {
|
|
340
|
+
acc.push({ ...item, categoryName: category.name });
|
|
341
|
+
}
|
|
342
|
+
return acc;
|
|
343
|
+
}, []) }}
|
|
344
|
+
|
|
345
|
+
// BETTER: Move to Code node for complex transformations
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## Testing Patterns
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
// Expression test suite
|
|
354
|
+
const expressionTests = [
|
|
355
|
+
{
|
|
356
|
+
name: 'Basic property access',
|
|
357
|
+
expression: '{{ $json.name }}',
|
|
358
|
+
context: { $json: { name: 'John' } },
|
|
359
|
+
expected: 'John'
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
name: 'Nested with optional chaining',
|
|
363
|
+
expression: '{{ $json.user?.email ?? "default" }}',
|
|
364
|
+
context: { $json: { user: null } },
|
|
365
|
+
expected: 'default'
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
name: 'Array mapping',
|
|
369
|
+
expression: '{{ $json.items.map(i => i.id).join(",") }}',
|
|
370
|
+
context: { $json: { items: [{ id: 1 }, { id: 2 }] } },
|
|
371
|
+
expected: '1,2'
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
name: 'Conditional expression',
|
|
375
|
+
expression: '{{ $json.score >= 70 ? "Pass" : "Fail" }}',
|
|
376
|
+
context: { $json: { score: 85 } },
|
|
377
|
+
expected: 'Pass'
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
name: 'Node reference',
|
|
381
|
+
expression: '{{ $node["Previous"].json.result }}',
|
|
382
|
+
context: { $node: { Previous: { json: { result: 'success' } } } },
|
|
383
|
+
expected: 'success'
|
|
384
|
+
}
|
|
385
|
+
];
|
|
386
|
+
|
|
387
|
+
// Run tests
|
|
388
|
+
for (const test of expressionTests) {
|
|
389
|
+
const result = testExpression(test.expression, test.context);
|
|
390
|
+
console.log(`${test.name}: ${result.result === test.expected ? 'PASS' : 'FAIL'}`);
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## Agent Coordination
|
|
397
|
+
|
|
398
|
+
### Memory Namespace
|
|
399
|
+
```
|
|
400
|
+
aqe/n8n/expressions/
|
|
401
|
+
├── validations/* - Expression validation results
|
|
402
|
+
├── patterns/* - Discovered expression patterns
|
|
403
|
+
├── errors/* - Common error catalog
|
|
404
|
+
└── optimizations/* - Performance suggestions
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Fleet Coordination
|
|
408
|
+
```typescript
|
|
409
|
+
// Coordinate expression validation with workflow testing
|
|
410
|
+
await Task("Validate expressions", {
|
|
411
|
+
workflowId: "wf-123",
|
|
412
|
+
validateAll: true,
|
|
413
|
+
testWithSampleData: true
|
|
414
|
+
}, "n8n-expression-validator");
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## Related Skills
|
|
420
|
+
- [n8n-workflow-testing-fundamentals](../n8n-workflow-testing-fundamentals/) - Workflow testing
|
|
421
|
+
- [n8n-security-testing](../n8n-security-testing/) - Security validation
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## Remember
|
|
426
|
+
|
|
427
|
+
**n8n expressions are JavaScript-like** with special context variables ($json, $node, etc.). Testing requires:
|
|
428
|
+
- Syntax validation
|
|
429
|
+
- Context variable verification
|
|
430
|
+
- Null safety checks
|
|
431
|
+
- Type compatibility
|
|
432
|
+
- Security scanning
|
|
433
|
+
|
|
434
|
+
**Key patterns:** Use optional chaining (`?.`) and nullish coalescing (`??`) for safety. Move complex logic to Code nodes. Always test with edge cases (null, undefined, empty arrays).
|