refactor: Move runner types to runner package (#11552)

This commit is contained in:
Tomi Turtiainen 2024-11-05 11:32:15 +02:00 committed by GitHub
parent 6854f94875
commit 3edecffd71
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 376 additions and 566 deletions

View file

@ -17,10 +17,22 @@
}, },
"main": "dist/start.js", "main": "dist/start.js",
"module": "src/start.ts", "module": "src/start.ts",
"types": "dist/start.d.ts", "types": "dist/index.d.ts",
"files": [ "files": [
"dist/**/*" "dist/**/*"
], ],
"exports": {
"./start": {
"require": "./dist/start.js",
"import": "./src/start.ts",
"types": "./dist/start.d.ts"
},
".": {
"require": "./dist/index.js",
"import": "./src/index.ts",
"types": "./dist/index.d.ts"
}
},
"dependencies": { "dependencies": {
"@n8n/config": "workspace:*", "@n8n/config": "workspace:*",
"acorn": "8.14.0", "acorn": "8.14.0",

View file

@ -1,2 +1,3 @@
export * from './task-runner'; export * from './task-runner';
export * from './runner-types'; export * from './runner-types';
export * from './message-types';

View file

@ -1,4 +1,4 @@
import type { N8nMessage } from '../../runner-types'; import type { BrokerMessage } from '@/message-types';
/** /**
* Class to keep track of which built-in variables are accessed in the code * Class to keep track of which built-in variables are accessed in the code
@ -53,7 +53,7 @@ export class BuiltInsParserState {
this.needs$prevNode = true; this.needs$prevNode = true;
} }
toDataRequestParams(): N8nMessage.ToRequester.TaskDataRequest['requestParams'] { toDataRequestParams(): BrokerMessage.ToRequester.TaskDataRequest['requestParams'] {
return { return {
dataOfNodes: this.needsAllNodes ? 'all' : Array.from(this.neededNodeNames), dataOfNodes: this.needsAllNodes ? 'all' : Array.from(this.neededNodeNames),
env: this.needs$env, env: this.needs$env,

View file

@ -0,0 +1,204 @@
import type { INodeTypeBaseDescription } from 'n8n-workflow';
import type { RPC_ALLOW_LIST, TaskDataRequestParams, TaskResultData } from './runner-types';
export namespace BrokerMessage {
export namespace ToRunner {
export interface InfoRequest {
type: 'broker:inforequest';
}
export interface RunnerRegistered {
type: 'broker:runnerregistered';
}
export interface TaskOfferAccept {
type: 'broker:taskofferaccept';
taskId: string;
offerId: string;
}
export interface TaskCancel {
type: 'broker:taskcancel';
taskId: string;
reason: string;
}
export interface TaskSettings {
type: 'broker:tasksettings';
taskId: string;
settings: unknown;
}
export interface RPCResponse {
type: 'broker:rpcresponse';
callId: string;
taskId: string;
status: 'success' | 'error';
data: unknown;
}
export interface TaskDataResponse {
type: 'broker:taskdataresponse';
taskId: string;
requestId: string;
data: unknown;
}
export interface NodeTypes {
type: 'broker:nodetypes';
nodeTypes: INodeTypeBaseDescription[];
}
export type All =
| InfoRequest
| TaskOfferAccept
| TaskCancel
| TaskSettings
| RunnerRegistered
| RPCResponse
| TaskDataResponse
| NodeTypes;
}
export namespace ToRequester {
export interface TaskReady {
type: 'broker:taskready';
requestId: string;
taskId: string;
}
export interface TaskDone {
type: 'broker:taskdone';
taskId: string;
data: TaskResultData;
}
export interface TaskError {
type: 'broker:taskerror';
taskId: string;
error: unknown;
}
export interface TaskDataRequest {
type: 'broker:taskdatarequest';
taskId: string;
requestId: string;
requestParams: TaskDataRequestParams;
}
export interface RPC {
type: 'broker:rpc';
callId: string;
taskId: string;
name: (typeof RPC_ALLOW_LIST)[number];
params: unknown[];
}
export type All = TaskReady | TaskDone | TaskError | TaskDataRequest | RPC;
}
}
export namespace RequesterMessage {
export namespace ToBroker {
export interface TaskSettings {
type: 'requester:tasksettings';
taskId: string;
settings: unknown;
}
export interface TaskCancel {
type: 'requester:taskcancel';
taskId: string;
reason: string;
}
export interface TaskDataResponse {
type: 'requester:taskdataresponse';
taskId: string;
requestId: string;
data: unknown;
}
export interface RPCResponse {
type: 'requester:rpcresponse';
taskId: string;
callId: string;
status: 'success' | 'error';
data: unknown;
}
export interface TaskRequest {
type: 'requester:taskrequest';
requestId: string;
taskType: string;
}
export type All = TaskSettings | TaskCancel | RPCResponse | TaskDataResponse | TaskRequest;
}
}
export namespace RunnerMessage {
export namespace ToBroker {
export interface Info {
type: 'runner:info';
name: string;
types: string[];
}
export interface TaskAccepted {
type: 'runner:taskaccepted';
taskId: string;
}
export interface TaskRejected {
type: 'runner:taskrejected';
taskId: string;
reason: string;
}
export interface TaskDone {
type: 'runner:taskdone';
taskId: string;
data: TaskResultData;
}
export interface TaskError {
type: 'runner:taskerror';
taskId: string;
error: unknown;
}
export interface TaskOffer {
type: 'runner:taskoffer';
offerId: string;
taskType: string;
validFor: number;
}
export interface TaskDataRequest {
type: 'runner:taskdatarequest';
taskId: string;
requestId: string;
requestParams: TaskDataRequestParams;
}
export interface RPC {
type: 'runner:rpc';
callId: string;
taskId: string;
name: (typeof RPC_ALLOW_LIST)[number];
params: unknown[];
}
export type All =
| Info
| TaskDone
| TaskError
| TaskAccepted
| TaskRejected
| TaskOffer
| RPC
| TaskDataRequest;
}
}

View file

@ -1,216 +1,90 @@
import type { INodeExecutionData, INodeTypeBaseDescription } from 'n8n-workflow'; import type {
EnvProviderState,
IDataObject,
IExecuteData,
IExecuteFunctions,
INode,
INodeExecutionData,
INodeParameters,
IRunExecutionData,
ITaskDataConnections,
IWorkflowExecuteAdditionalData,
Workflow,
WorkflowExecuteMode,
WorkflowParameters,
} from 'n8n-workflow';
/**
* Specifies what data should be included for a task data request.
*/
export interface TaskDataRequestParams { export interface TaskDataRequestParams {
dataOfNodes: string[] | 'all'; dataOfNodes: string[] | 'all';
prevNode: boolean; prevNode: boolean;
/** Whether input data for the node should be included */
input: boolean; input: boolean;
/** Whether env provider's state should be included */
env: boolean; env: boolean;
} }
export interface DataRequestResponse {
workflow: Omit<WorkflowParameters, 'nodeTypes'>;
inputData: ITaskDataConnections;
node: INode;
runExecutionData: IRunExecutionData;
runIndex: number;
itemIndex: number;
activeNodeName: string;
connectionInputData: INodeExecutionData[];
siblingParameters: INodeParameters;
mode: WorkflowExecuteMode;
envProviderState: EnvProviderState;
executeData?: IExecuteData;
defaultReturnRunIndex: number;
selfData: IDataObject;
contextNodeName: string;
additionalData: PartialAdditionalData;
}
export interface TaskResultData { export interface TaskResultData {
result: INodeExecutionData[]; result: INodeExecutionData[];
customData?: Record<string, string>; customData?: Record<string, string>;
} }
export namespace N8nMessage { export interface TaskData {
export namespace ToRunner { executeFunctions: IExecuteFunctions;
export interface InfoRequest { inputData: ITaskDataConnections;
type: 'broker:inforequest'; node: INode;
workflow: Workflow;
runExecutionData: IRunExecutionData;
runIndex: number;
itemIndex: number;
activeNodeName: string;
connectionInputData: INodeExecutionData[];
siblingParameters: INodeParameters;
mode: WorkflowExecuteMode;
envProviderState: EnvProviderState;
executeData?: IExecuteData;
defaultReturnRunIndex: number;
selfData: IDataObject;
contextNodeName: string;
additionalData: IWorkflowExecuteAdditionalData;
} }
export interface RunnerRegistered { export interface PartialAdditionalData {
type: 'broker:runnerregistered'; executionId?: string;
} restartExecutionId?: string;
restApiUrl: string;
export interface TaskOfferAccept { instanceBaseUrl: string;
type: 'broker:taskofferaccept'; formWaitingBaseUrl: string;
taskId: string; webhookBaseUrl: string;
offerId: string; webhookWaitingBaseUrl: string;
} webhookTestBaseUrl: string;
currentNodeParameters?: INodeParameters;
export interface TaskCancel { executionTimeoutTimestamp?: number;
type: 'broker:taskcancel'; userId?: string;
taskId: string; variables: IDataObject;
reason: string;
}
export interface TaskSettings {
type: 'broker:tasksettings';
taskId: string;
settings: unknown;
}
export interface RPCResponse {
type: 'broker:rpcresponse';
callId: string;
taskId: string;
status: 'success' | 'error';
data: unknown;
}
export interface TaskDataResponse {
type: 'broker:taskdataresponse';
taskId: string;
requestId: string;
data: unknown;
}
export interface NodeTypes {
type: 'broker:nodetypes';
nodeTypes: INodeTypeBaseDescription[];
}
export type All =
| InfoRequest
| TaskOfferAccept
| TaskCancel
| TaskSettings
| RunnerRegistered
| RPCResponse
| TaskDataResponse
| NodeTypes;
}
export namespace ToRequester {
export interface TaskReady {
type: 'broker:taskready';
requestId: string;
taskId: string;
}
export interface TaskDone {
type: 'broker:taskdone';
taskId: string;
data: TaskResultData;
}
export interface TaskError {
type: 'broker:taskerror';
taskId: string;
error: unknown;
}
export interface TaskDataRequest {
type: 'broker:taskdatarequest';
taskId: string;
requestId: string;
requestParams: TaskDataRequestParams;
}
export interface RPC {
type: 'broker:rpc';
callId: string;
taskId: string;
name: (typeof RPC_ALLOW_LIST)[number];
params: unknown[];
}
export type All = TaskReady | TaskDone | TaskError | TaskDataRequest | RPC;
}
}
export namespace RequesterMessage {
export namespace ToN8n {
export interface TaskSettings {
type: 'requester:tasksettings';
taskId: string;
settings: unknown;
}
export interface TaskCancel {
type: 'requester:taskcancel';
taskId: string;
reason: string;
}
export interface TaskDataResponse {
type: 'requester:taskdataresponse';
taskId: string;
requestId: string;
data: unknown;
}
export interface RPCResponse {
type: 'requester:rpcresponse';
taskId: string;
callId: string;
status: 'success' | 'error';
data: unknown;
}
export interface TaskRequest {
type: 'requester:taskrequest';
requestId: string;
taskType: string;
}
export type All = TaskSettings | TaskCancel | RPCResponse | TaskDataResponse | TaskRequest;
}
}
export namespace RunnerMessage {
export namespace ToN8n {
export interface Info {
type: 'runner:info';
name: string;
types: string[];
}
export interface TaskAccepted {
type: 'runner:taskaccepted';
taskId: string;
}
export interface TaskRejected {
type: 'runner:taskrejected';
taskId: string;
reason: string;
}
export interface TaskDone {
type: 'runner:taskdone';
taskId: string;
data: TaskResultData;
}
export interface TaskError {
type: 'runner:taskerror';
taskId: string;
error: unknown;
}
export interface TaskOffer {
type: 'runner:taskoffer';
offerId: string;
taskType: string;
validFor: number;
}
export interface TaskDataRequest {
type: 'runner:taskdatarequest';
taskId: string;
requestId: string;
requestParams: TaskDataRequestParams;
}
export interface RPC {
type: 'runner:rpc';
callId: string;
taskId: string;
name: (typeof RPC_ALLOW_LIST)[number];
params: unknown[];
}
export type All =
| Info
| TaskDone
| TaskError
| TaskAccepted
| TaskRejected
| TaskOffer
| RPC
| TaskDataRequest;
}
} }
export const RPC_ALLOW_LIST = [ export const RPC_ALLOW_LIST = [

View file

@ -2,14 +2,10 @@ import { ApplicationError, type INodeTypeDescription } from 'n8n-workflow';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import { type MessageEvent, WebSocket } from 'ws'; import { type MessageEvent, WebSocket } from 'ws';
import type { BaseRunnerConfig } from './config/base-runner-config'; import type { BaseRunnerConfig } from '@/config/base-runner-config';
import { TaskRunnerNodeTypes } from './node-types'; import type { BrokerMessage, RunnerMessage } from '@/message-types';
import { import { TaskRunnerNodeTypes } from '@/node-types';
RPC_ALLOW_LIST, import { RPC_ALLOW_LIST, type TaskResultData } from '@/runner-types';
type RunnerMessage,
type N8nMessage,
type TaskResultData,
} from './runner-types';
export interface Task<T = unknown> { export interface Task<T = unknown> {
taskId: string; taskId: string;
@ -90,7 +86,7 @@ export abstract class TaskRunner {
private receiveMessage = (message: MessageEvent) => { private receiveMessage = (message: MessageEvent) => {
// eslint-disable-next-line n8n-local-rules/no-uncaught-json-parse // eslint-disable-next-line n8n-local-rules/no-uncaught-json-parse
const data = JSON.parse(message.data as string) as N8nMessage.ToRunner.All; const data = JSON.parse(message.data as string) as BrokerMessage.ToRunner.All;
void this.onMessage(data); void this.onMessage(data);
}; };
@ -140,11 +136,11 @@ export abstract class TaskRunner {
} }
} }
send(message: RunnerMessage.ToN8n.All) { send(message: RunnerMessage.ToBroker.All) {
this.ws.send(JSON.stringify(message)); this.ws.send(JSON.stringify(message));
} }
onMessage(message: N8nMessage.ToRunner.All) { onMessage(message: BrokerMessage.ToRunner.All) {
switch (message.type) { switch (message.type) {
case 'broker:inforequest': case 'broker:inforequest':
this.send({ this.send({
@ -252,7 +248,7 @@ export abstract class TaskRunner {
this.sendOffers(); this.sendOffers();
} }
taskDone(taskId: string, data: RunnerMessage.ToN8n.TaskDone['data']) { taskDone(taskId: string, data: RunnerMessage.ToBroker.TaskDone['data']) {
this.send({ this.send({
type: 'runner:taskdone', type: 'runner:taskdone',
taskId, taskId,
@ -288,7 +284,7 @@ export abstract class TaskRunner {
async requestData<T = unknown>( async requestData<T = unknown>(
taskId: Task['taskId'], taskId: Task['taskId'],
requestParams: RunnerMessage.ToN8n.TaskDataRequest['requestParams'], requestParams: RunnerMessage.ToBroker.TaskDataRequest['requestParams'],
): Promise<T> { ): Promise<T> {
const requestId = nanoid(); const requestId = nanoid();
@ -314,7 +310,7 @@ export abstract class TaskRunner {
} }
} }
async makeRpcCall(taskId: string, name: RunnerMessage.ToN8n.RPC['name'], params: unknown[]) { async makeRpcCall(taskId: string, name: RunnerMessage.ToBroker.RPC['name'], params: unknown[]) {
const callId = nanoid(); const callId = nanoid();
const dataPromise = new Promise((resolve, reject) => { const dataPromise = new Promise((resolve, reject) => {
@ -342,7 +338,7 @@ export abstract class TaskRunner {
handleRpcResponse( handleRpcResponse(
callId: string, callId: string,
status: N8nMessage.ToRunner.RPCResponse['status'], status: BrokerMessage.ToRunner.RPCResponse['status'],
data: unknown, data: unknown,
) { ) {
const call = this.rpcCalls.get(callId); const call = this.rpcCalls.get(callId);

View file

@ -1,7 +1,7 @@
import type { RunnerMessage, TaskResultData } from '@n8n/task-runner';
import { mock } from 'jest-mock-extended'; import { mock } from 'jest-mock-extended';
import { TaskRejectError } from '../errors'; import { TaskRejectError } from '../errors';
import type { RunnerMessage, TaskResultData } from '../runner-types';
import { TaskBroker } from '../task-broker.service'; import { TaskBroker } from '../task-broker.service';
import type { TaskOffer, TaskRequest, TaskRunner } from '../task-broker.service'; import type { TaskOffer, TaskRequest, TaskRunner } from '../task-broker.service';
@ -381,7 +381,7 @@ describe('TaskBroker', () => {
const runnerId = 'runner1'; const runnerId = 'runner1';
const taskId = 'task1'; const taskId = 'task1';
const message: RunnerMessage.ToN8n.TaskAccepted = { const message: RunnerMessage.ToBroker.TaskAccepted = {
type: 'runner:taskaccepted', type: 'runner:taskaccepted',
taskId, taskId,
}; };
@ -406,7 +406,7 @@ describe('TaskBroker', () => {
const taskId = 'task1'; const taskId = 'task1';
const rejectionReason = 'Task execution failed'; const rejectionReason = 'Task execution failed';
const message: RunnerMessage.ToN8n.TaskRejected = { const message: RunnerMessage.ToBroker.TaskRejected = {
type: 'runner:taskrejected', type: 'runner:taskrejected',
taskId, taskId,
reason: rejectionReason, reason: rejectionReason,
@ -433,7 +433,7 @@ describe('TaskBroker', () => {
const requesterId = 'requester1'; const requesterId = 'requester1';
const data = mock<TaskResultData>(); const data = mock<TaskResultData>();
const message: RunnerMessage.ToN8n.TaskDone = { const message: RunnerMessage.ToBroker.TaskDone = {
type: 'runner:taskdone', type: 'runner:taskdone',
taskId, taskId,
data, data,
@ -464,7 +464,7 @@ describe('TaskBroker', () => {
const requesterId = 'requester1'; const requesterId = 'requester1';
const errorMessage = 'Task execution failed'; const errorMessage = 'Task execution failed';
const message: RunnerMessage.ToN8n.TaskError = { const message: RunnerMessage.ToBroker.TaskError = {
type: 'runner:taskerror', type: 'runner:taskerror',
taskId, taskId,
error: errorMessage, error: errorMessage,
@ -494,14 +494,14 @@ describe('TaskBroker', () => {
const taskId = 'task1'; const taskId = 'task1';
const requesterId = 'requester1'; const requesterId = 'requester1';
const requestId = 'request1'; const requestId = 'request1';
const requestParams: RunnerMessage.ToN8n.TaskDataRequest['requestParams'] = { const requestParams: RunnerMessage.ToBroker.TaskDataRequest['requestParams'] = {
dataOfNodes: 'all', dataOfNodes: 'all',
env: true, env: true,
input: true, input: true,
prevNode: true, prevNode: true,
}; };
const message: RunnerMessage.ToN8n.TaskDataRequest = { const message: RunnerMessage.ToBroker.TaskDataRequest = {
type: 'runner:taskdatarequest', type: 'runner:taskdatarequest',
taskId, taskId,
requestId, requestId,
@ -534,7 +534,7 @@ describe('TaskBroker', () => {
const rpcName = 'helpers.httpRequestWithAuthentication'; const rpcName = 'helpers.httpRequestWithAuthentication';
const rpcParams = ['param1', 'param2']; const rpcParams = ['param1', 'param2'];
const message: RunnerMessage.ToN8n.RPC = { const message: RunnerMessage.ToBroker.RPC = {
type: 'runner:rpc', type: 'runner:rpc',
taskId, taskId,
callId, callId,

View file

@ -1,5 +1,5 @@
import type { Response } from 'express'; import type { Response } from 'express';
import type { INodeExecutionData, INodeTypeBaseDescription } from 'n8n-workflow'; import type { INodeExecutionData } from 'n8n-workflow';
import type WebSocket from 'ws'; import type WebSocket from 'ws';
import type { TaskRunner } from './task-broker.service'; import type { TaskRunner } from './task-broker.service';
@ -34,230 +34,3 @@ export interface TaskRunnerServerInitRequest
} }
export type TaskRunnerServerInitResponse = Response & { req: TaskRunnerServerInitRequest }; export type TaskRunnerServerInitResponse = Response & { req: TaskRunnerServerInitRequest };
export namespace N8nMessage {
export namespace ToRunner {
export interface InfoRequest {
type: 'broker:inforequest';
}
export interface RunnerRegistered {
type: 'broker:runnerregistered';
}
export interface TaskOfferAccept {
type: 'broker:taskofferaccept';
taskId: string;
offerId: string;
}
export interface TaskCancel {
type: 'broker:taskcancel';
taskId: string;
reason: string;
}
export interface TaskSettings {
type: 'broker:tasksettings';
taskId: string;
settings: unknown;
}
export interface RPCResponse {
type: 'broker:rpcresponse';
callId: string;
taskId: string;
status: 'success' | 'error';
data: unknown;
}
export interface TaskDataResponse {
type: 'broker:taskdataresponse';
taskId: string;
requestId: string;
data: unknown;
}
export interface NodeTypes {
type: 'broker:nodetypes';
nodeTypes: INodeTypeBaseDescription[];
}
export type All =
| InfoRequest
| TaskOfferAccept
| TaskCancel
| TaskSettings
| RunnerRegistered
| RPCResponse
| TaskDataResponse
| NodeTypes;
}
export namespace ToRequester {
export interface TaskReady {
type: 'broker:taskready';
requestId: string;
taskId: string;
}
export interface TaskDone {
type: 'broker:taskdone';
taskId: string;
data: TaskResultData;
}
export interface TaskError {
type: 'broker:taskerror';
taskId: string;
error: unknown;
}
export interface TaskDataRequest {
type: 'broker:taskdatarequest';
taskId: string;
requestId: string;
requestParams: TaskDataRequestParams;
}
export interface RPC {
type: 'broker:rpc';
callId: string;
taskId: string;
name: (typeof RPC_ALLOW_LIST)[number];
params: unknown[];
}
export type All = TaskReady | TaskDone | TaskError | TaskDataRequest | RPC;
}
}
export namespace RequesterMessage {
export namespace ToN8n {
export interface TaskSettings {
type: 'requester:tasksettings';
taskId: string;
settings: unknown;
}
export interface TaskCancel {
type: 'requester:taskcancel';
taskId: string;
reason: string;
}
export interface TaskDataResponse {
type: 'requester:taskdataresponse';
taskId: string;
requestId: string;
data: unknown;
}
export interface RPCResponse {
type: 'requester:rpcresponse';
taskId: string;
callId: string;
status: 'success' | 'error';
data: unknown;
}
export interface TaskRequest {
type: 'requester:taskrequest';
requestId: string;
taskType: string;
}
export type All = TaskSettings | TaskCancel | RPCResponse | TaskDataResponse | TaskRequest;
}
}
export namespace RunnerMessage {
export namespace ToN8n {
export interface Info {
type: 'runner:info';
name: string;
types: string[];
}
export interface TaskAccepted {
type: 'runner:taskaccepted';
taskId: string;
}
export interface TaskRejected {
type: 'runner:taskrejected';
taskId: string;
reason: string;
}
export interface TaskDone {
type: 'runner:taskdone';
taskId: string;
data: TaskResultData;
}
export interface TaskError {
type: 'runner:taskerror';
taskId: string;
error: unknown;
}
export interface TaskOffer {
type: 'runner:taskoffer';
offerId: string;
taskType: string;
validFor: number;
}
export interface TaskDataRequest {
type: 'runner:taskdatarequest';
taskId: string;
requestId: string;
requestParams: TaskDataRequestParams;
}
export interface RPC {
type: 'runner:rpc';
callId: string;
taskId: string;
name: (typeof RPC_ALLOW_LIST)[number];
params: unknown[];
}
export type All =
| Info
| TaskDone
| TaskError
| TaskAccepted
| TaskRejected
| TaskOffer
| RPC
| TaskDataRequest;
}
}
export const RPC_ALLOW_LIST = [
'logNodeOutput',
'helpers.httpRequestWithAuthentication',
'helpers.requestWithAuthenticationPaginated',
// "helpers.normalizeItems"
// "helpers.constructExecutionMetaData"
// "helpers.assertBinaryData"
'helpers.getBinaryDataBuffer',
// "helpers.copyInputItems"
// "helpers.returnJsonArray"
'helpers.getSSHClient',
'helpers.createReadStream',
// "helpers.getStoragePath"
'helpers.writeContentToFile',
'helpers.prepareBinaryData',
'helpers.setBinaryDataBuffer',
'helpers.copyBinaryFile',
'helpers.binaryToBuffer',
// "helpers.binaryToString"
// "helpers.getBinaryPath"
'helpers.getBinaryStream',
'helpers.getBinaryMetadata',
'helpers.createDeferredPromise',
'helpers.httpRequest',
] as const;

View file

@ -1,3 +1,4 @@
import type { BrokerMessage, RunnerMessage } from '@n8n/task-runner';
import { Service } from 'typedi'; import { Service } from 'typedi';
import type WebSocket from 'ws'; import type WebSocket from 'ws';
@ -5,11 +6,9 @@ import { Logger } from '@/logging/logger.service';
import { DefaultTaskRunnerDisconnectAnalyzer } from './default-task-runner-disconnect-analyzer'; import { DefaultTaskRunnerDisconnectAnalyzer } from './default-task-runner-disconnect-analyzer';
import type { import type {
RunnerMessage, DisconnectAnalyzer,
N8nMessage,
TaskRunnerServerInitRequest, TaskRunnerServerInitRequest,
TaskRunnerServerInitResponse, TaskRunnerServerInitResponse,
DisconnectAnalyzer,
} from './runner-types'; } from './runner-types';
import { TaskBroker, type MessageCallback, type TaskRunner } from './task-broker.service'; import { TaskBroker, type MessageCallback, type TaskRunner } from './task-broker.service';
@ -35,7 +34,7 @@ export class TaskRunnerWsServer {
return this.disconnectAnalyzer; return this.disconnectAnalyzer;
} }
sendMessage(id: TaskRunner['id'], message: N8nMessage.ToRunner.All) { sendMessage(id: TaskRunner['id'], message: BrokerMessage.ToRunner.All) {
this.runnerConnections.get(id)?.send(JSON.stringify(message)); this.runnerConnections.get(id)?.send(JSON.stringify(message));
} }
@ -49,9 +48,9 @@ export class TaskRunnerWsServer {
try { try {
const buffer = Array.isArray(data) ? Buffer.concat(data) : Buffer.from(data); const buffer = Array.isArray(data) ? Buffer.concat(data) : Buffer.from(data);
const message: RunnerMessage.ToN8n.All = JSON.parse( const message: RunnerMessage.ToBroker.All = JSON.parse(
buffer.toString('utf8'), buffer.toString('utf8'),
) as RunnerMessage.ToN8n.All; ) as RunnerMessage.ToBroker.All;
if (!isConnected && message.type !== 'runner:info') { if (!isConnected && message.type !== 'runner:info') {
return; return;
@ -94,7 +93,7 @@ export class TaskRunnerWsServer {
connection.on('message', onMessage); connection.on('message', onMessage);
connection.send( connection.send(
JSON.stringify({ type: 'broker:inforequest' } as N8nMessage.ToRunner.InfoRequest), JSON.stringify({ type: 'broker:inforequest' } as BrokerMessage.ToRunner.InfoRequest),
); );
} }

View file

@ -1,3 +1,9 @@
import type {
BrokerMessage,
RequesterMessage,
RunnerMessage,
TaskResultData,
} from '@n8n/task-runner';
import { ApplicationError } from 'n8n-workflow'; import { ApplicationError } from 'n8n-workflow';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import { Service } from 'typedi'; import { Service } from 'typedi';
@ -6,7 +12,6 @@ import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials';
import { Logger } from '@/logging/logger.service'; import { Logger } from '@/logging/logger.service';
import { TaskRejectError } from './errors'; import { TaskRejectError } from './errors';
import type { N8nMessage, RunnerMessage, RequesterMessage, TaskResultData } from './runner-types';
export interface TaskRunner { export interface TaskRunner {
id: string; id: string;
@ -38,13 +43,15 @@ export interface TaskRequest {
acceptInProgress?: boolean; acceptInProgress?: boolean;
} }
export type MessageCallback = (message: N8nMessage.ToRunner.All) => Promise<void> | void; export type MessageCallback = (message: BrokerMessage.ToRunner.All) => Promise<void> | void;
export type RequesterMessageCallback = ( export type RequesterMessageCallback = (
message: N8nMessage.ToRequester.All, message: BrokerMessage.ToRequester.All,
) => Promise<void> | void; ) => Promise<void> | void;
type RunnerAcceptCallback = () => void; type RunnerAcceptCallback = () => void;
type RequesterAcceptCallback = (settings: RequesterMessage.ToN8n.TaskSettings['settings']) => void; type RequesterAcceptCallback = (
settings: RequesterMessage.ToBroker.TaskSettings['settings'],
) => void;
type TaskRejectCallback = (reason: TaskRejectError) => void; type TaskRejectCallback = (reason: TaskRejectError) => void;
@Service() @Service()
@ -134,11 +141,11 @@ export class TaskBroker {
this.requesters.delete(requesterId); this.requesters.delete(requesterId);
} }
private async messageRunner(runnerId: TaskRunner['id'], message: N8nMessage.ToRunner.All) { private async messageRunner(runnerId: TaskRunner['id'], message: BrokerMessage.ToRunner.All) {
await this.knownRunners.get(runnerId)?.messageCallback(message); await this.knownRunners.get(runnerId)?.messageCallback(message);
} }
private async messageAllRunners(message: N8nMessage.ToRunner.All) { private async messageAllRunners(message: BrokerMessage.ToRunner.All) {
await Promise.allSettled( await Promise.allSettled(
[...this.knownRunners.values()].map(async (runner) => { [...this.knownRunners.values()].map(async (runner) => {
await runner.messageCallback(message); await runner.messageCallback(message);
@ -146,11 +153,11 @@ export class TaskBroker {
); );
} }
private async messageRequester(requesterId: string, message: N8nMessage.ToRequester.All) { private async messageRequester(requesterId: string, message: BrokerMessage.ToRequester.All) {
await this.requesters.get(requesterId)?.(message); await this.requesters.get(requesterId)?.(message);
} }
async onRunnerMessage(runnerId: TaskRunner['id'], message: RunnerMessage.ToN8n.All) { async onRunnerMessage(runnerId: TaskRunner['id'], message: RunnerMessage.ToBroker.All) {
const runner = this.knownRunners.get(runnerId); const runner = this.knownRunners.get(runnerId);
if (!runner) { if (!runner) {
return; return;
@ -193,7 +200,7 @@ export class TaskBroker {
async handleRpcRequest( async handleRpcRequest(
taskId: Task['id'], taskId: Task['id'],
callId: string, callId: string,
name: RunnerMessage.ToN8n.RPC['name'], name: RunnerMessage.ToBroker.RPC['name'],
params: unknown[], params: unknown[],
) { ) {
const task = this.tasks.get(taskId); const task = this.tasks.get(taskId);
@ -227,8 +234,8 @@ export class TaskBroker {
async handleDataRequest( async handleDataRequest(
taskId: Task['id'], taskId: Task['id'],
requestId: RunnerMessage.ToN8n.TaskDataRequest['requestId'], requestId: RunnerMessage.ToBroker.TaskDataRequest['requestId'],
requestParams: RunnerMessage.ToN8n.TaskDataRequest['requestParams'], requestParams: RunnerMessage.ToBroker.TaskDataRequest['requestParams'],
) { ) {
const task = this.tasks.get(taskId); const task = this.tasks.get(taskId);
if (!task) { if (!task) {
@ -244,7 +251,7 @@ export class TaskBroker {
async handleResponse( async handleResponse(
taskId: Task['id'], taskId: Task['id'],
requestId: RunnerMessage.ToN8n.TaskDataRequest['requestId'], requestId: RunnerMessage.ToBroker.TaskDataRequest['requestId'],
data: unknown, data: unknown,
) { ) {
const task = this.tasks.get(taskId); const task = this.tasks.get(taskId);
@ -259,7 +266,7 @@ export class TaskBroker {
}); });
} }
async onRequesterMessage(requesterId: string, message: RequesterMessage.ToN8n.All) { async onRequesterMessage(requesterId: string, message: RequesterMessage.ToBroker.All) {
switch (message.type) { switch (message.type) {
case 'requester:tasksettings': case 'requester:tasksettings':
this.handleRequesterAccept(message.taskId, message.settings); this.handleRequesterAccept(message.taskId, message.settings);
@ -291,7 +298,7 @@ export class TaskBroker {
async handleRequesterRpcResponse( async handleRequesterRpcResponse(
taskId: string, taskId: string,
callId: string, callId: string,
status: RequesterMessage.ToN8n.RPCResponse['status'], status: RequesterMessage.ToBroker.RPCResponse['status'],
data: unknown, data: unknown,
) { ) {
const runner = await this.getRunnerOrFailTask(taskId); const runner = await this.getRunnerOrFailTask(taskId);
@ -317,7 +324,7 @@ export class TaskBroker {
handleRequesterAccept( handleRequesterAccept(
taskId: Task['id'], taskId: Task['id'],
settings: RequesterMessage.ToN8n.TaskSettings['settings'], settings: RequesterMessage.ToBroker.TaskSettings['settings'],
) { ) {
const acceptReject = this.requesterAcceptRejects.get(taskId); const acceptReject = this.requesterAcceptRejects.get(taskId);
if (acceptReject) { if (acceptReject) {
@ -467,10 +474,12 @@ export class TaskBroker {
this.pendingTaskRequests.splice(requestIndex, 1); this.pendingTaskRequests.splice(requestIndex, 1);
try { try {
const acceptPromise = new Promise<RequesterMessage.ToN8n.TaskSettings['settings']>( const acceptPromise = new Promise<RequesterMessage.ToBroker.TaskSettings['settings']>(
(resolve, reject) => { (resolve, reject) => {
this.requesterAcceptRejects.set(taskId, { this.requesterAcceptRejects.set(taskId, {
accept: resolve as (settings: RequesterMessage.ToN8n.TaskSettings['settings']) => void, accept: resolve as (
settings: RequesterMessage.ToBroker.TaskSettings['settings'],
) => void,
reject, reject,
}); });

View file

@ -1,9 +1,9 @@
import type { TaskData } from '@n8n/task-runner';
import { mock } from 'jest-mock-extended'; import { mock } from 'jest-mock-extended';
import type { IExecuteFunctions, IWorkflowExecuteAdditionalData } from 'n8n-workflow'; import type { IExecuteFunctions, IWorkflowExecuteAdditionalData } from 'n8n-workflow';
import { type INode, type INodeExecutionData, type Workflow } from 'n8n-workflow'; import { type INode, type INodeExecutionData, type Workflow } from 'n8n-workflow';
import { DataRequestResponseBuilder } from '../data-request-response-builder'; import { DataRequestResponseBuilder } from '../data-request-response-builder';
import type { TaskData } from '../task-manager';
const triggerNode: INode = mock<INode>({ const triggerNode: INode = mock<INode>({
name: 'Trigger', name: 'Trigger',

View file

@ -1,3 +1,9 @@
import type {
DataRequestResponse,
BrokerMessage,
PartialAdditionalData,
TaskData,
} from '@n8n/task-runner';
import type { import type {
EnvProviderState, EnvProviderState,
IExecuteData, IExecuteData,
@ -11,9 +17,6 @@ import type {
WorkflowParameters, WorkflowParameters,
} from 'n8n-workflow'; } from 'n8n-workflow';
import type { DataRequestResponse, PartialAdditionalData, TaskData } from './task-manager';
import type { N8nMessage } from '../runner-types';
/** /**
* Builds the response to a data request coming from a Task Runner. Tries to minimize * Builds the response to a data request coming from a Task Runner. Tries to minimize
* the amount of data that is sent to the runner by only providing what is requested. * the amount of data that is sent to the runner by only providing what is requested.
@ -23,7 +26,7 @@ export class DataRequestResponseBuilder {
constructor( constructor(
private readonly taskData: TaskData, private readonly taskData: TaskData,
private readonly requestParams: N8nMessage.ToRequester.TaskDataRequest['requestParams'], private readonly requestParams: BrokerMessage.ToRequester.TaskDataRequest['requestParams'],
) { ) {
this.requestedNodeNames = new Set(requestParams.dataOfNodes); this.requestedNodeNames = new Set(requestParams.dataOfNodes);

View file

@ -1,7 +1,7 @@
import type { RequesterMessage } from '@n8n/task-runner';
import Container from 'typedi'; import Container from 'typedi';
import { TaskManager } from './task-manager'; import { TaskManager } from './task-manager';
import type { RequesterMessage } from '../runner-types';
import type { RequesterMessageCallback } from '../task-broker.service'; import type { RequesterMessageCallback } from '../task-broker.service';
import { TaskBroker } from '../task-broker.service'; import { TaskBroker } from '../task-broker.service';
@ -24,7 +24,7 @@ export class LocalTaskManager extends TaskManager {
); );
} }
sendMessage(message: RequesterMessage.ToN8n.All) { sendMessage(message: RequesterMessage.ToBroker.All) {
void this.taskBroker.onRequesterMessage(this.id, message); void this.taskBroker.onRequesterMessage(this.id, message);
} }
} }

View file

@ -1,30 +1,24 @@
import { import type { TaskResultData, RequesterMessage, BrokerMessage, TaskData } from '@n8n/task-runner';
type EnvProviderState, import { RPC_ALLOW_LIST } from '@n8n/task-runner';
type IExecuteFunctions, import type {
type Workflow, EnvProviderState,
type IRunExecutionData, IExecuteFunctions,
type INodeExecutionData, Workflow,
type ITaskDataConnections, IRunExecutionData,
type INode, INodeExecutionData,
type WorkflowParameters, ITaskDataConnections,
type INodeParameters, INode,
type WorkflowExecuteMode, INodeParameters,
type IExecuteData, WorkflowExecuteMode,
type IDataObject, IExecuteData,
type IWorkflowExecuteAdditionalData, IDataObject,
type Result, IWorkflowExecuteAdditionalData,
createResultOk, Result,
createResultError,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { createResultOk, createResultError } from 'n8n-workflow';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import { DataRequestResponseBuilder } from './data-request-response-builder'; import { DataRequestResponseBuilder } from './data-request-response-builder';
import {
RPC_ALLOW_LIST,
type TaskResultData,
type N8nMessage,
type RequesterMessage,
} from '../runner-types';
export type RequestAccept = (jobId: string) => void; export type RequestAccept = (jobId: string) => void;
export type RequestReject = (reason: string) => void; export type RequestReject = (reason: string) => void;
@ -32,62 +26,6 @@ export type RequestReject = (reason: string) => void;
export type TaskAccept = (data: TaskResultData) => void; export type TaskAccept = (data: TaskResultData) => void;
export type TaskReject = (error: unknown) => void; export type TaskReject = (error: unknown) => void;
export interface TaskData {
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;
defaultReturnRunIndex: number;
selfData: IDataObject;
contextNodeName: string;
additionalData: IWorkflowExecuteAdditionalData;
}
export interface PartialAdditionalData {
executionId?: string;
restartExecutionId?: string;
restApiUrl: string;
instanceBaseUrl: string;
formWaitingBaseUrl: string;
webhookBaseUrl: string;
webhookWaitingBaseUrl: string;
webhookTestBaseUrl: string;
currentNodeParameters?: INodeParameters;
executionTimeoutTimestamp?: number;
userId?: string;
variables: IDataObject;
}
export interface DataRequestResponse {
workflow: Omit<WorkflowParameters, 'nodeTypes'>;
inputData: ITaskDataConnections;
node: INode;
runExecutionData: IRunExecutionData;
runIndex: number;
itemIndex: number;
activeNodeName: string;
connectionInputData: INodeExecutionData[];
siblingParameters: INodeParameters;
mode: WorkflowExecuteMode;
envProviderState: EnvProviderState;
executeData?: IExecuteData;
defaultReturnRunIndex: number;
selfData: IDataObject;
contextNodeName: string;
additionalData: PartialAdditionalData;
}
export interface TaskRequest { export interface TaskRequest {
requestId: string; requestId: string;
taskType: string; taskType: string;
@ -219,9 +157,9 @@ export class TaskManager {
} }
} }
sendMessage(_message: RequesterMessage.ToN8n.All) {} sendMessage(_message: RequesterMessage.ToBroker.All) {}
onMessage(message: N8nMessage.ToRequester.All) { onMessage(message: BrokerMessage.ToRequester.All) {
switch (message.type) { switch (message.type) {
case 'broker:taskready': case 'broker:taskready':
this.taskReady(message.requestId, message.taskId); this.taskReady(message.requestId, message.taskId);
@ -282,7 +220,7 @@ export class TaskManager {
sendTaskData( sendTaskData(
taskId: string, taskId: string,
requestId: string, requestId: string,
requestParams: N8nMessage.ToRequester.TaskDataRequest['requestParams'], requestParams: BrokerMessage.ToRequester.TaskDataRequest['requestParams'],
) { ) {
const job = this.tasks.get(taskId); const job = this.tasks.get(taskId);
if (!job) { if (!job) {
@ -304,7 +242,7 @@ export class TaskManager {
async handleRpc( async handleRpc(
taskId: string, taskId: string,
callId: string, callId: string,
name: N8nMessage.ToRequester.RPC['name'], name: BrokerMessage.ToRequester.RPC['name'],
params: unknown[], params: unknown[],
) { ) {
const job = this.tasks.get(taskId); const job = this.tasks.get(taskId);

View file

@ -92,7 +92,7 @@ export class TaskRunnerProcess extends TypedEmitter<TaskRunnerProcessEventMap> {
} }
startNode(grantToken: string, n8nUri: string) { startNode(grantToken: string, n8nUri: string) {
const startScript = require.resolve('@n8n/task-runner'); const startScript = require.resolve('@n8n/task-runner/start');
return spawn('node', [startScript], { return spawn('node', [startScript], {
env: this.getProcessEnvVars(grantToken, n8nUri), env: this.getProcessEnvVars(grantToken, n8nUri),

View file

@ -8,6 +8,7 @@ import { TaskRunnerWsServer } from '../../../src/runners/runner-ws-server';
describe('TaskRunnerModule in internal_childprocess mode', () => { describe('TaskRunnerModule in internal_childprocess mode', () => {
const runnerConfig = Container.get(TaskRunnersConfig); const runnerConfig = Container.get(TaskRunnersConfig);
runnerConfig.port = 0; // Random port
runnerConfig.mode = 'internal_childprocess'; runnerConfig.mode = 'internal_childprocess';
const module = Container.get(TaskRunnerModule); const module = Container.get(TaskRunnerModule);