UNPKG

n8n-nodes-base

Version:

Base nodes of n8n

260 lines 11.2 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Code = void 0; /* eslint-disable n8n-nodes-base/node-execute-block-wrong-error-thrown */ const config_1 = require("@n8n/config"); const di_1 = require("@n8n/di"); const set_1 = __importDefault(require("lodash/set")); const n8n_workflow_1 = require("n8n-workflow"); const JavascriptCodeDescription_1 = require("./descriptions/JavascriptCodeDescription"); const PythonCodeDescription_1 = require("./descriptions/PythonCodeDescription"); const JavaScriptSandbox_1 = require("./JavaScriptSandbox"); const JsTaskRunnerSandbox_1 = require("./JsTaskRunnerSandbox"); const python_runner_unavailable_error_1 = require("./python-runner-unavailable.error"); const PythonTaskRunnerSandbox_1 = require("./PythonTaskRunnerSandbox"); const Sandbox_1 = require("./Sandbox"); const utils_1 = require("./utils"); const { CODE_ENABLE_STDOUT } = process.env; class PythonDisabledError extends n8n_workflow_1.UserError { constructor() { super('This instance disallows Python execution because it has the environment variable `N8N_PYTHON_ENABLED` set to `false`. To restore Python execution, remove this environment variable or set it to `true` and restart the instance.'); } } class Code { description = { displayName: 'Code', name: 'code', icon: 'file:code.svg', group: ['transform'], version: [1, 2], defaultVersion: 2, description: 'Run custom JavaScript or Python code', defaults: { name: 'Code', }, inputs: [n8n_workflow_1.NodeConnectionTypes.Main], outputs: [n8n_workflow_1.NodeConnectionTypes.Main], builderHint: { message: 'Use Code node as a LAST RESORT — it runs in a sandboxed environment and is slower than native nodes. Code node is ONLY appropriate for complex multi-step algorithms that cannot be expressed in single expressions, or operations requiring complex data structures.', relatedNodes: [ { nodeType: 'n8n-nodes-base.set', relationHint: 'Use this instead for data manipulation: add/modify/rename fields, set values, map data', }, { nodeType: 'n8n-nodes-base.filter', relationHint: 'Use this instead for filtering items by condition', }, { nodeType: 'n8n-nodes-base.if', relationHint: 'Use this instead for routing by condition', }, { nodeType: 'n8n-nodes-base.switch', relationHint: 'Use this instead for multi-way routing by condition', }, { nodeType: 'n8n-nodes-base.splitOut', relationHint: 'Use this instead for splitting arrays into separate items', }, { nodeType: 'n8n-nodes-base.aggregate', relationHint: 'Use this instead for combining multiple items into one', }, { nodeType: 'n8n-nodes-base.summarize', relationHint: 'Use this instead for summarizing or pivoting data', }, { nodeType: 'n8n-nodes-base.removeDuplicates', relationHint: 'Use this instead for removing duplicates', }, { nodeType: 'n8n-nodes-base.limit', relationHint: 'Use this instead to reduce the number of items returned', }, { nodeType: 'n8n-nodes-base.merge', relationHint: 'Use this instead for merging data from multiple branches', }, { nodeType: 'n8n-nodes-base.dateTime', relationHint: 'Use this instead for date time operations', }, { nodeType: 'n8n-nodes-base.html', relationHint: 'Use this instead for creating html pages', }, ], }, parameterPane: 'wide', properties: [ { displayName: 'Mode', name: 'mode', type: 'options', noDataExpression: true, options: [ { name: 'Run Once for All Items', value: 'runOnceForAllItems', description: 'Run this code only once, no matter how many input items there are', }, { name: 'Run Once for Each Item', value: 'runOnceForEachItem', description: 'Run this code as many times as there are input items', }, ], default: 'runOnceForAllItems', }, { displayName: 'Language', name: 'language', type: 'options', noDataExpression: true, displayOptions: { show: { '@version': [2], }, }, options: [ { name: 'JavaScript', value: 'javaScript', action: 'Code in JavaScript', }, { name: 'Python', value: 'pythonNative', action: 'Code in Python', }, ], default: 'javaScript', }, { displayName: 'Language', name: 'language', type: 'hidden', displayOptions: { show: { '@version': [1], }, }, default: 'javaScript', }, ...JavascriptCodeDescription_1.javascriptCodeDescription, ...PythonCodeDescription_1.pythonCodeDescription, ], }; async execute() { const node = this.getNode(); const language = node.typeVersion === 2 ? this.getNodeParameter('language', 0) : 'javaScript'; const isJsLang = language === 'javaScript'; const isPyLang = language === 'python' || language === 'pythonNative'; // keep legacy `python` for backwards compatibility const runnersConfig = di_1.Container.get(config_1.TaskRunnersConfig); const isJsRunner = runnersConfig.enabled; if (isPyLang && !di_1.Container.get(config_1.NodesConfig).pythonEnabled) { throw new PythonDisabledError(); } const nodeMode = this.getNodeParameter('mode', 0); const workflowMode = this.getMode(); const codeParameterName = isPyLang ? 'pythonCode' : 'jsCode'; if (isJsLang && isJsRunner) { const code = this.getNodeParameter(codeParameterName, 0); const sandbox = new JsTaskRunnerSandbox_1.JsTaskRunnerSandbox(workflowMode, this); const numInputItems = this.getInputData().length; return nodeMode === 'runOnceForAllItems' ? [await sandbox.runCodeAllItems(code)] : [await sandbox.runCodeForEachItem(code, numInputItems)]; } if (isPyLang) { const runnerStatus = this.getRunnerStatus('python'); if (!runnerStatus.available) { throw new python_runner_unavailable_error_1.PythonRunnerUnavailableError(runnerStatus.reason); } const code = this.getNodeParameter(codeParameterName, 0); const sandbox = new PythonTaskRunnerSandbox_1.PythonTaskRunnerSandbox(code, nodeMode, workflowMode, this); return [await sandbox.runUsingIncomingItems()]; } const getSandbox = (index = 0) => { const code = this.getNodeParameter(codeParameterName, index); const context = Sandbox_1.getSandboxContext.call(this, index); if (nodeMode === 'runOnceForAllItems') { context.items = context.$input.all(); } else { context.item = context.$input.item; } const sandbox = new JavaScriptSandbox_1.JavaScriptSandbox(context, code, this.helpers); sandbox.on('output', workflowMode === 'manual' ? this.sendMessageToUI.bind(this) : CODE_ENABLE_STDOUT === 'true' ? (...args) => console.log(`[Workflow "${this.getWorkflow().id}"][Node "${node.name}"]`, ...args) : () => { }); return sandbox; }; const inputDataItems = this.getInputData(); // ---------------------------------- // runOnceForAllItems // ---------------------------------- if (nodeMode === 'runOnceForAllItems') { const sandbox = getSandbox(); let items; try { items = (await sandbox.runCodeAllItems()); } catch (error) { if (!this.continueOnFail()) { (0, set_1.default)(error, 'node', node); throw error; } items = [{ json: { error: error.message } }]; } for (const item of items) { (0, utils_1.standardizeOutput)(item.json); } (0, utils_1.addPostExecutionWarning)(this, items, inputDataItems?.length); return [items]; } // ---------------------------------- // runOnceForEachItem // ---------------------------------- const returnData = []; for (let index = 0; index < inputDataItems.length; index++) { const sandbox = getSandbox(index); let result; try { result = await sandbox.runCodeEachItem(index); } catch (error) { if (!this.continueOnFail()) { (0, set_1.default)(error, 'node', node); throw error; } returnData.push({ json: { error: error.message }, pairedItem: { item: index, }, }); } if (result) { returnData.push({ json: (0, utils_1.standardizeOutput)(result.json), pairedItem: { item: index }, ...(result.binary && { binary: result.binary }), }); } } (0, utils_1.addPostExecutionWarning)(this, returnData, inputDataItems?.length); return [returnData]; } } exports.Code = Code; //# sourceMappingURL=Code.node.js.map