n8n/packages/workflow/src/interfaces.ts

3314 lines
92 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* eslint-disable @typescript-eslint/no-explicit-any */
import type { CallbackManager as CallbackManagerLC } from '@langchain/core/callbacks/manager';
import type { LogScope } from '@n8n/config';
import type { AxiosProxyConfig, GenericAbortSignal } from 'axios';
import type * as express from 'express';
import type FormData from 'form-data';
import type { PathLike } from 'fs';
import type { IncomingHttpHeaders } from 'http';
import type { ReplyHeaders, RequestBodyMatcher, RequestHeaderMatcher } from 'nock';
import type { Client as SSHClient } from 'ssh2';
import type { Readable } from 'stream';
import type { SecureContextOptions } from 'tls';
import type { URLSearchParams } from 'url';
import type {
CODE_EXECUTION_MODES,
CODE_LANGUAGES,
LOG_LEVELS,
BINARY_MODE_COMBINED,
BINARY_MODE_SEPARATE,
} from './constants';
import type {
IDataTableProjectAggregateService,
IDataTableProjectService,
} from './data-table.types';
import type { IDeferredPromise } from './deferred-promise';
import type { ExecutionCancelledError } from './errors';
import type { ExpressionError } from './errors/expression.error';
import type { NodeApiError } from './errors/node-api.error';
import type { NodeOperationError } from './errors/node-operation.error';
import type { WorkflowActivationError } from './errors/workflow-activation.error';
import type { WorkflowOperationError } from './errors/workflow-operation.error';
import type {
IExecutionContext,
WorkflowExecuteModeValues as WorkflowExecuteMode,
} from './execution-context';
import type { ExecutionStatus } from './execution-status';
import type { Result } from './result';
import type { Workflow } from './workflow';
import type { EnvProviderState } from './workflow-data-proxy-env-provider';
import type { IRunExecutionData } from './run-execution-data/run-execution-data';
export type { WorkflowExecuteModeValues as WorkflowExecuteMode } from './execution-context';
export interface IAdditionalCredentialOptions {
oauth2?: IOAuth2Options;
credentialsDecrypted?: ICredentialsDecrypted;
}
export type IAllExecuteFunctions =
| IExecuteFunctions
| IExecutePaginationFunctions
| IExecuteSingleFunctions
| ISupplyDataFunctions
| IHookFunctions
| ILoadOptionsFunctions
| IPollFunctions
| ITriggerFunctions
| IWebhookFunctions;
export type BinaryFileType = 'text' | 'json' | 'image' | 'audio' | 'video' | 'pdf' | 'html';
export interface IBinaryData {
[key: string]: string | number | undefined;
data: string;
mimeType: string;
fileType?: BinaryFileType;
fileName?: string;
directory?: string;
fileExtension?: string;
fileSize?: string;
bytes?: number;
id?: string;
}
// All properties in this interface except for
// "includeCredentialsOnRefreshOnBody" will get
// removed once we add the OAuth2 hooks to the
// credentials file.
export interface IOAuth2Options {
includeCredentialsOnRefreshOnBody?: boolean;
property?: string;
tokenType?: string;
keepBearer?: boolean;
tokenExpiredStatusCode?: number;
keyToIncludeInAccessTokenHeader?: string;
}
export interface IConnection {
// The node the connection is to
node: string;
// The type of the input on destination node (for example "main")
type: NodeConnectionType;
// The output/input-index of destination node (if node has multiple inputs/outputs of the same type)
index: number;
}
export type ExecutionError =
| ExpressionError
| WorkflowActivationError
| WorkflowOperationError
| ExecutionCancelledError
| NodeOperationError
| NodeApiError;
// Get used to gives nodes access to credentials
export interface IGetCredentials {
get(type: string, id: string | null): Promise<ICredentialsEncrypted>;
}
export abstract class ICredentials<T extends object = ICredentialDataDecryptedObject> {
id?: string;
name: string;
type: string;
data: string | undefined;
constructor(nodeCredentials: INodeCredentialsDetails, type: string, data?: string) {
this.id = nodeCredentials.id ?? undefined;
this.name = nodeCredentials.name;
this.type = type;
this.data = data;
}
abstract getData(nodeType?: string): T;
abstract getDataToSave(): ICredentialsEncrypted;
abstract setData(data: T): void;
}
export interface IUser {
id: string;
email: string;
firstName: string;
lastName: string;
}
export type ProjectSharingData = {
id: string;
name: string | null;
icon: { type: 'emoji' | 'icon'; value: string } | null;
type: 'personal' | 'team' | 'public';
createdAt: string;
updatedAt: string;
};
export interface ICredentialsDecrypted<T extends object = ICredentialDataDecryptedObject> {
id: string;
name: string;
type: string;
data?: T;
homeProject?: ProjectSharingData;
sharedWithProjects?: ProjectSharingData[];
isGlobal?: boolean;
isResolvable?: boolean;
}
export interface ICredentialsEncrypted {
id?: string;
name: string;
type: string;
data?: string;
}
export interface ICredentialsExpressionResolveValues {
connectionInputData: INodeExecutionData[];
itemIndex: number;
node: INode;
runExecutionData: IRunExecutionData | null;
runIndex: number;
workflow: Workflow;
}
// Simplified options of request library
export interface IRequestOptionsSimplified {
auth?: {
username: string;
password: string;
sendImmediately?: boolean;
};
body: IDataObject;
headers: IDataObject;
qs: IDataObject;
}
export interface IRequestOptionsSimplifiedAuth {
auth?: {
username: string;
password: string;
sendImmediately?: boolean;
};
body?: IDataObject;
headers?: IDataObject;
qs?: IDataObject;
url?: string;
skipSslCertificateValidation?: boolean | string;
}
export interface IHttpRequestHelper {
helpers: { httpRequest: IAllExecuteFunctions['helpers']['httpRequest'] };
}
export abstract class ICredentialsHelper {
abstract getParentTypes(name: string): string[];
abstract authenticate(
credentials: ICredentialDataDecryptedObject,
typeName: string,
requestOptions: IHttpRequestOptions | IRequestOptionsSimplified,
workflow: Workflow,
node: INode,
): Promise<IHttpRequestOptions>;
abstract preAuthentication(
helpers: IHttpRequestHelper,
credentials: ICredentialDataDecryptedObject,
typeName: string,
node: INode,
credentialsExpired: boolean,
): Promise<ICredentialDataDecryptedObject | undefined>;
abstract getCredentials(
nodeCredentials: INodeCredentialsDetails,
type: string,
): Promise<ICredentials>;
abstract getDecrypted(
additionalData: IWorkflowExecuteAdditionalData,
nodeCredentials: INodeCredentialsDetails,
type: string,
mode: WorkflowExecuteMode,
executeData?: IExecuteData,
raw?: boolean,
expressionResolveValues?: ICredentialsExpressionResolveValues,
): Promise<ICredentialDataDecryptedObject>;
abstract updateCredentials(
nodeCredentials: INodeCredentialsDetails,
type: string,
data: ICredentialDataDecryptedObject,
): Promise<void>;
abstract updateCredentialsOauthTokenData(
nodeCredentials: INodeCredentialsDetails,
type: string,
data: ICredentialDataDecryptedObject,
additionalData: IWorkflowExecuteAdditionalData,
): Promise<void>;
abstract getCredentialsProperties(type: string): INodeProperties[];
}
export interface IAuthenticateBase {
type: string;
properties:
| {
[key: string]: string;
}
| IRequestOptionsSimplifiedAuth;
}
export interface IAuthenticateGeneric extends IAuthenticateBase {
type: 'generic';
properties: IRequestOptionsSimplifiedAuth;
}
export type IAuthenticate =
| ((
credentials: ICredentialDataDecryptedObject,
requestOptions: IHttpRequestOptions,
) => Promise<IHttpRequestOptions>)
| IAuthenticateGeneric;
export interface IAuthenticateRuleBase {
type: string;
properties: {
[key: string]: string | number;
};
errorMessage?: string;
}
export interface IAuthenticateRuleResponseCode extends IAuthenticateRuleBase {
type: 'responseCode';
properties: {
value: number;
message: string;
};
}
export interface IAuthenticateRuleResponseSuccessBody extends IAuthenticateRuleBase {
type: 'responseSuccessBody';
properties: {
message: string;
key: string;
value: any;
};
}
type Override<A extends object, B extends object> = Omit<A, keyof B> & B;
export namespace DeclarativeRestApiSettings {
// The type below might be extended
// with new options that need to be parsed as expressions
export type HttpRequestOptions = Override<
IHttpRequestOptions,
{ skipSslCertificateValidation?: string | boolean; url?: string }
>;
export type ResultOptions = {
maxResults?: number | string;
options: HttpRequestOptions;
paginate?: boolean | string;
preSend: PreSendAction[];
postReceive: Array<{
data: {
parameterValue: string | IDataObject | undefined;
};
actions: PostReceiveAction[];
}>;
requestOperations?: IN8nRequestOperations;
};
}
export interface ICredentialTestRequest {
request: DeclarativeRestApiSettings.HttpRequestOptions;
rules?: IAuthenticateRuleResponseCode[] | IAuthenticateRuleResponseSuccessBody[];
}
export interface ICredentialTestRequestData {
nodeType?: INodeType;
testRequest: ICredentialTestRequest;
}
type ICredentialHttpRequestNode = {
name: string;
docsUrl: string;
hidden?: boolean;
} & ({ apiBaseUrl: string } | { apiBaseUrlPlaceholder: string });
export interface ICredentialType {
name: string;
displayName: string;
icon?: Icon;
iconColor?: ThemeIconColor;
iconUrl?: Themed<string>;
/**
* Base path for resolving file icons from expressions.
* Used when icon is an expression that resolves to `file:filename.svg`
* Format: `icons/${packageName}/${nodeDirPath}`
*/
iconBasePath?: string;
extends?: string[];
properties: INodeProperties[];
documentationUrl?: string;
__overwrittenProperties?: string[];
authenticate?: IAuthenticate;
preAuthentication?: (
this: IHttpRequestHelper,
credentials: ICredentialDataDecryptedObject,
) => Promise<IDataObject>;
test?: ICredentialTestRequest;
genericAuth?: boolean;
httpRequestNode?: ICredentialHttpRequestNode;
supportedNodes?: string[];
}
export interface ICredentialTypes {
recognizes(credentialType: string): boolean;
getByName(credentialType: string): ICredentialType;
getSupportedNodes(type: string): string[];
getParentTypes(typeName: string): string[];
}
// The way the credentials get saved in the database (data encrypted)
export interface ICredentialData {
id?: string;
name: string;
data: string; // Contains the access data as encrypted JSON string
}
// The encrypted credentials which the nodes can access
export type CredentialInformation =
| string
| string[]
| number
| boolean
| IDataObject
| IDataObject[];
// The encrypted credentials which the nodes can access
export interface ICredentialDataDecryptedObject {
[key: string]: CredentialInformation;
}
// First array index: The output/input-index (if node has multiple inputs/outputs of the same type)
// Second array index: The different connections (if one node is connected to multiple nodes)
// Any index can be null, for example in a switch node with multiple indexes some of which are not connected
export type NodeInputConnections = Array<IConnection[] | null>;
export interface INodeConnection {
sourceIndex: number;
destinationIndex: number;
}
export interface INodeConnections {
// Input name
[key: string]: NodeInputConnections;
}
export interface IConnections {
// Node name
[key: string]: INodeConnections;
}
export type GenericValue = string | object | number | boolean | undefined | null;
export type CloseFunction = () => Promise<void>;
export interface IDataObject {
[key: string]: GenericValue | IDataObject | GenericValue[] | IDataObject[];
}
export type IExecuteResponsePromiseData = IDataObject | IN8nHttpFullResponse;
export interface INodeTypeNameVersion {
name: string;
version: number;
}
export interface IRunNodeResponse {
data: INodeExecutionData[][] | null | undefined;
hints?: NodeExecutionHint[];
closeFunction?: CloseFunction;
}
export interface ISourceDataConnections {
// Key for each input type and because there can be multiple inputs of the same type it is an array
// null is also allowed because if we still need data for a later while executing the workflow set temporary to null
// the nodes get as input TaskDataConnections which is identical to this one except that no null is allowed.
[key: string]: Array<ISourceData[] | null>;
}
export interface IExecuteData {
data: ITaskDataConnections;
metadata?: ITaskMetadata;
node: INode;
source: ITaskDataConnectionsSource | null;
runIndex?: number;
}
export type IContextObject = {
[key: string]: any;
};
export interface IExecuteContextData {
// Keys are: "flow" | "node:<NODE_NAME>"
[key: string]: IContextObject;
}
export type IHttpRequestMethods = 'DELETE' | 'GET' | 'HEAD' | 'PATCH' | 'POST' | 'PUT';
/** used in helpers.httpRequest(WithAuthentication) */
export interface IHttpRequestOptions {
url: string;
baseURL?: string;
headers?: IDataObject;
method?: IHttpRequestMethods;
body?: FormData | GenericValue | GenericValue[] | Buffer | URLSearchParams;
qs?: IDataObject;
arrayFormat?: 'indices' | 'brackets' | 'repeat' | 'comma';
auth?: {
username: string;
password: string;
sendImmediately?: boolean;
};
disableFollowRedirect?: boolean;
encoding?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream';
skipSslCertificateValidation?: boolean;
returnFullResponse?: boolean;
ignoreHttpStatusErrors?: boolean;
proxy?: {
host: string;
port: number;
auth?: {
username: string;
password: string;
};
protocol?: string;
};
timeout?: number;
json?: boolean;
abortSignal?: GenericAbortSignal;
}
/**
* used in helpers.request(WithAuthentication)
* @see IHttpRequestOptions
* @deprecated Prefer using IHttpRequestOptions
*/
export interface IRequestOptions {
baseURL?: string;
uri?: string;
url?: string;
method?: IHttpRequestMethods;
qs?: IDataObject;
qsStringifyOptions?: { arrayFormat: 'repeat' | 'brackets' | 'indices' };
useQuerystring?: boolean;
headers?: IDataObject;
auth?: Partial<{
sendImmediately: boolean;
bearer: string;
user: string;
username: string;
password: string;
pass: string;
}>;
body?: any;
formData?: IDataObject | FormData;
form?: IDataObject | FormData;
json?: boolean;
useStream?: boolean;
encoding?: string | null;
timeout?: number;
rejectUnauthorized?: boolean;
proxy?: string | AxiosProxyConfig;
simple?: boolean;
gzip?: boolean;
resolveWithFullResponse?: boolean;
/** Whether to follow GET or HEAD HTTP 3xx redirects @default true */
followRedirect?: boolean;
/** Whether to follow **All** HTTP 3xx redirects @default false */
followAllRedirects?: boolean;
/** Max number of redirects to follow @default 21 */
maxRedirects?: number;
agentOptions?: SecureContextOptions;
}
export interface PaginationOptions {
binaryResult?: boolean;
continue: boolean | string;
request: IRequestOptionsSimplifiedAuth;
requestInterval: number;
maxRequests?: number;
}
export type IN8nHttpResponse = IDataObject | Buffer | GenericValue | GenericValue[] | null;
export interface IN8nHttpFullResponse {
body: IN8nHttpResponse | Readable;
__bodyResolved?: boolean;
headers: IDataObject;
statusCode: number;
statusMessage?: string;
}
export interface IN8nRequestOperations {
pagination?:
| IN8nRequestOperationPaginationGeneric
| IN8nRequestOperationPaginationOffset
| ((
this: IExecutePaginationFunctions,
requestOptions: DeclarativeRestApiSettings.ResultOptions,
) => Promise<INodeExecutionData[]>);
}
export interface IN8nRequestOperationPaginationBase {
type: string;
properties: {
[key: string]: unknown;
};
}
export interface IN8nRequestOperationPaginationGeneric extends IN8nRequestOperationPaginationBase {
type: 'generic';
properties: {
continue: boolean | string;
request: IRequestOptionsSimplifiedAuth;
};
}
export interface IN8nRequestOperationPaginationOffset extends IN8nRequestOperationPaginationBase {
type: 'offset';
properties: {
limitParameter: string;
offsetParameter: string;
pageSize: number;
rootProperty?: string; // Optional Path to option array
type: 'body' | 'query';
};
}
export type EnsureTypeOptions = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'json';
export interface IGetNodeParameterOptions {
contextNode?: INode;
// make sure that returned value would be of specified type, converts it if needed
ensureType?: EnsureTypeOptions;
// extract value from regex, works only when parameter type is resourceLocator
extractValue?: boolean;
// get raw value of parameter with unresolved expressions
rawExpressions?: boolean;
// skip validation of parameter
skipValidation?: boolean;
}
namespace ExecuteFunctions {
namespace StringReturning {
export type NodeParameter =
| 'binaryProperty'
| 'binaryPropertyName'
| 'binaryPropertyOutput'
| 'dataPropertyName'
| 'dataBinaryProperty'
| 'resource'
| 'operation'
| 'filePath'
| 'encodingType';
}
namespace NumberReturning {
export type NodeParameter = 'limit';
}
namespace BooleanReturning {
export type NodeParameter =
| 'binaryData'
| 'download'
| 'jsonParameters'
| 'returnAll'
| 'rawData'
| 'resolveData';
}
namespace RecordReturning {
export type NodeParameter = 'additionalFields' | 'filters' | 'options' | 'updateFields';
}
export type GetNodeParameterFn = {
// @TECH_DEBT: Refactor to remove this barely used overload - N8N-5632
getNodeParameter<T extends { resource: string }>(
parameterName: 'resource',
itemIndex?: number,
): T['resource'];
getNodeParameter(
parameterName: StringReturning.NodeParameter,
itemIndex: number,
fallbackValue?: string,
options?: IGetNodeParameterOptions,
): string;
getNodeParameter(
parameterName: RecordReturning.NodeParameter,
itemIndex: number,
fallbackValue?: IDataObject,
options?: IGetNodeParameterOptions,
): IDataObject;
getNodeParameter(
parameterName: BooleanReturning.NodeParameter,
itemIndex: number,
fallbackValue?: boolean,
options?: IGetNodeParameterOptions,
): boolean;
getNodeParameter(
parameterName: NumberReturning.NodeParameter,
itemIndex: number,
fallbackValue?: number,
options?: IGetNodeParameterOptions,
): number;
getNodeParameter(
parameterName: string,
itemIndex: number,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
};
}
export interface IExecuteWorkflowInfo {
code?: IWorkflowBase;
id?: string;
}
export type ICredentialTestFunction = (
this: ICredentialTestFunctions,
credential: ICredentialsDecrypted<ICredentialDataDecryptedObject>,
) => Promise<INodeCredentialTestResult>;
export interface ICredentialTestFunctions {
logger: Logger;
helpers: SSHTunnelFunctions & {
request: (uriOrObject: string | object, options?: object) => Promise<any>;
};
}
export interface BaseHelperFunctions {
createDeferredPromise: <T = void>() => IDeferredPromise<T>;
returnJsonArray(jsonData: IDataObject | IDataObject[]): INodeExecutionData[];
}
const __brand = Symbol('resolvedFilePath');
export type ResolvedFilePath = string & {
[__brand]: 'ResolvedFilePath';
};
export interface FileSystemHelperFunctions {
resolvePath(path: PathLike): Promise<ResolvedFilePath>;
/**
* Use {@link resolvePath} to resolve the path first.
*/
isFilePathBlocked(filePath: ResolvedFilePath): boolean;
/**
* Use {@link resolvePath} to resolve the path first.
*/
createReadStream(filePath: ResolvedFilePath): Promise<Readable>;
getStoragePath(): string;
/**
* Use {@link resolvePath} to resolve the path first.
*/
writeContentToFile(
path: ResolvedFilePath,
content: string | Buffer | Readable,
flag?: number,
): Promise<void>;
}
export interface BinaryHelperFunctions {
prepareBinaryData(
binaryData: Buffer | Readable,
filePath?: string,
mimeType?: string,
): Promise<IBinaryData>;
setBinaryDataBuffer(data: IBinaryData, binaryData: Buffer): Promise<IBinaryData>;
/** @deprecated */
copyBinaryFile(): Promise<never>;
binaryToBuffer(body: Buffer | Readable): Promise<Buffer>;
binaryToString(body: Buffer | Readable, encoding?: BufferEncoding): Promise<string>;
getBinaryPath(binaryDataId: string): string;
getBinaryStream(binaryDataId: string, chunkSize?: number): Promise<Readable>;
createBinarySignedUrl(binaryData: IBinaryData, expiresIn?: string): string;
getBinaryMetadata(binaryDataId: string): Promise<{
fileName?: string;
mimeType?: string;
fileSize: number;
}>;
}
export type DeduplicationScope = 'node' | 'workflow';
export type DeduplicationItemTypes = string | number;
export type DeduplicationMode = 'entries' | 'latestIncrementalKey' | 'latestDate';
export interface IProcessedDataLatest {
mode: DeduplicationMode;
data: DeduplicationItemTypes;
}
export interface IProcessedDataEntries {
mode: DeduplicationMode;
data: DeduplicationItemTypes[];
}
export interface IDeduplicationOutput {
new: DeduplicationItemTypes[];
processed: DeduplicationItemTypes[];
}
export interface IDeduplicationOutputItems {
new: IDataObject[];
processed: IDataObject[];
}
export interface ICheckProcessedOptions {
mode: DeduplicationMode;
maxEntries?: number;
}
export interface DeduplicationHelperFunctions {
checkProcessedAndRecord(
items: DeduplicationItemTypes[],
scope: DeduplicationScope,
options: ICheckProcessedOptions,
): Promise<IDeduplicationOutput>;
checkProcessedItemsAndRecord(
propertyName: string,
items: IDataObject[],
scope: DeduplicationScope,
options: ICheckProcessedOptions,
): Promise<IDeduplicationOutputItems>;
removeProcessed(
items: DeduplicationItemTypes[],
scope: DeduplicationScope,
options: ICheckProcessedOptions,
): Promise<void>;
clearAllProcessedItems(scope: DeduplicationScope, options: ICheckProcessedOptions): Promise<void>;
getProcessedDataCount(
scope: DeduplicationScope,
options: ICheckProcessedOptions,
): Promise<number>;
}
interface NodeHelperFunctions {
copyBinaryFile(filePath: string, fileName: string, mimeType?: string): Promise<IBinaryData>;
}
export interface RequestHelperFunctions {
httpRequest(requestOptions: IHttpRequestOptions): Promise<any>;
httpRequestWithAuthentication(
this: IAllExecuteFunctions,
credentialsType: string,
requestOptions: IHttpRequestOptions,
additionalCredentialOptions?: IAdditionalCredentialOptions,
): Promise<any>;
requestWithAuthenticationPaginated(
this: IAllExecuteFunctions,
requestOptions: IRequestOptions,
itemIndex: number,
paginationOptions: PaginationOptions,
credentialsType?: string,
additionalCredentialOptions?: IAdditionalCredentialOptions,
): Promise<any[]>;
/**
* @deprecated Use .httpRequest instead
* @see RequestHelperFunctions.httpRequest
*/
request(uriOrObject: string | IRequestOptions, options?: IRequestOptions): Promise<any>;
/**
* @deprecated Use .httpRequestWithAuthentication instead
* @see RequestHelperFunctions.requestWithAuthentication
*/
requestWithAuthentication(
this: IAllExecuteFunctions,
credentialsType: string,
requestOptions: IRequestOptions,
additionalCredentialOptions?: IAdditionalCredentialOptions,
itemIndex?: number,
): Promise<any>;
/**
* @deprecated Use .httpRequestWithAuthentication instead
* @see RequestHelperFunctions.requestWithAuthentication
*/
requestOAuth1(
this: IAllExecuteFunctions,
credentialsType: string,
requestOptions: IRequestOptions,
): Promise<any>;
/**
* @deprecated Use .httpRequestWithAuthentication instead
* @see RequestHelperFunctions.requestWithAuthentication
*/
requestOAuth2(
this: IAllExecuteFunctions,
credentialsType: string,
requestOptions: IRequestOptions,
oAuth2Options?: IOAuth2Options,
): Promise<any>;
refreshOAuth2Token(
this: IAllExecuteFunctions,
credentialsType: string,
oAuth2Options?: IOAuth2Options,
): Promise<any>;
}
export type SSHCredentials = {
sshHost: string;
sshPort: number;
sshUser: string;
} & (
| {
sshAuthenticateWith: 'password';
sshPassword: string;
}
| {
sshAuthenticateWith: 'privateKey';
// TODO: rename this to `sshPrivateKey`
privateKey: string;
// TODO: rename this to `sshPassphrase`
passphrase?: string;
}
);
export interface SSHTunnelFunctions {
getSSHClient(credentials: SSHCredentials, abortController?: AbortController): Promise<SSHClient>;
updateLastUsed(client: SSHClient): void;
}
type CronUnit = number | '*' | `*/${number}`;
export type CronExpression =
`${CronUnit} ${CronUnit} ${CronUnit} ${CronUnit} ${CronUnit} ${CronUnit}`;
type CronRecurrenceRule =
| { activated: false }
| {
activated: true;
index: number;
intervalSize: number;
typeInterval: 'hours' | 'days' | 'weeks' | 'months';
};
export type CronContext = {
nodeId: string;
workflowId: string;
timezone: string;
expression: CronExpression;
recurrence?: CronRecurrenceRule;
};
export type Cron = { expression: CronExpression; recurrence?: CronRecurrenceRule };
export interface SchedulingFunctions {
registerCron(cron: Cron, onTick: () => void): void;
}
export type NodeTypeAndVersion = {
name: string;
type: string;
typeVersion: number;
disabled: boolean;
parameters?: INodeParameters;
};
export interface FunctionsBase {
logger: Logger;
getCredentials<T extends object = ICredentialDataDecryptedObject>(
type: string,
itemIndex?: number,
): Promise<T>;
getCredentialsProperties(type: string): INodeProperties[];
getExecutionId(): string;
getNode(): INode;
getWorkflow(): IWorkflowMetadata;
getWorkflowSettings(): IWorkflowSettings;
getWorkflowStaticData(type: string): IDataObject;
getTimezone(): string;
getRestApiUrl(): string;
getInstanceBaseUrl(): string;
getInstanceId(): string;
/** Get the waiting resume url signed with the signature token */
getSignedResumeUrl(parameters?: Record<string, string>): string;
/** Set requirement in the execution for signature token validation */
setSignatureValidationRequired(): void;
getChildNodes(
nodeName: string,
options?: { includeNodeParameters?: boolean },
): NodeTypeAndVersion[];
getParentNodes(
nodeName: string,
options?: {
includeNodeParameters?: boolean;
connectionType?: NodeConnectionType;
depth?: number;
},
): NodeTypeAndVersion[];
getKnownNodeTypes(): IDataObject;
getMode?: () => WorkflowExecuteMode;
getActivationMode?: () => WorkflowActivateMode;
getChatTrigger: () => INode | null;
isNodeFeatureEnabled(featureName: string): boolean;
getExecutionContext: () => IExecutionContext | undefined;
/** @deprecated */
prepareOutputData(outputData: INodeExecutionData[]): Promise<INodeExecutionData[][]>;
}
type FunctionsBaseWithRequiredKeys<Keys extends keyof FunctionsBase> = FunctionsBase & {
[K in Keys]: NonNullable<FunctionsBase[K]>;
};
export type ContextType = 'flow' | 'node';
export type DataTableProxyProvider = {
getDataTableAggregateProxy(
workflow: Workflow,
node: INode,
projectId?: string,
): Promise<IDataTableProjectAggregateService>;
getDataTableProxy(
workflow: Workflow,
node: INode,
dataTableId: string,
projectId?: string,
): Promise<IDataTableProjectService>;
};
export type DataTableProxyFunctions = {
// These are optional to account for situations where the data-table module is disabled
getDataTableAggregateProxy?(): Promise<IDataTableProjectAggregateService>;
getDataTableProxy?(dataTableId: string): Promise<IDataTableProjectService>;
};
type BaseExecutionFunctions = FunctionsBaseWithRequiredKeys<'getMode'> & {
continueOnFail(): boolean;
setMetadata(metadata: ITaskMetadata): void;
evaluateExpression(expression: string, itemIndex: number): NodeParameterValueType;
getContext(type: ContextType): IContextObject;
getExecuteData(): IExecuteData;
getWorkflowDataProxy(itemIndex: number): IWorkflowDataProxyData;
getInputSourceData(inputIndex?: number, connectionType?: NodeConnectionType): ISourceData;
getExecutionCancelSignal(): AbortSignal | undefined;
onExecutionCancellation(handler: () => unknown): void;
logAiEvent(eventName: AiEvent, msg?: string): void;
};
// TODO: Create later own type only for Config-Nodes
export type IExecuteFunctions = ExecuteFunctions.GetNodeParameterFn &
BaseExecutionFunctions & {
executeWorkflow(
workflowInfo: IExecuteWorkflowInfo,
inputData?: INodeExecutionData[],
parentCallbackManager?: CallbackManager,
options?: {
doNotWaitToFinish?: boolean;
parentExecution?: RelatedExecution;
executionMode?: WorkflowExecuteMode;
},
): Promise<ExecuteWorkflowData>;
getExecutionDataById(executionId: string): Promise<IRunExecutionData | undefined>;
getInputConnectionData(
connectionType: AINodeConnectionType,
itemIndex: number,
inputIndex?: number,
): Promise<unknown>;
getInputData(inputIndex?: number, connectionType?: NodeConnectionType): INodeExecutionData[];
getNodeInputs(): INodeInputConfiguration[];
getNodeOutputs(): INodeOutputConfiguration[];
putExecutionToWait(waitTill: Date): Promise<void>;
sendMessageToUI(message: any): void;
sendResponse(response: IExecuteResponsePromiseData): void;
sendChunk(type: ChunkType, itemIndex: number, content?: IDataObject | string): void;
isStreaming(): boolean;
/** Returns true if the node is being executed as an AI Agent tool */
isToolExecution(): boolean;
// TODO: Make this one then only available in the new config one
addInputData(
connectionType: NodeConnectionType,
data: INodeExecutionData[][] | ExecutionError,
runIndex?: number,
): { index: number };
addOutputData(
connectionType: NodeConnectionType,
currentNodeRunIndex: number,
data: INodeExecutionData[][] | ExecutionError,
metadata?: ITaskMetadata,
sourceNodeRunIndex?: number,
): void;
addExecutionHints(...hints: NodeExecutionHint[]): void;
nodeHelpers: NodeHelperFunctions;
helpers: RequestHelperFunctions &
BaseHelperFunctions &
BinaryHelperFunctions &
DeduplicationHelperFunctions &
FileSystemHelperFunctions &
SSHTunnelFunctions &
DataTableProxyFunctions & {
normalizeItems(items: INodeExecutionData | INodeExecutionData[]): INodeExecutionData[];
constructExecutionMetaData(
inputData: INodeExecutionData[],
options: { itemData: IPairedItemData | IPairedItemData[] },
): NodeExecutionWithMetadata[];
assertBinaryData(itemIndex: number, parameterData: string | IBinaryData): IBinaryData;
getBinaryDataBuffer(
itemIndex: number,
parameterData: string | IBinaryData,
): Promise<Buffer>;
detectBinaryEncoding(buffer: Buffer): string;
copyInputItems(items: INodeExecutionData[], properties: string[]): IDataObject[];
};
getParentCallbackManager(): CallbackManager | undefined;
startJob<T = unknown, E = unknown>(
jobType: string,
settings: unknown,
itemIndex: number,
): Promise<Result<T, E>>;
getRunnerStatus(taskType: string): { available: true } | { available: false; reason?: string };
};
export interface IExecuteSingleFunctions extends BaseExecutionFunctions {
getInputData(inputIndex?: number, connectionType?: NodeConnectionType): INodeExecutionData;
getItemIndex(): number;
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
helpers: RequestHelperFunctions &
BaseHelperFunctions &
BinaryHelperFunctions & {
assertBinaryData(propertyName: string, inputIndex?: number): IBinaryData;
getBinaryDataBuffer(propertyName: string, inputIndex?: number): Promise<Buffer>;
detectBinaryEncoding(buffer: Buffer): string;
};
}
export type ISupplyDataFunctions = ExecuteFunctions.GetNodeParameterFn &
FunctionsBaseWithRequiredKeys<'getMode'> &
Pick<
IExecuteFunctions,
| 'addInputData'
| 'addOutputData'
| 'getInputConnectionData'
| 'getInputData'
| 'getNodeOutputs'
| 'executeWorkflow'
| 'sendMessageToUI'
| 'startJob'
| 'helpers'
| 'isToolExecution'
> & {
getNextRunIndex(): number;
continueOnFail(): boolean;
evaluateExpression(expression: string, itemIndex: number): NodeParameterValueType;
getWorkflowDataProxy(itemIndex: number): IWorkflowDataProxyData;
getExecutionCancelSignal(): AbortSignal | undefined;
onExecutionCancellation(handler: () => unknown): void;
logAiEvent(eventName: AiEvent, msg?: string): void;
addExecutionHints(...hints: NodeExecutionHint[]): void;
cloneWith(replacements: {
runIndex: number;
inputData: INodeExecutionData[][];
}): ISupplyDataFunctions;
};
export interface IExecutePaginationFunctions extends IExecuteSingleFunctions {
makeRoutingRequest(
this: IAllExecuteFunctions,
requestOptions: DeclarativeRestApiSettings.ResultOptions,
): Promise<INodeExecutionData[]>;
}
export interface ILoadOptionsFunctions extends FunctionsBase {
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
getCurrentNodeParameter(
parameterName: string,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object | undefined;
getCurrentNodeParameters(): INodeParameters | undefined;
helpers: RequestHelperFunctions & SSHTunnelFunctions & DataTableProxyFunctions;
}
export type FieldValueOption = { name: string; type: FieldType | 'any' };
export type IWorkflowNodeContext = ExecuteFunctions.GetNodeParameterFn &
Pick<FunctionsBase, 'getNode' | 'getWorkflow'>;
export interface ILocalLoadOptionsFunctions {
getWorkflowNodeContext(
nodeType: string,
useActiveVersion?: boolean,
): Promise<IWorkflowNodeContext | null>;
}
export interface IWorkflowLoader {
get(workflowId: string): Promise<IWorkflowBase>;
}
export interface IPollFunctions
extends FunctionsBaseWithRequiredKeys<'getMode' | 'getActivationMode'> {
__emit(
data: INodeExecutionData[][],
responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>,
donePromise?: IDeferredPromise<IRun>,
): void;
__emitError(error: Error, responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>): void;
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
helpers: RequestHelperFunctions &
BaseHelperFunctions &
BinaryHelperFunctions &
SchedulingFunctions;
}
export interface ITriggerFunctions
extends FunctionsBaseWithRequiredKeys<'getMode' | 'getActivationMode'> {
emit(
data: INodeExecutionData[][],
responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>,
donePromise?: IDeferredPromise<IRun>,
): void;
emitError(error: Error, responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>): void;
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
helpers: RequestHelperFunctions &
BaseHelperFunctions &
BinaryHelperFunctions &
SSHTunnelFunctions &
SchedulingFunctions;
}
export interface IHookFunctions
extends FunctionsBaseWithRequiredKeys<'getMode' | 'getActivationMode'> {
getWebhookName(): string;
getWebhookDescription(name: WebhookType): IWebhookDescription | undefined;
getNodeWebhookUrl: (name: WebhookType) => string | undefined;
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
helpers: RequestHelperFunctions;
}
export interface IWebhookFunctions extends FunctionsBaseWithRequiredKeys<'getMode'> {
getBodyData(): IDataObject;
getHeaderData(): IncomingHttpHeaders;
getInputConnectionData(
connectionType: AINodeConnectionType,
itemIndex: number,
inputIndex?: number,
): Promise<unknown>;
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
getNodeWebhookUrl: (name: WebhookType) => string | undefined;
evaluateExpression(expression: string, itemIndex?: number): NodeParameterValueType;
getParamsData(): object;
getQueryData(): object;
getRequestObject(): express.Request;
getResponseObject(): express.Response;
getWebhookName(): string;
nodeHelpers: NodeHelperFunctions;
helpers: RequestHelperFunctions & BaseHelperFunctions & BinaryHelperFunctions;
}
export interface INodeCredentialsDetails {
id: string | null;
name: string;
}
export interface INodeCredentials {
[key: string]: INodeCredentialsDetails;
}
export type OnError = 'continueErrorOutput' | 'continueRegularOutput' | 'stopWorkflow';
export interface INode {
id: string;
name: string;
typeVersion: number;
type: string;
position: [number, number];
disabled?: boolean;
notes?: string;
notesInFlow?: boolean;
retryOnFail?: boolean;
maxTries?: number;
waitBetweenTries?: number;
alwaysOutputData?: boolean;
executeOnce?: boolean;
onError?: OnError;
continueOnFail?: boolean;
parameters: INodeParameters;
credentials?: INodeCredentials;
webhookId?: string;
extendsCredential?: string;
rewireOutputLogTo?: NodeConnectionType;
// forces the node to execute a particular custom operation
// based on resource and operation
// instead of calling default execute function
// used by evaluations test-runner
forceCustomOperation?: {
resource: string;
operation: string;
};
}
export interface IPinData {
[nodeName: string]: INodeExecutionData[];
}
export interface INodes {
[key: string]: INode;
}
export interface IObservableObject {
[key: string]: any;
__dataChanged: boolean;
}
export interface IBinaryKeyData {
[key: string]: IBinaryData;
}
export interface IPairedItemData {
item: number;
input?: number; // If undefined "0" gets used
sourceOverwrite?: ISourceData;
}
export interface INodeExecutionData {
[key: string]:
| IDataObject
| IBinaryKeyData
| IPairedItemData
| IPairedItemData[]
| NodeApiError
| NodeOperationError
| number
| string
| undefined;
json: IDataObject;
binary?: IBinaryKeyData;
error?: NodeApiError | NodeOperationError;
pairedItem?: IPairedItemData | IPairedItemData[] | number;
metadata?: {
subExecution: RelatedExecution;
};
evaluationData?: Record<string, GenericValue>;
/**
* Use this key to send a message to the chat.
*
* - Workflow has to be started by a chat node.
* - Put execution to wait after sending.
*
* See example in
* packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/Chat.node.ts
*/
sendMessage?: string;
/**
* @deprecated This key was added by accident and should not be used as it
* will be removed in future. For more information see PR #12469.
*/
index?: number;
}
export type NodeParameterValue = string | number | boolean | undefined | null;
export type ResourceLocatorModes = 'id' | 'url' | 'list' | string;
export interface IResourceLocatorResult {
name: string;
value: string;
url?: string;
}
export interface INodeParameterResourceLocator {
__rl: true;
mode: ResourceLocatorModes;
value: Exclude<NodeParameterValue, boolean>;
cachedResultName?: string;
cachedResultUrl?: string;
__regex?: string;
}
export type IconOrEmoji = { type: 'icon'; value: string } | { type: 'emoji'; value: string };
export type NodeParameterValueType =
// TODO: Later also has to be possible to add multiple ones with the name name. So array has to be possible
| NodeParameterValue
| INodeParameters
| INodeParameterResourceLocator
| ResourceMapperValue
| FilterValue
| AssignmentCollectionValue
| IconOrEmoji
| NodeParameterValue[]
| INodeParameters[]
| INodeParameterResourceLocator[]
| ResourceMapperValue[];
export interface INodeParameters {
[key: string]: NodeParameterValueType;
}
export type NodePropertyTypes =
| 'boolean'
| 'button'
| 'collection'
| 'color'
| 'dateTime'
| 'fixedCollection'
| 'hidden'
| 'icon'
| 'json'
| 'callout'
| 'notice'
| 'multiOptions'
| 'number'
| 'options'
| 'string'
| 'credentialsSelect'
| 'resourceLocator'
| 'curlImport'
| 'resourceMapper'
| 'filter'
| 'assignmentCollection'
| 'credentials'
| 'workflowSelector';
export type CodeAutocompleteTypes = 'function' | 'functionItem';
export type EditorType = 'codeNodeEditor' | 'jsEditor' | 'htmlEditor' | 'sqlEditor' | 'cssEditor';
export type CodeNodeEditorLanguage = (typeof CODE_LANGUAGES)[number];
export type CodeExecutionMode = (typeof CODE_EXECUTION_MODES)[number];
export type SQLDialect =
| 'StandardSQL'
| 'PostgreSQL'
| 'MySQL'
| 'OracleDB'
| 'MariaSQL'
| 'MSSQL'
| 'SQLite'
| 'Cassandra'
| 'PLSQL';
export interface ILoadOptions {
routing?: {
operations?: IN8nRequestOperations;
output?: INodeRequestOutput;
request?: DeclarativeRestApiSettings.HttpRequestOptions;
};
}
export type NodePropertyAction = {
type: 'askAiCodeGeneration';
handler?: string;
target?: string;
};
export interface CalloutActionBase {
type: string;
label: string;
icon?: string;
}
export interface CalloutActionOpenSampleWorkflowTemplate extends CalloutActionBase {
type: 'openSampleWorkflowTemplate';
templateId: string;
}
export type CalloutAction = CalloutActionOpenSampleWorkflowTemplate;
export interface INodePropertyTypeOptions {
// Supported by: button
buttonConfig?: {
action?: string | NodePropertyAction;
label?: string; // otherwise "displayName" is used
hasInputField?: boolean;
inputFieldMaxLength?: number; // Supported if hasInputField is true
};
containerClass?: string; // Supported by: notice
alwaysOpenEditWindow?: boolean; // Supported by: json
codeAutocomplete?: CodeAutocompleteTypes; // Supported by: string
editor?: EditorType; // Supported by: string
editorIsReadOnly?: boolean; // Supported by: string
sqlDialect?: SQLDialect; // Supported by: sqlEditor
loadOptionsDependsOn?: string[]; // Supported by: options
loadOptionsMethod?: string; // Supported by: options
loadOptions?: ILoadOptions; // Supported by: options
maxValue?: number; // Supported by: number
minValue?: number; // Supported by: number
multipleValues?: boolean; // Supported by: <All>
multipleValueButtonText?: string; // Supported when "multipleValues" set to true
numberPrecision?: number; // Supported by: number
fixedCollection?: {
itemTitle?: string; // Template for item titles, supports {{ $collection.item.value }}, {{ $collection.item.index }}
};
password?: boolean; // Supported by: string
rows?: number; // Supported by: string
showAlpha?: boolean; // Supported by: color
sortable?: boolean; // Supported when "multipleValues" set to true
expirable?: boolean; // Supported by: hidden (only in the credentials)
dateOnly?: boolean; // Supported by: dateTime
resourceMapper?: ResourceMapperTypeOptions;
filter?: FilterTypeOptions;
assignment?: AssignmentTypeOptions;
minRequiredFields?: number; // Supported by: fixedCollection
maxAllowedFields?: number; // Supported by: fixedCollection
hideOptionalFields?: boolean; // Supported by: fixedCollection - hide non-required fields by default
addOptionalFieldButtonText?: string; // Supported by: fixedCollection with hideOptionalFields set to true
showEvenWhenOptional?: boolean; // Supported by: fixedCollection with hideOptionalFields
calloutAction?: CalloutAction; // Supported by: callout
binaryDataProperty?: boolean; // Indicate that the property expects binary data
[key: string]: any;
}
export interface ResourceMapperTypeOptionsBase {
mode: 'add' | 'update' | 'upsert' | 'map';
valuesLabel?: string;
fieldWords?: {
singular: string;
plural: string;
};
addAllFields?: boolean;
noFieldsError?: string;
multiKeyMatch?: boolean;
supportAutoMap?: boolean;
hideNoDataError?: boolean; // Hide "No data found" error when no fields are available
matchingFieldsLabels?: {
title?: string;
description?: string;
hint?: string;
};
showTypeConversionOptions?: boolean;
allowEmptyValues?: boolean;
}
// Enforce at least one of resourceMapperMethod or localResourceMapperMethod
export type ResourceMapperTypeOptionsLocal = {
resourceMapperMethod: string;
localResourceMapperMethod?: never; // Explicitly disallows this property
};
export type ResourceMapperTypeOptionsExternal = {
localResourceMapperMethod: string;
resourceMapperMethod?: never; // Explicitly disallows this property
};
export type ResourceMapperTypeOptions = ResourceMapperTypeOptionsBase &
(ResourceMapperTypeOptionsLocal | ResourceMapperTypeOptionsExternal);
type NonEmptyArray<T> = [T, ...T[]];
export type FilterTypeCombinator = 'and' | 'or';
export type FilterTypeOptions = {
version: 1 | 2 | 3 | {}; // required so nodes are pinned on a version
caseSensitive?: boolean | string; // default = true
leftValue?: string; // when set, user can't edit left side of condition
allowedCombinators?: NonEmptyArray<FilterTypeCombinator>; // default = ['and', 'or']
maxConditions?: number; // default = 10
typeValidation?: 'strict' | 'loose' | {}; // default = strict, `| {}` is a TypeScript trick to allow custom strings (expressions), but still give autocomplete
};
export type AssignmentTypeOptions = Partial<{
hideType?: boolean; // visible by default
defaultType?: FieldType | 'string';
disableType?: boolean; // visible by default
}>;
export type DisplayCondition =
| { _cnd: { eq: NodeParameterValue } }
| { _cnd: { not: NodeParameterValue } }
| { _cnd: { gte: number | string } }
| { _cnd: { lte: number | string } }
| { _cnd: { gt: number | string } }
| { _cnd: { lt: number | string } }
| { _cnd: { between: { from: number | string; to: number | string } } }
| { _cnd: { startsWith: string } }
| { _cnd: { endsWith: string } }
| { _cnd: { includes: string } }
| { _cnd: { regex: string } }
| { _cnd: { exists: true } };
export type NodeFeatures = Record<string, boolean>;
export type FeatureCondition = { '@version': Array<number | DisplayCondition> };
export type NodeFeaturesDefinition = Record<string, FeatureCondition>;
export interface IDisplayOptions {
hide?: {
[key: string]: Array<NodeParameterValue | DisplayCondition> | undefined;
};
show?: {
'@version'?: Array<number | DisplayCondition>;
'@feature'?: Array<string | DisplayCondition>;
'@tool'?: boolean[];
[key: string]: Array<NodeParameterValue | DisplayCondition> | undefined;
};
hideOnCloud?: boolean;
}
export interface ICredentialsDisplayOptions {
hide?: {
[key: string]: NodeParameterValue[] | undefined;
};
show?: {
'@version'?: number[];
[key: string]: NodeParameterValue[] | undefined;
};
hideOnCloud?: boolean;
}
export interface INodeProperties {
displayName: string;
name: string;
type: NodePropertyTypes;
typeOptions?: INodePropertyTypeOptions;
default: NodeParameterValueType;
description?: string;
hint?: string;
disabledOptions?: IDisplayOptions;
displayOptions?: IDisplayOptions;
options?: Array<INodePropertyOptions | INodeProperties | INodePropertyCollection>;
placeholder?: string;
isNodeSetting?: boolean;
noDataExpression?: boolean;
required?: boolean;
routing?: INodePropertyRouting;
credentialTypes?: Array<
'extends:oAuth2Api' | 'extends:oAuth1Api' | 'has:authenticate' | 'has:genericAuth'
>;
extractValue?: INodePropertyValueExtractor;
modes?: INodePropertyMode[];
requiresDataPath?: 'single' | 'multiple';
doNotInherit?: boolean;
// set expected type for the value which would be used for validation and type casting
validateType?: FieldType;
// works only if validateType is set
// allows to skip validation during execution or set custom validation/casting logic inside node
// inline error messages would still be shown in UI
ignoreValidationDuringExecution?: boolean;
// for type: options | multiOptions skip validation of the value (e.g. when value is not in the list and specified via expression)
allowArbitraryValues?: boolean;
// This field indicates that the field is a resolvable field that should be resolved in dynamic credential setup
resolvableField?: boolean;
}
export interface INodePropertyModeTypeOptions {
searchListMethod?: string; // Supported by: options
searchFilterRequired?: boolean;
searchable?: boolean;
/**
* If provided, a slow load notice will be shown to the user if the resource locator takes longer than the timeout to load.
* @example
* {
* message: 'If loading takes longer than expected, try selecting a resource using "By ID"',
* timeout: 10000,
* }
*/
slowLoadNotice?: { message: string; timeout: number };
/**
* If true, the resource locator will not show an error if the credentials are not selected
*/
skipCredentialsCheckInRLC?: boolean;
allowNewResource?: {
label: string;
} & (
| { method: string; url?: never; defaultName: string }
| { method?: never; url: string; defaultName?: never }
);
}
export interface INodePropertyMode {
displayName: string;
name: string;
type: 'string' | 'list';
hint?: string;
validation?: Array<
INodePropertyModeValidation | { (this: IExecuteSingleFunctions, value: string): void }
>;
placeholder?: string;
url?: string;
extractValue?: INodePropertyValueExtractor;
initType?: string;
entryTypes?: {
[name: string]: {
selectable?: boolean;
hidden?: boolean;
queryable?: boolean;
data?: {
request?: IHttpRequestOptions;
output?: INodeRequestOutput;
};
};
};
search?: INodePropertyRouting;
typeOptions?: INodePropertyModeTypeOptions;
}
export interface INodePropertyModeValidation {
type: string;
properties: {};
}
export interface INodePropertyRegexValidation extends INodePropertyModeValidation {
type: 'regex';
properties: {
regex: string;
errorMessage: string;
};
}
export interface INodePropertyOptions {
name: string;
value: string | number | boolean;
action?: string;
description?: string;
routing?: INodePropertyRouting;
outputConnectionType?: NodeConnectionType;
inputSchema?: any;
displayOptions?: IDisplayOptions;
// disabledOptions added for compatibility with INodeProperties and INodeCredentialDescription types
// it needs to be implemented, if needed
disabledOptions?: undefined;
}
export interface INodeListSearchItems extends INodePropertyOptions {
icon?: string;
url?: string;
}
export interface INodeListSearchResult {
results: INodeListSearchItems[];
paginationToken?: unknown;
}
export interface INodePropertyCollection {
displayName: string;
name: string;
values: INodeProperties[];
}
export interface INodePropertyValueExtractorBase {
type: string;
}
export interface INodePropertyValueExtractorRegex extends INodePropertyValueExtractorBase {
type: 'regex';
regex: string | RegExp;
}
export interface INodePropertyValueExtractorFunction {
(
this: IExecuteSingleFunctions,
value: string | NodeParameterValue,
): Promise<string | NodeParameterValue> | string;
}
export type INodePropertyValueExtractor = INodePropertyValueExtractorRegex;
export interface IParameterDependencies {
[key: string]: string[];
}
export type IParameterLabel = {
size?: 'small' | 'medium';
};
export interface ITriggerResponse {
closeFunction?: CloseFunction;
// To manually trigger the run
manualTriggerFunction?: () => Promise<void>;
// Gets added automatically at manual workflow runs resolves with
// the first emitted data
manualTriggerResponse?: Promise<INodeExecutionData[][]>;
}
export interface ExecuteWorkflowData {
executionId: string;
data: Array<INodeExecutionData[] | null>;
waitTill?: Date | null;
}
export type WebhookSetupMethodNames = 'checkExists' | 'create' | 'delete';
export namespace MultiPartFormData {
export interface File {
filepath: string;
mimetype?: string;
originalFilename?: string;
newFilename: string;
size?: number;
}
export type Request = express.Request<
{},
{},
{
data: Record<string, string | string[]>;
files: Record<string, File | File[]>;
}
>;
}
export interface SupplyData {
metadata?: IDataObject;
response: unknown;
closeFunction?: CloseFunction;
hints?: NodeExecutionHint[];
}
export type NodeOutput =
| INodeExecutionData[][]
| NodeExecutionWithMetadata[][]
| EngineRequest
| null;
export interface INodeType {
description: INodeTypeDescription;
supplyData?(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData>;
execute?(this: IExecuteFunctions, response?: EngineResponse): Promise<NodeOutput>;
/**
* A function called when a node receives a chat message. Allows it to react
* to the message before it gets executed.
*/
onMessage?(context: IExecuteFunctions, data: INodeExecutionData): Promise<NodeOutput>;
poll?(this: IPollFunctions): Promise<INodeExecutionData[][] | null>;
trigger?(this: ITriggerFunctions): Promise<ITriggerResponse | undefined>;
webhook?(this: IWebhookFunctions): Promise<IWebhookResponseData>;
methods?: {
loadOptions?: {
[key: string]: (this: ILoadOptionsFunctions) => Promise<INodePropertyOptions[]>;
};
listSearch?: {
[key: string]: (
this: ILoadOptionsFunctions,
filter?: string,
paginationToken?: string,
) => Promise<INodeListSearchResult>;
};
credentialTest?: {
// Contains a group of functions that test credentials.
[functionName: string]: ICredentialTestFunction;
};
resourceMapping?: {
[functionName: string]: (this: ILoadOptionsFunctions) => Promise<ResourceMapperFields>;
};
localResourceMapping?: {
[functionName: string]: (this: ILocalLoadOptionsFunctions) => Promise<ResourceMapperFields>;
};
actionHandler?: {
[functionName: string]: (
this: ILoadOptionsFunctions,
payload: IDataObject | string | undefined,
) => Promise<NodeParameterValueType>;
};
};
webhookMethods?: {
[name in WebhookType]?: {
[method in WebhookSetupMethodNames]: (this: IHookFunctions) => Promise<boolean>;
};
};
/**
* Defines custom operations for nodes that do not implement an `execute` method, such as declarative nodes.
* This function will be invoked instead of `execute` for a specific resource and operation.
* Should be either `execute` or `customOperations` defined for a node, but not both.
*
* @property customOperations - Maps specific resource and operation to a custom function
*/
customOperations?: {
[resource: string]: {
[operation: string]: (this: IExecuteFunctions) => Promise<NodeOutput>;
};
};
}
/**
* Represents a request to execute a specific node and receive the result back.
* This action tells the engine to execute the specified node with the provided input
* and then call back the requesting node with the execution result.
*
* @template T - The type of metadata associated with this action
*/
type ExecuteNodeAction<T> = {
/** The type identifier for this action */
actionType: 'ExecutionNodeAction';
/** The name of the node to be executed */
nodeName: string;
/** Input data to be passed to the node for execution */
input: IDataObject;
/** The type of connection this execution request uses */
type: NodeConnectionType;
/** Unique identifier for this execution request */
id: string;
/** Additional metadata for this execution request */
metadata: T;
};
/**
* Union type of all possible actions that nodes can request from the workflow engine.
* Currently only contains ExecuteNodeAction, but will be extended with additional
* action types as they are implemented.
*
* @template T - The type of metadata associated with this action
*/
type EngineAction<T = unknown> = ExecuteNodeAction<T>;
/**
* A collection of actions that a node wants the engine to fulfill and call back with results.
* The requesting node sends this to the engine and expects to receive an EngineResponse
* containing the results of all requested actions.
*
* @template T - The type of metadata associated with this request
*
* @todo This should use `unknown`, but jest-mock-extended will turn this into
* `Partial<unknown>` which `unknown` cannot be assigned to, which leads to a
* lot of type errors in our tests.
* The correct fix is to make a PR to jest-mock-extended and make it handle
* `unknown` special, turning it into `unknown` instead of `Partial<unknown>`.
*/
export type EngineRequest<T = object> = {
/** Array of actions that the requesting node wants the engine to fulfill */
actions: Array<EngineAction<T>>;
/** Metadata associated with this request */
metadata: T;
};
/**
* Result of executing a single node action within the workflow engine.
* Contains the original action and the resulting task data.
*
* @template T - The type of metadata associated with this result
*/
export type ExecuteNodeResult<T = unknown> = {
/** The action that was executed */
action: ExecuteNodeAction<T>;
/** The resulting task data from the execution */
data: ITaskData;
};
/**
* Union type of all possible results from engine actions.
* Currently only contains ExecuteNodeResult, but will be extended with additional
* result types as new action types are implemented.
*
* @template T - The type of metadata associated with this result
*/
type EngineResult<T> = ExecuteNodeResult<T>;
/**
* Response structure returned from the workflow engine after execution.
* Contains the results of all executed actions along with associated metadata.
*
* @template T - The type of metadata associated with this response
*/
export type EngineResponse<T = unknown> = {
/** Array of results from each executed action */
actionResponses: Array<EngineResult<T>>;
/** Metadata associated with this response */
metadata: T;
};
/**
* This class serves as the base for all nodes using the new context API
* having this as a class enables us to identify these instances at runtime
*/
export abstract class Node {
abstract description: INodeTypeDescription;
execute?(
context: IExecuteFunctions,
response?: EngineResponse,
): Promise<INodeExecutionData[][] | EngineRequest>;
webhook?(context: IWebhookFunctions): Promise<IWebhookResponseData>;
poll?(context: IPollFunctions): Promise<INodeExecutionData[][] | null>;
}
export interface IVersionedNodeType {
nodeVersions: {
[key: number]: INodeType;
};
currentVersion: number;
description: INodeTypeBaseDescription;
getNodeType: (version?: number) => INodeType;
}
export interface INodeCredentialTestResult {
status: 'OK' | 'Error';
message: string;
}
export interface INodeCredentialTestRequest {
credentials: ICredentialsDecrypted;
}
export interface INodeCredentialDescription {
name: string;
required?: boolean;
displayName?: string;
disabledOptions?: ICredentialsDisplayOptions;
displayOptions?: ICredentialsDisplayOptions;
testedBy?: ICredentialTestRequest | string; // Name of a function inside `loadOptions.credentialTest`
}
export type INodeIssueTypes = 'credentials' | 'execution' | 'input' | 'parameters' | 'typeUnknown';
export interface INodeIssueObjectProperty {
[key: string]: string[];
}
export interface INodeIssueData {
node: string;
type: INodeIssueTypes;
value: null | boolean | string | string[] | INodeIssueObjectProperty;
}
export interface INodeIssues {
execution?: boolean;
credentials?: INodeIssueObjectProperty;
input?: INodeIssueObjectProperty;
parameters?: INodeIssueObjectProperty;
typeUnknown?: boolean;
[key: string]: undefined | boolean | INodeIssueObjectProperty;
}
export interface IWorkflowIssues {
[key: string]: INodeIssues;
}
export type ThemeIconColor =
| 'gray'
| 'black'
| 'blue'
| 'light-blue'
| 'dark-blue'
| 'orange'
| 'orange-red'
| 'pink-red'
| 'red'
| 'light-green'
| 'green'
| 'dark-green'
| 'azure'
| 'purple'
| 'crimson';
export type Themed<T> = T | { light: T; dark: T };
export type IconRef = `fa:${string}` | `node:${string}.${string}`;
export type IconFile = `file:${string}.png` | `file:${string}.svg` | ExpressionString;
export type Icon = IconRef | Themed<IconFile> | IconFile;
type NodeGroupType = 'input' | 'output' | 'organization' | 'schedule' | 'transform' | 'trigger';
export interface INodeTypeBaseDescription {
displayName: string;
name: string;
icon?: Icon;
iconColor?: ThemeIconColor;
iconUrl?: Themed<string>;
/**
* Base path for resolving file icons from expressions.
* Used when icon is an expression that resolves to `file:filename.svg`
* Format: `icons/${packageName}/${nodeDirPath}`
*/
iconBasePath?: string;
badgeIconUrl?: Themed<string>;
group: NodeGroupType[];
description: string;
documentationUrl?: string;
subtitle?: string;
defaultVersion?: number;
codex?: CodexData;
parameterPane?: 'wide';
/**
* Whether the node must be hidden in the node creator panel,
* due to deprecation or as a special case (e.g. Start node)
*/
hidden?: true;
/**
* Whether the node will be wrapped for tool-use by AI Agents,
* optionally replacing provided parts of the description
*/
usableAsTool?: true | UsableAsToolDescription;
}
/**
* NodeDescription entries that replace the base node entries when
* the node is used as a tool
*
* Note that the new codex is hardcoded and may not behave as expected
* without additional changes to the implementation.
*/
export type UsableAsToolDescription = {
replacements?: Partial<Omit<INodeTypeBaseDescription, 'usableAsTool'>>;
};
export interface INodePropertyRouting {
operations?: IN8nRequestOperations; // Should be changed, does not sound right
output?: INodeRequestOutput;
request?: DeclarativeRestApiSettings.HttpRequestOptions;
send?: INodeRequestSend;
}
export type PostReceiveAction =
| ((
this: IExecuteSingleFunctions,
items: INodeExecutionData[],
response: IN8nHttpFullResponse,
) => Promise<INodeExecutionData[]>)
| IPostReceiveBinaryData
| IPostReceiveFilter
| IPostReceiveLimit
| IPostReceiveRootProperty
| IPostReceiveSet
| IPostReceiveSetKeyValue
| IPostReceiveSort;
export type PreSendAction = (
this: IExecuteSingleFunctions,
requestOptions: IHttpRequestOptions,
) => Promise<IHttpRequestOptions>;
export interface INodeRequestOutput {
maxResults?: number | string;
postReceive?: PostReceiveAction[];
}
export interface INodeRequestSend {
preSend?: PreSendAction[];
paginate?: boolean | string; // Where should this life?
property?: string; // Maybe: propertyName, destinationProperty?
propertyInDotNotation?: boolean; // Enabled by default
type?: 'body' | 'query';
value?: string;
}
export interface IPostReceiveBase {
type: string;
enabled?: boolean | string;
properties: {
[key: string]: string | number | boolean | IDataObject;
};
errorMessage?: string;
}
export interface IPostReceiveBinaryData extends IPostReceiveBase {
type: 'binaryData';
properties: {
destinationProperty: string;
};
}
export interface IPostReceiveFilter extends IPostReceiveBase {
type: 'filter';
properties: {
pass: boolean | string;
};
}
export interface IPostReceiveLimit extends IPostReceiveBase {
type: 'limit';
properties: {
maxResults: number | string;
};
}
export interface IPostReceiveRootProperty extends IPostReceiveBase {
type: 'rootProperty';
properties: {
property: string;
};
}
export interface IPostReceiveSet extends IPostReceiveBase {
type: 'set';
properties: {
value: string;
};
}
export interface IPostReceiveSetKeyValue extends IPostReceiveBase {
type: 'setKeyValue';
properties: {
[key: string]: string | number;
};
}
export interface IPostReceiveSort extends IPostReceiveBase {
type: 'sort';
properties: {
key: string;
};
}
export const NodeConnectionTypes = {
AiAgent: 'ai_agent',
AiChain: 'ai_chain',
AiDocument: 'ai_document',
AiEmbedding: 'ai_embedding',
AiLanguageModel: 'ai_languageModel',
AiMemory: 'ai_memory',
AiOutputParser: 'ai_outputParser',
AiRetriever: 'ai_retriever',
AiReranker: 'ai_reranker',
AiTextSplitter: 'ai_textSplitter',
AiTool: 'ai_tool',
AiVectorStore: 'ai_vectorStore',
Main: 'main',
} as const;
export type NodeConnectionType = (typeof NodeConnectionTypes)[keyof typeof NodeConnectionTypes];
export type AINodeConnectionType = Exclude<NodeConnectionType, typeof NodeConnectionTypes.Main>;
export const nodeConnectionTypes: NodeConnectionType[] = Object.values(NodeConnectionTypes);
export interface INodeInputFilter {
// TODO: Later add more filter options like categories, subcatogries,
// regex, allow to exclude certain nodes, ... ?
// Potentially change totally after alpha/beta. Is not a breaking change after all.
nodes?: string[]; // Allowed nodes
excludedNodes?: string[];
}
export interface INodeInputConfiguration {
category?: string;
displayName?: string;
required?: boolean;
type: NodeConnectionType;
filter?: INodeInputFilter;
maxConnections?: number;
}
export interface INodeOutputConfiguration {
category?: 'error';
displayName?: string;
maxConnections?: number;
required?: boolean;
type: NodeConnectionType;
}
export type ExpressionString = `={{${string}}}`;
export type NodeDefaults = Partial<{
/**
* @deprecated Use {@link INodeTypeBaseDescription.iconColor|iconColor} instead. `iconColor` supports dark mode and uses preset colors from n8n's design system.
*/
color: string;
name: string;
}>;
export interface INodeTypeDescription extends INodeTypeBaseDescription {
version: number | number[];
defaults: NodeDefaults;
eventTriggerDescription?: string;
activationMessage?: string;
inputs: Array<NodeConnectionType | INodeInputConfiguration> | ExpressionString;
requiredInputs?: string | number[] | number; // Ony available with executionOrder => "v1"
inputNames?: string[];
outputs: Array<NodeConnectionType | INodeOutputConfiguration> | ExpressionString;
outputNames?: string[];
properties: INodeProperties[];
credentials?: INodeCredentialDescription[];
maxNodes?: number; // How many nodes of that type can be created in a workflow
polling?: true | undefined;
supportsCORS?: true | undefined;
requestDefaults?: DeclarativeRestApiSettings.HttpRequestOptions;
requestOperations?: IN8nRequestOperations;
hooks?: {
[key: string]: INodeHookDescription[] | undefined;
activate?: INodeHookDescription[];
deactivate?: INodeHookDescription[];
};
webhooks?: IWebhookDescription[];
translation?: { [key: string]: object };
mockManualExecution?: true;
triggerPanel?: TriggerPanelDefinition | boolean;
extendsCredential?: string;
hints?: NodeHint[];
communityNodePackageVersion?: string;
waitingNodeTooltip?: string;
__loadOptionsMethods?: string[]; // only for validation during build
features?: NodeFeaturesDefinition;
}
export type TriggerPanelDefinition = {
hideContent?: boolean | string;
header?: string;
executionsHelp?: string | { active: string; inactive: string };
activationHint?: string | { active: string; inactive: string };
};
export type NodeHint = {
message: string;
type?: 'info' | 'warning' | 'danger';
location?: 'outputPane' | 'inputPane' | 'ndv';
displayCondition?: string;
whenToDisplay?: 'always' | 'beforeExecution' | 'afterExecution';
};
export type NodeExecutionHint = Omit<NodeHint, 'whenToDisplay' | 'displayCondition'>;
export interface INodeHookDescription {
method: string;
}
export interface IWebhookData {
httpMethod: IHttpRequestMethods;
node: string;
path: string;
webhookDescription: IWebhookDescription;
workflowId: string;
workflowExecuteAdditionalData: IWorkflowExecuteAdditionalData;
webhookId?: string;
isTest?: boolean;
userId?: string;
staticData?: Workflow['staticData'];
}
export type WebhookType = 'default' | 'setup';
export interface IWebhookDescription {
[key: string]: IHttpRequestMethods | WebhookResponseMode | boolean | string | undefined;
httpMethod: IHttpRequestMethods | string;
isFullPath?: boolean;
name: WebhookType;
path: string;
responseBinaryPropertyName?: string;
responseContentType?: string;
responsePropertyName?: string;
responseMode?: WebhookResponseMode | string;
responseData?: WebhookResponseData | string;
restartWebhook?: boolean;
nodeType?: 'webhook' | 'form' | 'mcp';
ndvHideUrl?: string | boolean; // If true the webhook will not be displayed in the editor
ndvHideMethod?: string | boolean; // If true the method will not be displayed in the editor
}
export interface ProxyInput {
all: () => INodeExecutionData[];
context: any;
first: () => INodeExecutionData | undefined;
item: INodeExecutionData | undefined;
last: () => INodeExecutionData | undefined;
params?: INodeParameters;
}
export interface IWorkflowDataProxyData {
[key: string]: any;
$binary: INodeExecutionData['binary'];
$data: any;
$env: any;
$evaluateExpression: (expression: string, itemIndex?: number) => NodeParameterValueType;
$item: (itemIndex: number, runIndex?: number) => IWorkflowDataProxyData;
$items: (nodeName?: string, outputIndex?: number, runIndex?: number) => INodeExecutionData[];
$json: INodeExecutionData['json'];
$node: any;
$parameter: INodeParameters;
$position: number;
$workflow: any;
$: any;
$input: ProxyInput;
$thisItem: any;
$thisRunIndex: number;
$thisItemIndex: number;
$now: any;
$today: any;
$getPairedItem: (
destinationNodeName: string,
incomingSourceData: ISourceData | null,
pairedItem: IPairedItemData,
) => INodeExecutionData | null;
constructor: any;
}
export type IWorkflowDataProxyAdditionalKeys = IDataObject & {
$execution?: {
id: string;
mode: 'test' | 'production';
resumeUrl: string;
resumeFormUrl: string;
customData?: {
set(key: string, value: string): void;
setAll(obj: Record<string, string>): void;
get(key: string): string;
getAll(): Record<string, string>;
};
};
$vars?: IDataObject;
$secrets?: IDataObject;
$pageCount?: number;
/** @deprecated */
$executionId?: string;
/** @deprecated */
$resumeWebhookUrl?: string;
};
export interface IWorkflowMetadata {
id?: string;
name?: string;
active: boolean;
}
export interface IWebhookResponseData {
workflowData?: INodeExecutionData[][];
webhookResponse?: any;
noWebhookResponse?: boolean;
}
export type WebhookResponseData = 'allEntries' | 'firstEntryJson' | 'firstEntryBinary' | 'noData';
/**
* Defines how and when response should be sent:
*
* onReceived: Response is sent immidiatly after node done executing
*
* lastNode: Response is sent after the last node finishes executing
*
* responseNode: Response is sent from the Responde to Webhook node
*
* formPage: Special response with executionId sent to the form trigger node
*
* hostedChat: Special response with executionId sent to the hosted chat trigger node
*
* streaming: Response added to runData to httpResponse and streamingEnabled set to true
*/
export type WebhookResponseMode =
| 'onReceived'
| 'lastNode'
| 'responseNode'
| 'formPage'
| 'hostedChat'
| 'streaming';
export interface INodeTypes {
getByName(nodeType: string): INodeType | IVersionedNodeType;
getByNameAndVersion(nodeType: string, version?: number): INodeType;
getKnownTypes(): IDataObject;
}
export type LoadingDetails = {
className: string;
sourcePath: string;
};
export type CredentialLoadingDetails = LoadingDetails & {
supportedNodes?: string[];
extends?: string[];
};
export type NodeLoadingDetails = LoadingDetails;
export type KnownNodesAndCredentials = {
nodes: Record<string, NodeLoadingDetails>;
credentials: Record<string, CredentialLoadingDetails>;
};
export interface LoadedNodesAndCredentials {
nodes: INodeTypeData;
credentials: ICredentialTypeData;
}
export interface LoadedClass<T> {
sourcePath: string;
type: T;
}
type LoadedData<T> = Record<string, LoadedClass<T>>;
export type ICredentialTypeData = LoadedData<ICredentialType>;
export type INodeTypeData = LoadedData<INodeType | IVersionedNodeType>;
export interface IRun {
data: IRunExecutionData;
/**
* @deprecated Use status instead
*/
finished?: boolean;
mode: WorkflowExecuteMode;
waitTill?: Date | null;
startedAt: Date;
stoppedAt?: Date;
status: ExecutionStatus;
/** ID of the job this execution belongs to. Only in scaling mode. */
jobId?: string;
}
export type SchemaType =
| 'string'
| 'number'
| 'boolean'
| 'bigint'
| 'symbol'
| 'array'
| 'object'
| 'function'
| 'null'
| 'undefined';
export type Schema = { type: SchemaType; key?: string; value: string | Schema[]; path: string };
export interface NodeExecutionSchema {
nodeName: string;
schema: Schema;
}
export interface IRunData {
// node-name: result-data
[key: string]: ITaskData[];
}
export interface ITaskSubRunMetadata {
node: string;
runIndex: number;
}
export interface RelatedExecution {
executionId: string;
workflowId: string;
// In the case of a parent execution, whether the parent should be resumed when the sub execution finishes.
shouldResume?: boolean;
executionContext?: IExecutionContext;
}
type SubNodeExecutionDataAction = {
nodeName: string;
runIndex: number;
action: EngineRequest['actions'][number];
response?: object;
};
export interface ITaskMetadata {
subRun?: ITaskSubRunMetadata[];
parentExecution?: RelatedExecution;
subExecution?: RelatedExecution;
subExecutionsCount?: number;
subNodeExecutionData?: {
actions: SubNodeExecutionDataAction[];
metadata: object;
};
/**
* When true, preserves sourceOverwrite information in pairedItem data during node execution.
* This is used for AI tool nodes to maintain correct expression resolution context, allowing
* tools to access data from nodes earlier in the workflow chain via $() expressions.
*/
preserveSourceOverwrite?: boolean;
preservedSourceOverwrite?: ISourceData;
/**
* Indicates that this node execution is resuming from a previous pause (e.g., AI agent
* resuming after tool execution). When true, the nodeExecuteBefore hook is skipped to
* prevent duplicate event emissions that would cause UI state issues.
* @see AI-1414
*/
nodeWasResumed?: boolean;
/**
* Time saved by this workflow execution in minutes. Used by SavedTime nodes to track
* dynamic time savings that can be calculated based on execution data (e.g., number of
* items processed). The behavior determines how this value interacts with the workflow's
* default timeSavedPerExecution setting.
*/
timeSaved?: {
/** Time saved in minutes */
minutes: number;
};
}
/** The data that gets returned when a node execution starts */
export interface ITaskStartedData {
startTime: number;
/** This index tracks the order in which nodes are executed */
executionIndex: number;
source: Array<ISourceData | null>; // Is an array as nodes have multiple inputs
hints?: NodeExecutionHint[];
}
/** The data that gets returned when a node execution ends */
export interface ITaskData extends ITaskStartedData {
executionTime: number;
executionStatus?: ExecutionStatus;
data?: ITaskDataConnections;
inputOverride?: ITaskDataConnections;
error?: ExecutionError;
metadata?: ITaskMetadata;
}
export interface ISourceData {
previousNode: string;
previousNodeOutput?: number; // If undefined "0" gets used
previousNodeRun?: number; // If undefined "0" gets used
}
export interface StartNodeData {
name: string;
sourceData: ISourceData | null;
}
// The data for all the different kind of connections (like main) and all the indexes
export interface ITaskDataConnections {
// Key for each input type and because there can be multiple inputs of the same type it is an array
// null is also allowed because if we still need data for a later while executing the workflow set temporary to null
// the nodes get as input TaskDataConnections which is identical to this one except that no null is allowed.
[key: string]: Array<INodeExecutionData[] | null>;
}
// Keeps data while workflow gets executed and allows when provided to restart execution
export interface IWaitingForExecution {
// Node name
[key: string]: {
// Run index
[key: number]: ITaskDataConnections;
};
}
export interface ITaskDataConnectionsSource {
// Key for each input type and because there can be multiple inputs of the same type it is an array
// null is also allowed because if we still need data for a later while executing the workflow set temporary to null
// the nodes get as input TaskDataConnections which is identical to this one except that no null is allowed.
[key: string]: Array<ISourceData | null>;
}
export interface IWaitingForExecutionSource {
// Node name
[key: string]: {
// Run index
[key: number]: ITaskDataConnectionsSource;
};
}
export type WorkflowId = IWorkflowBase['id'];
export interface IWorkflowBase {
id: string;
name: string;
description?: string | null;
active: boolean;
isArchived: boolean;
createdAt: Date;
startedAt?: Date;
updatedAt: Date;
nodes: INode[];
connections: IConnections;
settings?: IWorkflowSettings;
staticData?: IDataObject;
pinData?: IPinData;
versionId?: string;
activeVersionId: string | null;
activeVersion?: IWorkflowHistory | null;
versionCounter?: number;
meta?: WorkflowFEMeta;
}
interface IWorkflowHistory {
versionId: string;
workflowId: string;
nodes: INode[];
connections: IConnections;
authors: string;
name: string | null;
description: string | null;
createdAt: Date;
updatedAt: Date;
}
export interface IWorkflowCredentials {
[credentialType: string]: {
[id: string]: ICredentialsEncrypted;
};
}
export interface IDestinationNode {
nodeName: string;
/**
* Execution mode for the destination node:
* - 'inclusive': Execute up to and including the destination node
* - 'exclusive': Execute up to but excluding the destination node
*/
mode: 'inclusive' | 'exclusive';
}
export interface IWorkflowExecutionDataProcess {
destinationNode?: IDestinationNode;
restartExecutionId?: string;
executionMode: WorkflowExecuteMode;
/**
* The data that is sent in the body of the webhook that started this
* execution.
*/
executionData?: IRunExecutionData;
runData?: IRunData;
pinData?: IPinData;
retryOf?: string | null;
pushRef?: string;
startNodes?: StartNodeData[];
workflowData: IWorkflowBase;
userId?: string;
projectId?: string;
dirtyNodeNames?: string[];
triggerToStartFrom?: {
name: string;
data?: ITaskData;
};
agentRequest?: AiAgentRequest;
httpResponse?: express.Response; // Used for streaming responses
streamingEnabled?: boolean;
startedAt?: Date;
}
export interface ExecuteWorkflowOptions {
node?: INode;
parentWorkflowId: string;
inputData?: INodeExecutionData[];
loadedWorkflowData?: IWorkflowBase;
loadedRunData?: IWorkflowExecutionDataProcess;
parentWorkflowSettings?: IWorkflowSettings;
parentCallbackManager?: CallbackManager;
doNotWaitToFinish?: boolean;
parentExecution?: RelatedExecution;
executionMode?: WorkflowExecuteMode;
}
export type AiEvent =
| 'ai-messages-retrieved-from-memory'
| 'ai-message-added-to-memory'
| 'ai-output-parsed'
| 'ai-documents-retrieved'
| 'ai-document-reranked'
| 'ai-document-embedded'
| 'ai-query-embedded'
| 'ai-document-processed'
| 'ai-text-split'
| 'ai-tool-called'
| 'ai-vector-store-searched'
| 'ai-llm-generated-output'
| 'ai-llm-errored'
| 'ai-vector-store-populated'
| 'ai-vector-store-updated';
type AiEventPayload = {
msg: string;
workflowName: string;
executionId: string;
nodeName: string;
workflowId?: string;
nodeType?: string;
};
// Used to transport an agent request for partial execution
export interface AiAgentRequest {
query: string | INodeParameters;
tool: {
name: string;
};
}
export interface IWorkflowExecuteAdditionalData {
credentialsHelper: ICredentialsHelper;
executeWorkflow: (
workflowInfo: IExecuteWorkflowInfo,
additionalData: IWorkflowExecuteAdditionalData,
options: ExecuteWorkflowOptions,
) => Promise<ExecuteWorkflowData>;
getRunExecutionData: (executionId: string) => Promise<IRunExecutionData | undefined>;
executionId?: string;
restartExecutionId?: string;
currentNodeExecutionIndex: number;
httpResponse?: express.Response;
httpRequest?: express.Request;
streamingEnabled?: boolean;
restApiUrl: string;
instanceBaseUrl: string;
setExecutionStatus?: (status: ExecutionStatus) => void;
sendDataToUI?: (type: string, data: IDataObject | IDataObject[]) => void;
formWaitingBaseUrl: string;
webhookBaseUrl: string;
webhookWaitingBaseUrl: string;
webhookTestBaseUrl: string;
currentNodeParameters?: INodeParameters;
executionTimeoutTimestamp?: number;
userId?: string;
variables: IDataObject;
logAiEvent: (eventName: AiEvent, payload: AiEventPayload) => void;
parentCallbackManager?: CallbackManager;
startRunnerTask<T, E = unknown>(
additionalData: IWorkflowExecuteAdditionalData,
jobType: string,
settings: unknown,
executeFunctions: IExecuteFunctions,
inputData: ITaskDataConnections,
node: INode,
workflow: Workflow,
runExecutionData: IRunExecutionData,
runIndex: number,
itemIndex: number,
activeNodeName: string,
connectionInputData: INodeExecutionData[],
siblingParameters: INodeParameters,
mode: WorkflowExecuteMode,
envProviderState: EnvProviderState,
executeData?: IExecuteData,
): Promise<Result<T, E>>;
getRunnerStatus?(taskType: string): { available: true } | { available: false; reason?: string };
}
export type WorkflowActivateMode =
| 'init'
| 'create' // unused
| 'update'
| 'activate'
| 'manual' // unused
| 'leadershipChange';
export namespace WorkflowSettings {
export type CallerPolicy = 'any' | 'none' | 'workflowsFromAList' | 'workflowsFromSameOwner';
export type SaveDataExecution = 'DEFAULT' | 'all' | 'none';
}
export type WorkflowSettingsBinaryMode = typeof BINARY_MODE_SEPARATE | typeof BINARY_MODE_COMBINED;
export interface IWorkflowSettings {
timezone?: 'DEFAULT' | string;
errorWorkflow?: string;
callerIds?: string;
callerPolicy?: WorkflowSettings.CallerPolicy;
saveDataErrorExecution?: WorkflowSettings.SaveDataExecution;
saveDataSuccessExecution?: WorkflowSettings.SaveDataExecution;
saveManualExecutions?: 'DEFAULT' | boolean;
saveExecutionProgress?: 'DEFAULT' | boolean;
executionTimeout?: number;
executionOrder?: 'v0' | 'v1';
binaryMode?: WorkflowSettingsBinaryMode;
timeSavedPerExecution?: number;
timeSavedMode?: 'fixed' | 'dynamic';
availableInMCP?: boolean;
credentialResolverId?: string;
}
export interface WorkflowFEMeta {
onboardingId?: string;
templateId?: string;
instanceId?: string;
templateCredsSetupCompleted?: boolean;
}
export interface WorkflowTestData {
description: string;
input: {
workflowData: {
nodes: INode[];
connections: IConnections;
settings?: IWorkflowSettings;
};
};
output: {
assertBinaryData?: boolean;
nodeExecutionOrder?: string[];
nodeExecutionStack?: IExecuteData[];
testAllOutputs?: boolean;
nodeData: {
[key: string]: any[][];
};
error?: string;
};
nock?: {
baseUrl: string;
mocks: Array<{
method: 'delete' | 'get' | 'patch' | 'post' | 'put';
path: string;
requestBody?: RequestBodyMatcher;
requestHeaders?: Record<string, RequestHeaderMatcher>;
statusCode: number;
responseBody: string | object;
responseHeaders?: ReplyHeaders;
}>;
};
trigger?: {
mode: WorkflowExecuteMode;
input: INodeExecutionData;
};
credentials?: Record<string, ICredentialDataDecryptedObject>;
}
export type LogLevel = (typeof LOG_LEVELS)[number];
export type LogMetadata = {
[key: string]: unknown;
scopes?: LogScope[];
file?: string;
function?: string;
};
export type Logger = Record<
Exclude<LogLevel, 'silent'>,
(message: string, metadata?: LogMetadata) => void
>;
export type LogLocationMetadata = Pick<LogMetadata, 'file' | 'function'>;
export interface IStatusCodeMessages {
[key: string]: string;
}
export type DocumentationLink = {
url: string;
};
export type CodexData = {
categories?: string[];
subcategories?: { [category: string]: string[] };
resources?: {
credentialDocumentation?: DocumentationLink[];
primaryDocumentation?: DocumentationLink[];
};
alias?: string[];
};
export type JsonValue = string | number | boolean | null | JsonObject | JsonValue[];
export type JsonObject = { [key: string]: JsonValue };
export type AllEntities<M> = M extends { [key: string]: string } ? Entity<M, keyof M> : never;
export type Entity<M, K> = K extends keyof M ? { resource: K; operation: M[K] } : never;
export type PropertiesOf<M extends { resource: string; operation: string }> = Array<
Omit<INodeProperties, 'displayOptions'> & {
displayOptions?: {
[key in 'show' | 'hide']?: {
resource?: Array<M['resource']>;
operation?: Array<M['operation']>;
[otherKey: string]: Array<NodeParameterValue | DisplayCondition> | undefined;
};
};
}
>;
// Telemetry
export interface ITelemetryTrackProperties {
user_id?: string;
[key: string]: GenericValue;
}
export interface INodesGraph {
node_types: string[];
node_connections: IDataObject[];
nodes: INodesGraphNode;
notes: INotesGraphNode;
is_pinned: boolean;
}
export interface INodesGraphNode {
[key: string]: INodeGraphItem;
}
export interface INotesGraphNode {
[key: string]: INoteGraphItem;
}
export interface INoteGraphItem {
overlapping: boolean;
position: [number, number];
height: number;
width: number;
}
export interface INodeGraphItem {
id: string;
type: string;
version?: number;
resource?: string;
operation?: string;
domain?: string; // HTTP Request node v1
domain_base?: string; // HTTP Request node v2
domain_path?: string; // HTTP Request node v2
position: [number, number];
mode?: string;
credential_type?: string; // HTTP Request node v2
credential_set?: boolean; // HTTP Request node v2
method?: string; // HTTP Request node v2
src_node_id?: string;
src_instance_id?: string;
agent?: string; //@n8n/n8n-nodes-langchain.agent
is_streaming?: boolean; //@n8n/n8n-nodes-langchain.agent
prompts?: IDataObject[] | IDataObject; //ai node's prompts, cloud only
use_responses_api?: boolean; //@n8n/n8n-nodes-langchain.lmChatOpenAi
toolSettings?: IDataObject; //various langchain tool's settings
sql?: string; //merge node combineBySql, cloud only
workflow_id?: string; //@n8n/n8n-nodes-langchain.toolWorkflow and n8n-nodes-base.executeWorkflow
response_mode?: string; // @n8n/n8n-nodes-langchain.chatTrigger, n8n-nodes-base.webhook selected response mode
public_chat?: boolean; // @n8n/n8n-nodes-langchain.chatTrigger
runs?: number;
items_total?: number;
metric_names?: string[];
language?: string; // only for Code node: 'javascript' or 'python' or 'pythonNative'
package_version?: string; // only for community nodes
used_guardrails?: string[]; // only for @n8n/n8n-nodes-langchain.guardrails
}
export interface INodeNameIndex {
[name: string]: string;
}
export interface INodesGraphResult {
nodeGraph: INodesGraph;
nameIndices: INodeNameIndex;
webhookNodeNames: string[];
evaluationTriggerNodeNames: string[];
}
export interface FeatureFlags {
[featureFlag: string]: string | boolean | undefined;
}
export interface IConnectedNode {
name: string;
indicies: number[];
depth: number;
}
export type PublicInstalledPackage = {
packageName: string;
installedVersion: string;
authorName?: string;
authorEmail?: string;
installedNodes: PublicInstalledNode[];
createdAt: Date;
updatedAt: Date;
updateAvailable?: string;
failedLoading?: boolean;
};
export type PublicInstalledNode = {
name: string;
type: string;
latestVersion: number;
package: PublicInstalledPackage;
};
export interface NodeExecutionWithMetadata extends INodeExecutionData {
pairedItem: IPairedItemData | IPairedItemData[];
}
export type AnnotationVote = 'up' | 'down';
export interface ExecutionSummary {
id: string;
/**
* @deprecated Use status instead
*/
finished?: boolean;
mode: WorkflowExecuteMode;
retryOf?: string | null;
retrySuccessId?: string | null;
waitTill?: Date;
createdAt: Date;
startedAt: Date | null;
stoppedAt?: Date;
workflowId: string;
workflowName?: string;
status: ExecutionStatus;
lastNodeExecuted?: string;
executionError?: ExecutionError;
nodeExecutionStatus?: {
[key: string]: IExecutionSummaryNodeExecutionResult;
};
annotation?: {
vote: AnnotationVote;
tags: Array<{
id: string;
name: string;
}>;
};
}
export interface IExecutionSummaryNodeExecutionResult {
executionStatus: ExecutionStatus;
errors?: Array<{
name?: string;
message?: string;
description?: string;
}>;
}
export interface ResourceMapperFields {
fields: ResourceMapperField[];
emptyFieldsNotice?: string;
}
export interface WorkflowInputsData {
fields: ResourceMapperField[];
dataMode: string;
subworkflowInfo?: { workflowId?: string; triggerId?: string };
}
export interface ResourceMapperField {
id: string;
displayName: string;
defaultMatch: boolean;
canBeUsedToMatch?: boolean;
required: boolean;
display: boolean;
type?: FieldType;
removed?: boolean;
options?: INodePropertyOptions[];
readOnly?: boolean;
defaultValue?: string | number | boolean | null;
}
export type FormFieldsParameter = Array<{
fieldLabel: string;
elementName?: string;
fieldType?: string;
requiredField?: boolean;
fieldOptions?: { values: Array<{ option: string }> };
multiselect?: boolean;
multipleFiles?: boolean;
acceptFileTypes?: string;
formatDate?: string;
html?: string;
placeholder?: string;
defaultValue?: string;
fieldName?: string;
fieldValue?: string;
limitSelection?: 'exact' | 'range' | 'unlimited';
numberOfSelections?: number;
minSelections?: number;
maxSelections?: number;
}>;
export type FieldTypeMap = {
// eslint-disable-next-line id-denylist
boolean: boolean;
// eslint-disable-next-line id-denylist
number: number;
// eslint-disable-next-line id-denylist
string: string;
'string-alphanumeric': string;
dateTime: string;
time: string;
array: unknown[];
object: object;
options: any;
url: string;
jwt: string;
'form-fields': FormFieldsParameter;
binary: string;
};
export type FieldType = keyof FieldTypeMap;
export type ValidationResult<T extends FieldType = FieldType> =
| { valid: false; errorMessage: string }
| {
valid: true;
newValue?: FieldTypeMap[T];
};
export type ResourceMapperValue = {
mappingMode: string;
value: { [key: string]: string | number | boolean | null } | null;
matchingColumns: string[];
schema: ResourceMapperField[];
attemptToConvertTypes: boolean;
convertFieldsToString: boolean;
};
export type FilterOperatorType =
| 'string'
| 'number'
| 'boolean'
| 'array'
| 'object'
| 'dateTime'
| 'any';
export interface FilterOperatorValue {
type: FilterOperatorType;
operation: string;
rightType?: FilterOperatorType;
singleValue?: boolean; // default = false
}
export type FilterConditionValue = {
id: string;
leftValue: NodeParameterValue | NodeParameterValue[];
operator: FilterOperatorValue;
rightValue: NodeParameterValue | NodeParameterValue[];
};
export type FilterOptionsValue = {
caseSensitive: boolean;
leftValue: string;
typeValidation: 'strict' | 'loose';
version: 1 | 2 | 3;
};
export type FilterValue = {
options: FilterOptionsValue;
conditions: FilterConditionValue[];
combinator: FilterTypeCombinator;
};
export type AssignmentCollectionValue = {
assignments: AssignmentValue[];
};
export type AssignmentValue = {
id: string;
name: string;
value: NodeParameterValue;
type?: string;
};
export interface ExecutionOptions {
limit?: number;
}
export interface ExecutionFilters {
/**
* @deprecated Use status instead
*/
finished?: boolean;
mode?: WorkflowExecuteMode[];
retryOf?: string;
retrySuccessId?: string;
status?: ExecutionStatus[];
waitTill?: boolean;
workflowId?: number | string;
}
export type NpsSurveyRespondedState = { lastShownAt: number; responded: true };
export type NpsSurveyWaitingState = {
lastShownAt: number;
waitingForResponse: true;
ignoredCount: number;
};
export type NpsSurveyState = NpsSurveyRespondedState | NpsSurveyWaitingState;
export interface IUserSettings {
isOnboarded?: boolean;
firstSuccessfulWorkflowId?: string;
userActivated?: boolean;
userActivatedAt?: number;
allowSSOManualLogin?: boolean;
npsSurvey?: NpsSurveyState;
easyAIWorkflowOnboarded?: boolean;
userClaimedAiCredits?: boolean;
dismissedCallouts?: Record<string, boolean>;
}
export interface IProcessedDataConfig {
availableModes: string;
mode: string;
}
export interface IDataDeduplicator {
checkProcessedAndRecord(
items: DeduplicationItemTypes[],
context: DeduplicationScope,
contextData: ICheckProcessedContextData,
options: ICheckProcessedOptions,
): Promise<IDeduplicationOutput>;
removeProcessed(
items: DeduplicationItemTypes[],
context: DeduplicationScope,
contextData: ICheckProcessedContextData,
options: ICheckProcessedOptions,
): Promise<void>;
clearAllProcessedItems(
context: DeduplicationScope,
contextData: ICheckProcessedContextData,
options: ICheckProcessedOptions,
): Promise<void>;
getProcessedDataCount(
context: DeduplicationScope,
contextData: ICheckProcessedContextData,
options: ICheckProcessedOptions,
): Promise<number>;
}
export interface ICheckProcessedContextData {
node?: INode;
workflow: {
id: string;
active: boolean;
};
}
export type N8nAIProviderType = 'openai' | 'unknown';
export type Functionality = 'regular' | 'configuration-node' | 'pairedItem';
export type CallbackManager = CallbackManagerLC;
export type IPersonalizationSurveyAnswersV4 = {
version: 'v4';
personalization_survey_submitted_at: string;
personalization_survey_n8n_version: string;
automationGoalDevops?: string[] | null;
automationGoalDevopsOther?: string | null;
companyIndustryExtended?: string[] | null;
otherCompanyIndustryExtended?: string[] | null;
companySize?: string | null;
companyType?: string | null;
automationGoalSm?: string[] | null;
automationGoalSmOther?: string | null;
usageModes?: string[] | null;
email?: string | null;
role?: string | null;
roleOther?: string | null;
reportedSource?: string | null;
reportedSourceOther?: string | null;
};
export type ChunkType = 'begin' | 'item' | 'end' | 'error';
export interface StructuredChunk {
type: ChunkType;
content?: string;
metadata: {
nodeId: string;
nodeName: string;
runIndex: number;
itemIndex: number;
timestamp: number;
};
}
export type ApiKeyAudience = 'public-api' | 'mcp-server-api';