0
# Extension System
1
2
Comprehensive extension system providing data type extensions, native method access, expression parsing capabilities, and enhanced functionality for workflow expressions and data manipulation.
3
4
## Capabilities
5
6
### Expression Extensions Registry
7
8
Core extension system for registering and managing expression functions.
9
10
```typescript { .api }
11
/**
12
* Expression extensions registry and management
13
*/
14
class ExpressionExtensions {
15
/**
16
* Registry of all available extension functions
17
*/
18
static readonly functions: Record<string, Extension>;
19
20
/**
21
* Add custom extension function to registry
22
* @param name - Extension function name
23
* @param extension - Extension implementation
24
*/
25
static addExtension(name: string, extension: Extension): void;
26
27
/**
28
* Get extension function by name
29
* @param name - Extension name
30
* @returns Extension implementation or undefined
31
*/
32
static getExtension(name: string): Extension | undefined;
33
34
/**
35
* Remove extension from registry
36
* @param name - Extension name to remove
37
* @returns Boolean indicating if extension was removed
38
*/
39
static removeExtension(name: string): boolean;
40
41
/**
42
* List all available extension names
43
* @returns Array of extension names
44
*/
45
static listExtensions(): string[];
46
}
47
48
/**
49
* Extension function interface
50
*/
51
interface Extension {
52
doc: DocMetadata;
53
transform: (value: any, ...args: any[]) => any;
54
}
55
56
/**
57
* Extension documentation metadata
58
*/
59
interface DocMetadata {
60
name: string;
61
description: string;
62
returnType?: string;
63
args?: DocMetadataArgument[];
64
examples?: DocMetadataExample[];
65
section?: string;
66
hidden?: boolean;
67
}
68
69
interface DocMetadataArgument {
70
name: string;
71
type?: string;
72
description?: string;
73
default?: any;
74
optional?: boolean;
75
}
76
77
interface DocMetadataExample {
78
example: string;
79
description: string;
80
result?: any;
81
}
82
```
83
84
### Native Methods Access
85
86
Secure access to native JavaScript and Node.js methods within expressions.
87
88
```typescript { .api }
89
/**
90
* Native methods registry for expression evaluation
91
*/
92
class NativeMethods {
93
/**
94
* Get native method implementation
95
* @param methodName - Native method name
96
* @returns Native method function or undefined
97
*/
98
static getNativeMethod(methodName: string): Function | undefined;
99
100
/**
101
* Register native method for expression use
102
* @param name - Method name
103
* @param method - Method implementation
104
* @param options - Registration options
105
*/
106
static registerNativeMethod(
107
name: string,
108
method: Function,
109
options?: INativeMethodOptions
110
): void;
111
112
/**
113
* List all available native methods
114
* @returns Array of native method names
115
*/
116
static listNativeMethods(): string[];
117
}
118
119
interface INativeMethodOptions {
120
allowedInSandbox?: boolean;
121
documentation?: DocMetadata;
122
category?: string;
123
}
124
```
125
126
### Data Type Extensions
127
128
Built-in extensions for different data types with comprehensive functionality.
129
130
```typescript { .api }
131
/**
132
* Array data type extensions
133
*/
134
interface ArrayExtensions {
135
/**
136
* Split array into chunks of specified size
137
* @param size - Chunk size
138
* @returns Array of chunks
139
*/
140
chunk(size: number): any[][];
141
142
/**
143
* Remove falsy values from array
144
* @returns Array with truthy values only
145
*/
146
compact(): any[];
147
148
/**
149
* Get difference between arrays
150
* @param values - Values to exclude
151
* @returns Array with differences
152
*/
153
difference(values: any[]): any[];
154
155
/**
156
* Get first element
157
* @returns First array element
158
*/
159
first(): any;
160
161
/**
162
* Get last element
163
* @returns Last array element
164
*/
165
last(): any;
166
167
/**
168
* Remove duplicate values
169
* @returns Array with unique values
170
*/
171
unique(): any[];
172
173
/**
174
* Flatten nested arrays
175
* @param depth - Maximum depth to flatten
176
* @returns Flattened array
177
*/
178
flatten(depth?: number): any[];
179
180
/**
181
* Check if array is empty
182
* @returns Boolean indicating if array is empty
183
*/
184
isEmpty(): boolean;
185
186
/**
187
* Get array length
188
* @returns Array length
189
*/
190
length(): number;
191
192
/**
193
* Sum all numeric values
194
* @returns Sum of array values
195
*/
196
sum(): number;
197
198
/**
199
* Get average of numeric values
200
* @returns Average value
201
*/
202
average(): number;
203
204
/**
205
* Get minimum value
206
* @returns Minimum value
207
*/
208
min(): any;
209
210
/**
211
* Get maximum value
212
* @returns Maximum value
213
*/
214
max(): any;
215
}
216
217
/**
218
* String data type extensions
219
*/
220
interface StringExtensions {
221
/**
222
* Check if string contains substring
223
* @param searchString - String to search for
224
* @returns Boolean indicating if string contains substring
225
*/
226
contains(searchString: string): boolean;
227
228
/**
229
* Check if string starts with substring
230
* @param searchString - String to check
231
* @returns Boolean indicating if string starts with substring
232
*/
233
startsWith(searchString: string): boolean;
234
235
/**
236
* Check if string ends with substring
237
* @param searchString - String to check
238
* @returns Boolean indicating if string ends with substring
239
*/
240
endsWith(searchString: string): boolean;
241
242
/**
243
* Convert to title case
244
* @returns Title case string
245
*/
246
toTitleCase(): string;
247
248
/**
249
* Convert to sentence case
250
* @returns Sentence case string
251
*/
252
toSentenceCase(): string;
253
254
/**
255
* Remove HTML tags
256
* @returns String without HTML tags
257
*/
258
stripTags(): string;
259
260
/**
261
* URL encode string
262
* @returns URL encoded string
263
*/
264
urlEncode(): string;
265
266
/**
267
* URL decode string
268
* @returns URL decoded string
269
*/
270
urlDecode(): string;
271
272
/**
273
* Generate hash of string
274
* @param algorithm - Hash algorithm (default: 'md5')
275
* @returns Hash string
276
*/
277
hash(algorithm?: string): string;
278
279
/**
280
* Extract email addresses from string
281
* @returns Array of email addresses
282
*/
283
extractEmails(): string[];
284
285
/**
286
* Extract URLs from string
287
* @returns Array of URLs
288
*/
289
extractUrls(): string[];
290
291
/**
292
* Truncate string to specified length
293
* @param length - Maximum length
294
* @param suffix - Suffix to add (default: '...')
295
* @returns Truncated string
296
*/
297
truncate(length: number, suffix?: string): string;
298
}
299
300
/**
301
* Number data type extensions
302
*/
303
interface NumberExtensions {
304
/**
305
* Get absolute value
306
* @returns Absolute value
307
*/
308
abs(): number;
309
310
/**
311
* Round up to nearest integer
312
* @returns Ceiling value
313
*/
314
ceil(): number;
315
316
/**
317
* Round down to nearest integer
318
* @returns Floor value
319
*/
320
floor(): number;
321
322
/**
323
* Round to specified precision
324
* @param precision - Number of decimal places
325
* @returns Rounded number
326
*/
327
round(precision?: number): number;
328
329
/**
330
* Check if value is NaN
331
* @returns Boolean indicating if value is NaN
332
*/
333
isNaN(): boolean;
334
335
/**
336
* Check if value is finite
337
* @returns Boolean indicating if value is finite
338
*/
339
isFinite(): boolean;
340
341
/**
342
* Format number with locale options
343
* @param options - Formatting options
344
* @returns Formatted number string
345
*/
346
format(options?: NumberFormatOptions): string;
347
348
/**
349
* Convert to percentage
350
* @param decimals - Number of decimal places
351
* @returns Percentage string
352
*/
353
toPercent(decimals?: number): string;
354
355
/**
356
* Convert to currency format
357
* @param currency - Currency code
358
* @param locale - Locale code
359
* @returns Currency formatted string
360
*/
361
toCurrency(currency?: string, locale?: string): string;
362
}
363
364
interface NumberFormatOptions {
365
locale?: string;
366
style?: 'decimal' | 'currency' | 'percent';
367
currency?: string;
368
minimumFractionDigits?: number;
369
maximumFractionDigits?: number;
370
}
371
```
372
373
### Date Extensions
374
375
Comprehensive date and time manipulation extensions.
376
377
```typescript { .api }
378
/**
379
* Date data type extensions
380
*/
381
interface DateExtensions {
382
/**
383
* Format date using specified format string
384
* @param format - Format string (e.g., 'yyyy-MM-dd')
385
* @returns Formatted date string
386
*/
387
format(format: string): string;
388
389
/**
390
* Convert to ISO string
391
* @returns ISO formatted date string
392
*/
393
toISOString(): string;
394
395
/**
396
* Get beginning of time unit
397
* @param unit - Time unit ('day', 'month', 'year', etc.)
398
* @returns Date at beginning of unit
399
*/
400
beginningOf(unit: DateUnit): Date;
401
402
/**
403
* Get end of time unit
404
* @param unit - Time unit
405
* @returns Date at end of unit
406
*/
407
endOf(unit: DateUnit): Date;
408
409
/**
410
* Add duration to date
411
* @param duration - Duration object or number
412
* @param unit - Time unit (if duration is number)
413
* @returns New date with added duration
414
*/
415
plus(duration: Duration | number, unit?: DateUnit): Date;
416
417
/**
418
* Subtract duration from date
419
* @param duration - Duration object or number
420
* @param unit - Time unit (if duration is number)
421
* @returns New date with subtracted duration
422
*/
423
minus(duration: Duration | number, unit?: DateUnit): Date;
424
425
/**
426
* Get difference between dates
427
* @param date - Date to compare with
428
* @param unit - Unit for difference calculation
429
* @returns Difference in specified unit
430
*/
431
diff(date: Date, unit?: DateUnit): number;
432
433
/**
434
* Check if date is before another date
435
* @param date - Date to compare with
436
* @returns Boolean indicating if date is before
437
*/
438
isBefore(date: Date): boolean;
439
440
/**
441
* Check if date is after another date
442
* @param date - Date to compare with
443
* @returns Boolean indicating if date is after
444
*/
445
isAfter(date: Date): boolean;
446
447
/**
448
* Check if date is between two dates
449
* @param start - Start date
450
* @param end - End date
451
* @returns Boolean indicating if date is between
452
*/
453
isBetween(start: Date, end: Date): boolean;
454
}
455
456
type DateUnit = 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year';
457
458
interface Duration {
459
years?: number;
460
months?: number;
461
weeks?: number;
462
days?: number;
463
hours?: number;
464
minutes?: number;
465
seconds?: number;
466
milliseconds?: number;
467
}
468
```
469
470
### Object Extensions
471
472
Object manipulation and transformation extensions.
473
474
```typescript { .api }
475
/**
476
* Object data type extensions
477
*/
478
interface ObjectExtensions {
479
/**
480
* Get object keys
481
* @returns Array of object keys
482
*/
483
keys(): string[];
484
485
/**
486
* Get object values
487
* @returns Array of object values
488
*/
489
values(): any[];
490
491
/**
492
* Get object entries as key-value pairs
493
* @returns Array of [key, value] pairs
494
*/
495
entries(): Array<[string, any]>;
496
497
/**
498
* Check if object has property
499
* @param key - Property key to check
500
* @returns Boolean indicating if property exists
501
*/
502
hasKey(key: string): boolean;
503
504
/**
505
* Pick specified properties
506
* @param keys - Keys to pick
507
* @returns Object with only specified keys
508
*/
509
pick(keys: string[]): object;
510
511
/**
512
* Omit specified properties
513
* @param keys - Keys to omit
514
* @returns Object without specified keys
515
*/
516
omit(keys: string[]): object;
517
518
/**
519
* Merge with another object
520
* @param other - Object to merge with
521
* @returns Merged object
522
*/
523
merge(other: object): object;
524
525
/**
526
* Convert keys to camelCase
527
* @returns Object with camelCase keys
528
*/
529
camelCaseKeys(): object;
530
531
/**
532
* Convert keys to snake_case
533
* @returns Object with snake_case keys
534
*/
535
snakeCaseKeys(): object;
536
537
/**
538
* Flatten nested object
539
* @param separator - Key separator (default: '.')
540
* @returns Flattened object
541
*/
542
flatten(separator?: string): object;
543
544
/**
545
* Unflatten dot notation object
546
* @returns Nested object structure
547
*/
548
unflatten(): object;
549
}
550
```
551
552
### Expression Parser
553
554
Advanced expression parsing and validation capabilities.
555
556
```typescript { .api }
557
/**
558
* Expression parser module
559
*/
560
namespace ExpressionParser {
561
/**
562
* Parse expression string into AST
563
* @param expression - Expression string to parse
564
* @returns Parsed expression AST
565
*/
566
function parse(expression: string): ExpressionAST;
567
568
/**
569
* Validate expression syntax
570
* @param expression - Expression to validate
571
* @returns Validation result
572
*/
573
function validate(expression: string): ExpressionValidationResult;
574
575
/**
576
* Extract all variables from expression
577
* @param expression - Expression string
578
* @returns Array of variable names
579
*/
580
function extractVariables(expression: string): string[];
581
582
/**
583
* Get expression dependencies
584
* @param expression - Expression string
585
* @returns Dependency information
586
*/
587
function getDependencies(expression: string): ExpressionDependencies;
588
589
/**
590
* Transform expression AST
591
* @param ast - Expression AST
592
* @param transformer - Transformation function
593
* @returns Transformed AST
594
*/
595
function transform(
596
ast: ExpressionAST,
597
transformer: (node: ASTNode) => ASTNode
598
): ExpressionAST;
599
}
600
601
interface ExpressionAST {
602
type: string;
603
body: ASTNode[];
604
sourceType: 'script' | 'module';
605
}
606
607
interface ASTNode {
608
type: string;
609
[key: string]: any;
610
}
611
612
interface ExpressionValidationResult {
613
isValid: boolean;
614
errors: ExpressionSyntaxError[];
615
warnings: ExpressionWarning[];
616
}
617
618
interface ExpressionDependencies {
619
variables: string[];
620
functions: string[];
621
dataRefs: string[];
622
nodeRefs: string[];
623
}
624
```
625
626
**Usage Examples:**
627
628
```typescript
629
import {
630
ExpressionExtensions,
631
NativeMethods,
632
ExpressionParser
633
} from "n8n-workflow";
634
635
// Register custom extension
636
ExpressionExtensions.addExtension('customTransform', {
637
doc: {
638
name: 'customTransform',
639
description: 'Transform data using custom logic',
640
returnType: 'any',
641
args: [
642
{ name: 'data', type: 'any', description: 'Data to transform' },
643
{ name: 'options', type: 'object', optional: true, description: 'Transform options' }
644
],
645
examples: [
646
{
647
example: 'data.customTransform({ format: "uppercase" })',
648
description: 'Transform data to uppercase format'
649
}
650
]
651
},
652
transform: (value: any, options: any = {}) => {
653
if (options.format === 'uppercase' && typeof value === 'string') {
654
return value.toUpperCase();
655
}
656
return value;
657
}
658
});
659
660
// Use data type extensions in expressions
661
const arrayExpression = `
662
{{
663
$json.items
664
.chunk(3)
665
.map(chunk => chunk.sum())
666
.unique()
667
.sort()
668
}}
669
`;
670
671
const stringExpression = `
672
{{
673
$json.description
674
.stripTags()
675
.truncate(100)
676
.toTitleCase()
677
}}
678
`;
679
680
const dateExpression = `
681
{{
682
$json.createdAt
683
.beginningOf('day')
684
.plus(1, 'week')
685
.format('yyyy-MM-dd')
686
}}
687
`;
688
689
// Register native method
690
NativeMethods.registerNativeMethod('customMath', Math.pow, {
691
allowedInSandbox: true,
692
documentation: {
693
name: 'customMath',
694
description: 'Calculate power of a number',
695
args: [
696
{ name: 'base', type: 'number', description: 'Base number' },
697
{ name: 'exponent', type: 'number', description: 'Exponent' }
698
]
699
},
700
category: 'math'
701
});
702
703
// Parse and analyze expressions
704
const expressionCode = '{{ $json.user.name.toTitleCase() + " - " + $json.createdAt.format("yyyy-MM-dd") }}';
705
706
const ast = ExpressionParser.parse(expressionCode);
707
console.log('Parsed AST:', ast);
708
709
const validation = ExpressionParser.validate(expressionCode);
710
if (!validation.isValid) {
711
console.log('Expression validation errors:', validation.errors);
712
}
713
714
const variables = ExpressionParser.extractVariables(expressionCode);
715
console.log('Variables used:', variables); // ['$json']
716
717
const dependencies = ExpressionParser.getDependencies(expressionCode);
718
console.log('Expression dependencies:', dependencies);
719
720
// List available extensions
721
const availableExtensions = ExpressionExtensions.listExtensions();
722
console.log('Available extensions:', availableExtensions);
723
724
// Get extension documentation
725
const stringExtension = ExpressionExtensions.getExtension('toTitleCase');
726
if (stringExtension) {
727
console.log('Extension documentation:', stringExtension.doc);
728
}
729
730
// Complex data manipulation using extensions
731
const complexExpression = `
732
{{
733
$json.users
734
.filter(user => user.active && user.email.contains('@company.com'))
735
.map(user => ({
736
id: user.id,
737
name: user.name.toTitleCase(),
738
email: user.email.toLowerCase(),
739
joinDate: user.joinDate.format('MMM dd, yyyy'),
740
tenure: user.joinDate.diff($now, 'months')
741
}))
742
.sort((a, b) => a.tenure - b.tenure)
743
.chunk(10)
744
.first()
745
}}
746
`;
747
748
// Object transformation example
749
const objectTransformExpression = `
750
{{
751
$json.apiResponse
752
.pick(['id', 'name', 'email', 'metadata'])
753
.camelCaseKeys()
754
.merge({
755
processedAt: $now.toISOString(),
756
status: 'processed'
757
})
758
}}
759
`;
760
761
console.log('Complex expressions ready for evaluation');
762
```