diff --git a/packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/datatype.completions.test.ts b/packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/datatype.completions.test.ts
new file mode 100644
index 00000000000..0754e079b90
--- /dev/null
+++ b/packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/datatype.completions.test.ts
@@ -0,0 +1,379 @@
+import { describe, test, expect } from 'vitest';
+import { sortCompletionsByInput } from './datatype.completions';
+import type { AliasCompletion } from 'n8n-workflow';
+
+describe('sortCompletionsByInput', () => {
+ describe('prefix match on label (order 3)', () => {
+ test('should assign order 3 when input matches label prefix', () => {
+ const options: AliasCompletion[] = [
+ { label: 'push()', type: 'method' },
+ { label: 'pop()', type: 'method' },
+ ];
+
+ const result = sortCompletionsByInput(options, 'pu');
+
+ expect(result[0]).toEqual({
+ option: options[0],
+ order: 3,
+ alias: undefined,
+ });
+ expect(result[1]).toEqual({
+ option: options[1],
+ order: 0,
+ alias: undefined,
+ });
+ });
+
+ test('should assign order 3 for exact label match', () => {
+ const options: AliasCompletion[] = [{ label: 'push()', type: 'method' }];
+
+ const result = sortCompletionsByInput(options, 'push()');
+
+ expect(result[0]).toEqual({
+ option: options[0],
+ order: 3,
+ alias: undefined,
+ });
+ });
+
+ test('should be case-insensitive for prefix matching', () => {
+ const options: AliasCompletion[] = [{ label: 'toLowerCase()', type: 'method' }];
+
+ const result = sortCompletionsByInput(options, 'tolower');
+
+ expect(result[0].order).toBe(3);
+ });
+ });
+
+ describe('exact alias match (order 2)', () => {
+ test('should assign order 2 when input exactly matches an alias with mode "exact"', () => {
+ const options: AliasCompletion[] = [
+ {
+ label: 'push()',
+ type: 'method',
+ alias: [{ label: 'append', mode: 'exact' }],
+ },
+ ];
+
+ const result = sortCompletionsByInput(options, 'append');
+
+ expect(result[0]).toEqual({
+ option: options[0],
+ order: 2,
+ alias: { label: 'append', mode: 'exact' },
+ });
+ });
+
+ test('should prefer exact alias match over prefix label match', () => {
+ const options: AliasCompletion[] = [
+ {
+ label: 'append()',
+ type: 'method',
+ alias: [{ label: 'push', mode: 'exact' }],
+ },
+ {
+ label: 'push()',
+ type: 'method',
+ },
+ ];
+
+ const result = sortCompletionsByInput(options, 'push');
+
+ // First should be exact alias match (order 2) even though second has prefix match (order 3)
+ // Wait, order 3 is higher than order 2, so prefix match wins
+ expect(result[0].option.label).toBe('push()');
+ expect(result[0].order).toBe(3);
+ expect(result[1].option.label).toBe('append()');
+ expect(result[1].order).toBe(2);
+ });
+
+ test('should not match if alias mode is not "exact"', () => {
+ const options: AliasCompletion[] = [
+ {
+ label: 'push()',
+ type: 'method',
+ alias: [{ label: 'append', mode: 'prefix' }],
+ },
+ ];
+
+ const result = sortCompletionsByInput(options, 'append');
+
+ // Should fall through to prefix alias match, not exact
+ expect(result[0].order).toBe(1);
+ });
+
+ test('should handle multiple aliases and match the exact one', () => {
+ const options: AliasCompletion[] = [
+ {
+ label: 'toUpperCase()',
+ type: 'method',
+ alias: [
+ { label: 'upper', mode: 'prefix' },
+ { label: 'UPPER', mode: 'exact' },
+ ],
+ },
+ ];
+
+ const result = sortCompletionsByInput(options, 'UPPER');
+
+ expect(result[0].order).toBe(2);
+ expect(result[0].alias).toEqual({ label: 'UPPER', mode: 'exact' });
+ });
+ });
+
+ describe('prefix alias match (order 1)', () => {
+ test('should assign order 1 when input matches alias prefix and mode is not "exact"', () => {
+ const options: AliasCompletion[] = [
+ {
+ label: 'push()',
+ type: 'method',
+ alias: [{ label: 'append', mode: 'prefix' }],
+ },
+ ];
+
+ const result = sortCompletionsByInput(options, 'app');
+
+ expect(result[0]).toEqual({
+ option: options[0],
+ order: 1,
+ alias: { label: 'append', mode: 'prefix' },
+ });
+ });
+
+ test('should match when alias has no mode specified (defaults to prefix)', () => {
+ const options: AliasCompletion[] = [
+ {
+ label: 'push()',
+ type: 'method',
+ alias: [{ label: 'append' }],
+ },
+ ];
+
+ const result = sortCompletionsByInput(options, 'app');
+
+ expect(result[0].order).toBe(1);
+ expect(result[0].alias).toEqual({ label: 'append' });
+ });
+
+ test('should match first prefix alias when multiple exist', () => {
+ const options: AliasCompletion[] = [
+ {
+ label: 'push()',
+ type: 'method',
+ alias: [{ label: 'append' }, { label: 'add' }],
+ },
+ ];
+
+ const result = sortCompletionsByInput(options, 'app');
+
+ expect(result[0].order).toBe(1);
+ expect(result[0].alias).toEqual({ label: 'append' });
+ });
+ });
+
+ describe('no match (order 0)', () => {
+ test('should assign order 0 when no matches found', () => {
+ const options: AliasCompletion[] = [
+ { label: 'push()', type: 'method' },
+ { label: 'pop()', type: 'method' },
+ ];
+
+ const result = sortCompletionsByInput(options, 'xyz');
+
+ expect(result[0].order).toBe(0);
+ expect(result[1].order).toBe(0);
+ });
+
+ test('should handle empty alias array', () => {
+ const options: AliasCompletion[] = [
+ {
+ label: 'push()',
+ type: 'method',
+ alias: [],
+ },
+ ];
+
+ const result = sortCompletionsByInput(options, 'app');
+
+ expect(result[0].order).toBe(0);
+ });
+ });
+
+ describe('sorting behavior', () => {
+ test('should sort by order descending (highest first)', () => {
+ const options: AliasCompletion[] = [
+ {
+ label: 'slice()',
+ type: 'method',
+ },
+ {
+ label: 'append()',
+ type: 'method',
+ alias: [{ label: 'push', mode: 'exact' }],
+ },
+ {
+ label: 'push()',
+ type: 'method',
+ alias: [{ label: 'add' }],
+ },
+ {
+ label: 'map()',
+ type: 'method',
+ },
+ ];
+
+ const result = sortCompletionsByInput(options, 'push');
+
+ // push() has prefix match on label (order 3) - should be first
+ // append() has exact alias match (order 2) - should be second
+ // slice() and map() have no match (order 0) - should be last
+ expect(result[0].option.label).toBe('push()'); // order 3
+ expect(result[0].order).toBe(3);
+ expect(result[1].option.label).toBe('append()'); // order 2
+ expect(result[1].order).toBe(2);
+ expect(result[2].option.label).toBe('slice()'); // order 0
+ expect(result[2].order).toBe(0);
+ expect(result[3].option.label).toBe('map()'); // order 0
+ expect(result[3].order).toBe(0);
+ });
+
+ test('should maintain order for items with same priority', () => {
+ const options: AliasCompletion[] = [
+ { label: 'pop()', type: 'method' },
+ { label: 'pow()', type: 'method' },
+ ];
+
+ const result = sortCompletionsByInput(options, 'po');
+
+ expect(result[0].order).toBe(3);
+ expect(result[1].order).toBe(3);
+ // Original order should be preserved for same priority
+ expect(result[0].option.label).toBe('pop()');
+ expect(result[1].option.label).toBe('pow()');
+ });
+ });
+
+ describe('complex scenarios', () => {
+ test('should handle all order types together', () => {
+ const options: AliasCompletion[] = [
+ {
+ label: 'splice()',
+ type: 'method',
+ alias: [{ label: 'remove', mode: 'exact' }],
+ },
+ {
+ label: 'split()',
+ type: 'method',
+ alias: [{ label: 'explode' }],
+ },
+ {
+ label: 'slice()',
+ type: 'method',
+ },
+ {
+ label: 'concat()',
+ type: 'method',
+ },
+ ];
+
+ const result = sortCompletionsByInput(options, 'spl');
+
+ // split() and splice() have prefix match on label (order 3)
+ // Others have no match (order 0)
+ expect(result[0].order).toBe(3);
+ expect(result[1].order).toBe(3);
+ expect(result[2].order).toBe(0);
+ expect(result[3].order).toBe(0);
+ });
+
+ test('should prioritize label over alias', () => {
+ const options: AliasCompletion[] = [
+ {
+ label: 'append()',
+ type: 'method',
+ alias: [{ label: 'push' }],
+ },
+ {
+ label: 'push()',
+ type: 'method',
+ alias: [{ label: 'append' }],
+ },
+ ];
+
+ const result = sortCompletionsByInput(options, 'push');
+
+ // push() has prefix match on label (order 3) - should be first
+ // append() has prefix alias match (order 1) - should be second
+ expect(result[0].option.label).toBe('push()');
+ expect(result[0].order).toBe(3);
+ expect(result[1].option.label).toBe('append()');
+ expect(result[1].order).toBe(1);
+ });
+
+ test('should handle empty input string', () => {
+ const options: AliasCompletion[] = [
+ { label: 'push()', type: 'method' },
+ { label: 'pop()', type: 'method' },
+ ];
+
+ const result = sortCompletionsByInput(options, '');
+
+ // Empty string should match all as prefix
+ expect(result[0].order).toBe(3);
+ expect(result[1].order).toBe(3);
+ });
+
+ test('should handle empty options array', () => {
+ const options: AliasCompletion[] = [];
+
+ const result = sortCompletionsByInput(options, 'test');
+
+ expect(result).toEqual([]);
+ });
+
+ test('should handle options without type property', () => {
+ const options: AliasCompletion[] = [
+ {
+ label: 'push()',
+ alias: [{ label: 'append' }],
+ },
+ ];
+
+ const result = sortCompletionsByInput(options, 'app');
+
+ expect(result[0].order).toBe(1);
+ });
+
+ test('should work with real-world array method aliases', () => {
+ const options: AliasCompletion[] = [
+ {
+ label: 'push()',
+ type: 'method',
+ alias: [{ label: 'append' }, { label: 'add' }],
+ },
+ {
+ label: 'pop()',
+ type: 'method',
+ alias: [{ label: 'remove' }],
+ },
+ {
+ label: 'concat()',
+ type: 'method',
+ alias: [{ label: 'extend' }],
+ },
+ {
+ label: 'includes()',
+ type: 'method',
+ alias: [{ label: 'contains' }],
+ },
+ ];
+
+ const result = sortCompletionsByInput(options, 'app');
+
+ // push() should match via its 'append' alias (order 1)
+ expect(result[0].option.label).toBe('push()');
+ expect(result[0].order).toBe(1);
+ expect(result[0].alias?.label).toBe('append');
+ });
+ });
+});
diff --git a/packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/datatype.completions.ts b/packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/datatype.completions.ts
index a6ef633368a..67452abc55b 100644
--- a/packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/datatype.completions.ts
+++ b/packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/datatype.completions.ts
@@ -2,7 +2,6 @@ import { VALID_EMAIL_REGEX } from '@/app/constants';
import { i18n } from '@n8n/i18n';
import { useEnvironmentsStore } from '@/features/settings/environments.ee/environments.store';
import { useExternalSecretsStore } from '@/features/integrations/externalSecrets.ee/externalSecrets.ee.store';
-
import type {
Completion,
CompletionContext,
@@ -11,8 +10,14 @@ import type {
} from '@codemirror/autocomplete';
import uniqBy from 'lodash/uniqBy';
import { DateTime } from 'luxon';
-import type { DocMetadata, IDataObject, NativeDoc } from 'n8n-workflow';
-import { Expression, ExpressionExtensions, NativeMethods, validateFieldType } from 'n8n-workflow';
+import type { Alias, DocMetadata, IDataObject, NativeDoc } from 'n8n-workflow';
+import {
+ Expression,
+ ExpressionExtensions,
+ NativeMethods,
+ validateFieldType,
+ type AliasCompletion,
+} from 'n8n-workflow';
import {
ARRAY_NUMBER_ONLY_METHODS,
ARRAY_RECOMMENDED_OPTIONS,
@@ -74,7 +79,7 @@ export function datatypeCompletions(context: CompletionContext): CompletionResul
const syntaxTree = javascriptLanguage.parser.parse(word.text);
const [base, tail] = splitBaseTail(syntaxTree, word.text);
- let options: Completion[] = [];
+ let options: AliasCompletion[] = [];
const isCredential = isCredentialsModalOpen();
@@ -116,7 +121,7 @@ export function datatypeCompletions(context: CompletionContext): CompletionResul
}
if (tail !== '') {
- options = options.filter((o) => prefixMatch(o.label, tail));
+ options = filterOptions(options, tail);
}
let from = word.to - tail.length;
@@ -142,10 +147,54 @@ export function datatypeCompletions(context: CompletionContext): CompletionResul
};
}
+/**
+ * Returns a sorted array of completions by the input string.
+ * Each completion is returned with its order.
+ * 0 if no match
+ * 1 if prefix alias match
+ * 2 if exact alias match
+ * 3 if prefix label match
+ */
+export function sortCompletionsByInput(options: AliasCompletion[], input: string) {
+ return options
+ .map((o) => {
+ let order = 0;
+ let alias: Alias | undefined = undefined;
+ if (prefixMatch(o.label, input)) {
+ order = 3;
+ } else {
+ const exactAliasMatch = o.alias?.find((a) => a.mode === 'exact' && a.label === input);
+ const prefixAliasMatch = o.alias?.find(
+ (a) => a.mode !== 'exact' && prefixMatch(a.label, input),
+ );
+ if (exactAliasMatch) {
+ alias = exactAliasMatch;
+ order = 2;
+ } else if (prefixAliasMatch) {
+ alias = prefixAliasMatch;
+ order = 1;
+ }
+ }
+ return {
+ option: o,
+ order,
+ alias,
+ };
+ })
+ .sort((a, b) => b.order - a.order);
+}
+
+function filterOptions(options: AliasCompletion[], tail: string): AliasCompletion[] {
+ const matches = sortCompletionsByInput(options, tail)
+ .filter((m) => m.order > 0)
+ .map((m) => m.option);
+ return matches;
+}
+
function explicitDataTypeOptions(
expression: string,
targetNodeParameterContext?: TargetNodeParameterContext,
-): Completion[] {
+): AliasCompletion[] {
return attempt(
() => {
const resolved = resolveAutocompleteExpression(
@@ -163,7 +212,7 @@ function explicitDataTypeOptions(
);
}
-function datatypeOptions(input: AutocompleteInput): Completion[] {
+function datatypeOptions(input: AutocompleteInput): AliasCompletion[] {
const { resolved } = input;
if (resolved === null) return [];
@@ -211,7 +260,7 @@ export const natives = ({
}: {
typeName: ExtensionTypeName;
transformLabel?: (label: string) => string;
-}): Completion[] => {
+}): AliasCompletion[] => {
const nativeDocs = NativeMethods.find((ee) => ee.typeName.toLowerCase() === typeName);
if (!nativeDocs) return [];
@@ -324,9 +373,9 @@ const createCompletionOption = ({
isFunction?: boolean;
transformLabel?: (label: string) => string;
type?: 'strikethrough';
-}): Completion => {
+}): AliasCompletion => {
const label = isFunction ? name + '()' : name;
- const option: Completion = {
+ const option: AliasCompletion = {
label,
section: doc?.section,
apply: applyCompletion({
@@ -334,6 +383,7 @@ const createCompletionOption = ({
defaultArgs: getDefaultArgs(doc),
transformLabel,
}),
+ alias: doc?.aliases?.map((alias) => ({ label: alias, mode: doc.aliasMode })),
type,
};
option.info = createInfoBoxRenderer(doc, isFunction);
diff --git a/packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/types.ts b/packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/types.ts
index 031eb12d6e8..0ab454f9e29 100644
--- a/packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/types.ts
+++ b/packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/completions/types.ts
@@ -4,7 +4,7 @@ export type Resolved = unknown;
export type ExtensionTypeName = 'number' | 'string' | 'date' | 'array' | 'object' | 'boolean';
-export type FnToDoc = { [fnName: string]: { doc?: DocMetadata } };
+export type FnToDoc = { [fnName: string]: { doc?: DocMetadata; aliases?: string[] } };
export type FunctionOptionType = 'native-function' | 'extension-function';
export type KeywordOptionType = 'keyword';
diff --git a/packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/typescript/client/completions.ts b/packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/typescript/client/completions.ts
index ba591996cf7..071fdc36bbf 100644
--- a/packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/typescript/client/completions.ts
+++ b/packages/frontend/editor-ui/src/features/shared/editors/plugins/codemirror/typescript/client/completions.ts
@@ -6,14 +6,12 @@ import {
type Completion,
type CompletionSource,
} from '@codemirror/autocomplete';
-import {
- autocompletableNodeNames,
- longestCommonPrefix,
- prefixMatch,
-} from '../../completions/utils';
+import { autocompletableNodeNames, longestCommonPrefix } from '../../completions/utils';
import { typescriptWorkerFacet } from './facet';
import { blockCommentSnippet, snippets } from './snippets';
import { TARGET_NODE_PARAMETER_FACET } from '../../completions/constants';
+import type { AliasCompletion, Alias } from 'n8n-workflow';
+import { sortCompletionsByInput } from '../../completions/datatype.completions';
const START_CHARACTERS = ['"', "'", '(', '.', '@'];
const START_CHARACTERS_REGEX = /[\.\(\'\"\@]/;
@@ -48,7 +46,49 @@ export const typescriptCompletionSource: CompletionSource = async (context) => {
const { result, isGlobal } = completionResult;
- let options = [...result.options];
+ let options: AliasCompletion[] = [...result.options];
+
+ const addAlias = (label: string, alias: Alias[]) => {
+ const completionIndex = options.findIndex((opt) => opt.label === label);
+ if (completionIndex !== -1) {
+ options[completionIndex].alias = alias;
+ }
+ };
+
+ // array aliases
+ addAlias('push()', [
+ {
+ label: 'append()',
+ info: 'Use push() to add one or more elements to the end of an array',
+ },
+ ]);
+ addAlias('pop()', [
+ { label: 'remove()', info: 'Use pop() to remove the last element from an array' },
+ ]);
+ addAlias('splice()', [
+ {
+ label: 'insertAt()',
+ info: 'Use splice(index, 0, value) to insert elements at a specified index in an array',
+ },
+ {
+ label: 'removeAt()',
+ info: 'Use splice(indexOf(itemToRemove), 1) to remove elements from an array',
+ },
+ ]);
+ addAlias('concat()', [{ label: 'extend()', info: 'Use concat() to merge two or more arrays' }]);
+ addAlias('includes()', [
+ { label: 'contains()', info: 'Use includes() to check if an array contains a value' },
+ { label: 'has()', info: 'Use includes() to check if an array contains a value' },
+ ]);
+
+ // string aliases
+ addAlias('toLowerCase()', [{ label: 'lower()' }]);
+ addAlias('toUpperCase()', [{ label: 'upper()' }]);
+
+ addAlias('length', [
+ { label: 'size', mode: 'exact' },
+ { label: 'count', mode: 'exact' },
+ ]);
if (isGlobal) {
options = options
@@ -67,6 +107,22 @@ export const typescriptCompletionSource: CompletionSource = async (context) => {
.concat(snippets);
}
+ const filteredOptions: AliasCompletion[] = [];
+ for (const { option, order, alias } of sortCompletionsByInput(options, word.text)) {
+ const isEmpty = word.text === '';
+ const isStartCharacter = START_CHARACTERS.includes(word.text);
+ const isMatch = order > 0;
+
+ // replace the default description with the alias description
+ if (alias?.info) {
+ option.info = alias.info;
+ }
+
+ if (isEmpty || isStartCharacter || isMatch) {
+ filteredOptions.push(option);
+ }
+ }
+
return {
from: word ? (START_CHARACTERS.includes(word.text) ? word.to : word.from) : context.pos,
filter: false,
@@ -74,28 +130,18 @@ export const typescriptCompletionSource: CompletionSource = async (context) => {
const lcp = longestCommonPrefix(completion.label, word.text);
return [0, lcp.length];
},
- options: options
- .filter(
- (option) =>
- word.text === '' ||
- START_CHARACTERS.includes(word.text) ||
- prefixMatch(
- option.label.replace(START_CHARACTERS_REGEX, ''),
- word.text.replace(START_CHARACTERS_REGEX, ''),
- ),
- )
- .map((completion) => {
- if (completion.label.endsWith('()')) {
- completion.apply = (view, _, from, to) => {
- const cursorPosition = from + completion.label.length - 1;
- view.dispatch({
- ...insertCompletionText(view.state, completion.label, from, to),
- annotations: pickedCompletion.of(completion),
- selection: { anchor: cursorPosition, head: cursorPosition },
- });
- };
- }
- return completion;
- }),
+ options: filteredOptions.map((completion) => {
+ if (completion.label.endsWith('()')) {
+ completion.apply = (view, _, from, to) => {
+ const cursorPosition = from + completion.label.length - 1;
+ view.dispatch({
+ ...insertCompletionText(view.state, completion.label, from, to),
+ annotations: pickedCompletion.of(completion),
+ selection: { anchor: cursorPosition, head: cursorPosition },
+ });
+ };
+ }
+ return completion;
+ }),
};
};
diff --git a/packages/workflow/src/extensions/array-extensions.ts b/packages/workflow/src/extensions/array-extensions.ts
index 079743d977a..701d3ad5a58 100644
--- a/packages/workflow/src/extensions/array-extensions.ts
+++ b/packages/workflow/src/extensions/array-extensions.ts
@@ -334,6 +334,7 @@ export function toDateTime() {
average.doc = {
name: 'average',
+ aliases: ['mean'],
description:
'Returns the average of the numbers in the array. Throws an error if there are any non-numbers.',
examples: [{ example: '[12, 1, 5].average()', evaluated: '6' }],
@@ -343,6 +344,7 @@ average.doc = {
compact.doc = {
name: 'compact',
+ aliases: ['removeEmpty'],
description:
'Removes any empty values from the array. null, "" and undefined count as empty.',
examples: [{ example: '[2, null, 1, ""].compact()', evaluated: '[2, 1]' }],
@@ -374,6 +376,7 @@ isNotEmpty.doc = {
first.doc = {
name: 'first',
+ aliases: ['head'],
description: 'Returns the first element of the array',
examples: [{ example: "['quick', 'brown', 'fox'].first()", evaluated: "'quick'" }],
returnType: 'any',
@@ -382,6 +385,7 @@ first.doc = {
last.doc = {
name: 'last',
+ aliases: ['tail'],
description: 'Returns the last element of the array',
examples: [{ example: "['quick', 'brown', 'fox'].last()", evaluated: "'fox'" }],
returnType: 'any',
@@ -640,6 +644,7 @@ toJsonString.doc = {
append.doc = {
name: 'append',
+ aliases: ['push'],
description:
'Adds new elements to the end of the array. Similar to push(), but returns the modified array. Consider using spread syntax instead (see examples).',
examples: [
diff --git a/packages/workflow/src/extensions/extensions.ts b/packages/workflow/src/extensions/extensions.ts
index 2a5ef9d33af..c1b55f063ae 100644
--- a/packages/workflow/src/extensions/extensions.ts
+++ b/packages/workflow/src/extensions/extensions.ts
@@ -1,3 +1,10 @@
+import type { Completion } from '@codemirror/autocomplete';
+
+export type Alias = { label: string; info?: string; mode?: 'prefix' | 'exact' };
+export interface AliasCompletion extends Completion {
+ alias?: Alias[];
+}
+
export interface ExtensionMap {
typeName: string;
functions: Record;
@@ -35,6 +42,7 @@ export type DocMetadata = {
section?: string;
hidden?: boolean;
aliases?: string[];
+ aliasMode?: 'prefix' | 'exact';
args?: DocMetadataArgument[];
examples?: DocMetadataExample[];
docURL?: string;
diff --git a/packages/workflow/src/extensions/index.ts b/packages/workflow/src/extensions/index.ts
index 3824acacf85..1445fb2af47 100644
--- a/packages/workflow/src/extensions/index.ts
+++ b/packages/workflow/src/extensions/index.ts
@@ -14,3 +14,8 @@ export type {
DocMetadataArgument,
DocMetadataExample,
} from './extensions';
+
+export type {
+ Alias,
+ AliasCompletion,
+} from './extensions';
diff --git a/packages/workflow/src/extensions/object-extensions.ts b/packages/workflow/src/extensions/object-extensions.ts
index 3dacff99f1b..954aa9232fa 100644
--- a/packages/workflow/src/extensions/object-extensions.ts
+++ b/packages/workflow/src/extensions/object-extensions.ts
@@ -179,6 +179,7 @@ hasField.doc = {
removeField.doc = {
name: 'removeField',
+ aliases: ['delete'],
description: "Removes a field from the Object. The same as JavaScript's delete.",
examples: [
{
diff --git a/packages/workflow/src/extensions/string-extensions.ts b/packages/workflow/src/extensions/string-extensions.ts
index 1bb15bd380c..974edc9862b 100644
--- a/packages/workflow/src/extensions/string-extensions.ts
+++ b/packages/workflow/src/extensions/string-extensions.ts
@@ -821,6 +821,7 @@ quote.doc = {
parseJson.doc = {
name: 'parseJson',
+ aliases: ['fromJson'],
description:
"Returns the JavaScript value or object represented by the string, or undefined if the string isn't valid JSON. Single-quoted JSON is not supported.",
section: 'cast',
@@ -836,6 +837,7 @@ parseJson.doc = {
base64Encode.doc = {
name: 'base64Encode',
+ aliases: ['toBase64'],
description: 'Converts plain text to a base64-encoded string',
examples: [{ example: '"hello".base64Encode()', evaluated: '"aGVsbG8="' }],
section: 'edit',
@@ -846,6 +848,7 @@ base64Encode.doc = {
base64Decode.doc = {
name: 'base64Decode',
+ aliases: ['fromBase64'],
description: 'Converts a base64-encoded string to plain text',
examples: [{ example: '"aGVsbG8=".base64Decode()', evaluated: '"hello"' }],
section: 'edit',
diff --git a/packages/workflow/src/index.ts b/packages/workflow/src/index.ts
index f5d6d204514..95a41dc954a 100644
--- a/packages/workflow/src/index.ts
+++ b/packages/workflow/src/index.ts
@@ -75,7 +75,7 @@ export {
type ExtractableSubgraphData,
type IConnectionAdjacencyList as AdjacencyList,
} from './graph/graph-utils';
-export { ExpressionExtensions } from './extensions';
+export { ExpressionExtensions, type Alias, type AliasCompletion } from './extensions';
export * as ExpressionParser from './extensions/expression-parser';
export { NativeMethods } from './native-methods';
export * from './node-parameters/filter-parameter';
diff --git a/packages/workflow/src/native-methods/array.methods.ts b/packages/workflow/src/native-methods/array.methods.ts
index b21cd5d9b4c..d6f13495754 100644
--- a/packages/workflow/src/native-methods/array.methods.ts
+++ b/packages/workflow/src/native-methods/array.methods.ts
@@ -6,6 +6,8 @@ export const arrayMethods: NativeDoc = {
length: {
doc: {
name: 'length',
+ aliases: ['size', 'count'],
+ aliasMode: 'exact',
description: 'The number of elements in the array',
examples: [{ example: "['Bob', 'Bill', 'Nat'].length", evaluated: '3' }],
docURL:
@@ -18,6 +20,7 @@ export const arrayMethods: NativeDoc = {
concat: {
doc: {
name: 'concat',
+ aliases: ['extend'],
description: 'Joins one or more arrays onto the end of the base array',
examples: [
{
@@ -239,6 +242,7 @@ export const arrayMethods: NativeDoc = {
includes: {
doc: {
name: 'includes',
+ aliases: ['contains', 'has'],
description: 'Returns true if the array contains the specified element',
examples: [
{ example: "['Bob', 'Bill', 'Nat'].includes('Nat')", evaluated: 'true' },
@@ -418,6 +422,7 @@ export const arrayMethods: NativeDoc = {
description:
'Returns a portion of the array, from the start index up to (but not including) the end index. Indexes start at 0.',
examples: [
+ { example: '[1, 2, 3, 4, 5].slice(0, -1)', evaluated: '[1, 2, 3, 4]' },
{ example: '[1, 2, 3, 4, 5].slice(2, 4)', evaluated: '[3, 4]' },
{ example: '[1, 2, 3, 4, 5].slice(2)', evaluated: '[3, 4, 5]' },
{ example: '[1, 2, 3, 4, 5].slice(-2)', evaluated: '[4, 5]' },
@@ -536,6 +541,7 @@ export const arrayMethods: NativeDoc = {
toSpliced: {
doc: {
name: 'toSpliced',
+ aliases: ['insertAt', 'removeAt'],
description:
'Adds and/or removes array elements at a given position. \n\nSee also slice() and append().',
examples: [
diff --git a/packages/workflow/src/native-methods/string.methods.ts b/packages/workflow/src/native-methods/string.methods.ts
index c49ffdd7569..cebe6217bc6 100644
--- a/packages/workflow/src/native-methods/string.methods.ts
+++ b/packages/workflow/src/native-methods/string.methods.ts
@@ -6,6 +6,8 @@ export const stringMethods: NativeDoc = {
length: {
doc: {
name: 'length',
+ aliases: ['size', 'count'],
+ aliasMode: 'exact',
description: 'The number of characters in the string',
examples: [{ example: '"hello".length', evaluated: '5' }],
section: 'query',
@@ -190,6 +192,7 @@ export const stringMethods: NativeDoc = {
includes: {
doc: {
name: 'includes',
+ aliases: ['contains'],
description:
'Returns true if the string contains the searchString. Case-sensitive.',
section: 'query',
@@ -483,6 +486,7 @@ export const stringMethods: NativeDoc = {
toLowerCase: {
doc: {
name: 'toLowerCase',
+ aliases: ['lower'],
description: 'Converts all letters in the string to lower case',
section: 'case',
docURL:
@@ -494,6 +498,7 @@ export const stringMethods: NativeDoc = {
toUpperCase: {
doc: {
name: 'toUpperCase',
+ aliases: ['upper'],
description: 'Converts all letters in the string to upper case (capitals)',
examples: [{ example: '"I\'m not angry".toUpperCase()', evaluated: '"I\'M NOT ANGRY"' }],
section: 'case',
@@ -517,6 +522,7 @@ export const stringMethods: NativeDoc = {
trimEnd: {
doc: {
name: 'trimEnd',
+ aliases: ['trimRight'],
description:
'Removes whitespace from the end of a string and returns a new string. Whitespace includes new lines, tabs, spaces, etc.',
examples: [{ example: "' lonely '.trimEnd()", evaluated: "' lonely'" }],
@@ -529,6 +535,7 @@ export const stringMethods: NativeDoc = {
trimStart: {
doc: {
name: 'trimStart',
+ aliases: ['trimLeft'],
description:
'Removes whitespace from the beginning of a string and returns a new string. Whitespace includes new lines, tabs, spaces, etc.',
examples: [{ example: "' lonely '.trimStart()", evaluated: "'lonely '" }],