n8n-nodes-base
Version:
Base nodes of n8n
260 lines • 11.2 kB
JavaScript
"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