import { ApplicationError, type CodeExecutionMode, type IExecuteFunctions, type INodeExecutionData, type WorkflowExecuteMode, } from 'n8n-workflow'; import { isWrappableError, WrappedExecutionError } from './errors/WrappedExecutionError'; import { validateNoDisallowedMethodsInRunForEach } from './JsCodeValidator'; /** * JS Code execution sandbox that executes the JS code using task runner. */ export class JsTaskRunnerSandbox { constructor( private readonly jsCode: string, private readonly nodeMode: CodeExecutionMode, private readonly workflowMode: WorkflowExecuteMode, private readonly executeFunctions: IExecuteFunctions, ) {} async runCodeAllItems(): Promise { const itemIndex = 0; const executionResult = await this.executeFunctions.startJob( 'javascript', { code: this.jsCode, nodeMode: this.nodeMode, workflowMode: this.workflowMode, continueOnFail: this.executeFunctions.continueOnFail(), }, itemIndex, ); return executionResult.ok ? executionResult.result : this.throwExecutionError(executionResult.error); } async runCodeForEachItem(): Promise { validateNoDisallowedMethodsInRunForEach(this.jsCode, 0); const itemIndex = 0; const executionResult = await this.executeFunctions.startJob( 'javascript', { code: this.jsCode, nodeMode: this.nodeMode, workflowMode: this.workflowMode, continueOnFail: this.executeFunctions.continueOnFail(), }, itemIndex, ); return executionResult.ok ? executionResult.result : this.throwExecutionError(executionResult.error); } private throwExecutionError(error: unknown): never { if (error instanceof Error) { throw error; } else if (isWrappableError(error)) { // The error coming from task runner is not an instance of error, // so we need to wrap it in an error instance. throw new WrappedExecutionError(error); } throw new ApplicationError(`Unknown error: ${JSON.stringify(error)}`); } }