diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 00000000000..adb4812df21
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,27 @@
+name: Node CI
+
+on: [push]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ node-version: [10.x, 12.x]
+
+ steps:
+ - uses: actions/checkout@v1
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v1
+ with:
+ node-version: ${{ matrix.node-version }}
+ - name: npm install, build, and test
+ run: |
+ npm install
+ npm run bootstrap
+ npm run build --if-present
+ npm test
+ env:
+ CI: true
diff --git a/docs/configuration.md b/docs/configuration.md
index fb612d31fc8..65dd36f472c 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -100,11 +100,13 @@ export N8N_CUSTOM_EXTENSIONS="/home/jim/n8n/custom-nodes;/data/n8n/nodes"
```
-## Use built-in modules in Function-Nodes
+## Use built-in and external modules in Function-Nodes
-By default is it for security reasons not allowed to import modules in Function-Nodes.
-It is, however, possible to lift that restriction for built-in modules by setting the
-environment variable `NODE_FUNCTION_ALLOW_BUILTIN`.
+For security reasons, importing modules is restricted by default in Function-Nodes.
+It is, however, possible to lift that restriction for built-in and external modules by
+setting the following environment variables:
+`NODE_FUNCTION_ALLOW_BUILTIN`: For builtin modules
+`NODE_FUNCTION_ALLOW_EXTERNAL`: For external modules sourced from n8n/node_modules directory. External module support is disabled when env variable is not set.
```bash
# Allows usage of all builtin modules
@@ -115,6 +117,9 @@ export NODE_FUNCTION_ALLOW_BUILTIN=crypto
# Allows usage of only crypto and fs
export NODE_FUNCTION_ALLOW_BUILTIN=crypto,fs
+
+# Allow usage of external npm modules. Wildcard matching is not supported.
+export NODE_FUNCTION_ALLOW_EXTERNAL=moment,lodash
```
diff --git a/packages/cli/commands/start.ts b/packages/cli/commands/start.ts
index 62e6615897f..5494b5c5c30 100644
--- a/packages/cli/commands/start.ts
+++ b/packages/cli/commands/start.ts
@@ -5,8 +5,7 @@ import {
} from "n8n-core";
import { Command, flags } from '@oclif/command';
const open = require('open');
-import { promisify } from 'util';
-import { dirname } from 'path';
+// import { dirname } from 'path';
import * as config from '../config';
import {
@@ -20,7 +19,6 @@ import {
TestWebhooks,
} from "../src";
-const tunnel = promisify(localtunnel);
// // Add support for internationalization
// const fullIcuPath = require.resolve('full-icu');
@@ -151,7 +149,7 @@ export class Start extends Command {
const port = config.get('port') as number;
// @ts-ignore
- const webhookTunnel = await tunnel(port, tunnelSettings);
+ const webhookTunnel = await localtunnel(port, tunnelSettings);
process.env.WEBHOOK_TUNNEL_URL = webhookTunnel.url + '/';
this.log(`Tunnel URL: ${process.env.WEBHOOK_TUNNEL_URL}\n`);
diff --git a/packages/cli/package.json b/packages/cli/package.json
index c7053147741..f69a2719f52 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "n8n",
- "version": "0.40.0",
+ "version": "0.41.0",
"description": "n8n Workflow Automation Tool",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",
@@ -52,24 +52,24 @@
"devDependencies": {
"@oclif/dev-cli": "^1.22.2",
"@types/basic-auth": "^1.1.2",
- "@types/compression": "0.0.36",
+ "@types/compression": "1.0.1",
"@types/connect-history-api-fallback": "^1.3.1",
"@types/convict": "^4.2.1",
- "@types/dotenv": "^6.1.1",
+ "@types/dotenv": "^8.2.0",
"@types/express": "^4.16.1",
"@types/jest": "^24.0.18",
"@types/localtunnel": "^1.9.0",
- "@types/lodash.get": "^4.4.2",
+ "@types/lodash.get": "^4.4.6",
"@types/node": "^10.10.1",
"@types/open": "^6.1.0",
"@types/parseurl": "^1.3.1",
"@types/request-promise-native": "^1.0.15",
"jest": "^24.9.0",
- "nodemon": "^1.19.1",
+ "nodemon": "^2.0.2",
"run-script-os": "^1.0.7",
"ts-jest": "^24.0.2",
"tslint": "^5.17.0",
- "typescript": "~3.5.2"
+ "typescript": "~3.7.4"
},
"dependencies": {
"@oclif/command": "^1.5.18",
@@ -86,17 +86,17 @@
"flatted": "^2.0.0",
"glob-promise": "^3.4.0",
"google-timezones-json": "^1.0.2",
- "inquirer": "^6.5.1",
+ "inquirer": "^7.0.1",
"jsonwebtoken": "^8.5.1",
"jwks-rsa": "^1.6.0",
- "localtunnel": "^1.9.1",
+ "localtunnel": "^2.0.0",
"lodash.get": "^4.4.2",
"mongodb": "^3.2.3",
"n8n-core": "~0.18.0",
"n8n-editor-ui": "~0.29.0",
- "n8n-nodes-base": "~0.35.0",
+ "n8n-nodes-base": "~0.36.0",
"n8n-workflow": "~0.18.0",
- "open": "^6.1.0",
+ "open": "^7.0.0",
"pg": "^7.11.0",
"request-promise-native": "^1.0.7",
"sqlite3": "^4.0.6",
diff --git a/packages/core/package.json b/packages/core/package.json
index 2fd061a6d57..716f466f02d 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,7 +28,7 @@
"@types/crypto-js": "^3.1.43",
"@types/express": "^4.16.1",
"@types/jest": "^24.0.18",
- "@types/lodash.get": "^4.4.5",
+ "@types/lodash.get": "^4.4.6",
"@types/mmmagic": "^0.4.29",
"@types/node": "^10.10.1",
"@types/request-promise-native": "^1.0.15",
@@ -36,7 +36,7 @@
"source-map-support": "^0.5.9",
"ts-jest": "^24.0.2",
"tslint": "^5.17.0",
- "typescript": "~3.5.2"
+ "typescript": "~3.7.4"
},
"dependencies": {
"crypto-js": "^3.1.9-1",
diff --git a/packages/editor-ui/.eslintrc.js b/packages/editor-ui/.eslintrc.js
index 25493bb780e..b2f7cd19817 100644
--- a/packages/editor-ui/.eslintrc.js
+++ b/packages/editor-ui/.eslintrc.js
@@ -5,7 +5,6 @@ module.exports = {
},
'extends': [
'plugin:vue/essential',
- '@vue/standard',
'@vue/typescript',
],
rules: {
@@ -18,6 +17,6 @@ module.exports = {
'no-labels': 0,
},
parserOptions: {
- parser: 'typescript-eslint-parser',
+ parser: '@typescript-eslint/parser',
},
};
diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json
index 2cbd05db4d2..333d9fde9e7 100644
--- a/packages/editor-ui/package.json
+++ b/packages/editor-ui/package.json
@@ -32,25 +32,29 @@
"@types/dateformat": "^3.0.0",
"@types/file-saver": "^2.0.1",
"@types/jest": "^24.0.18",
- "@types/lodash.get": "^4.4.5",
+ "@types/lodash.get": "^4.4.6",
"@types/lodash.set": "^4.3.6",
+ "@types/node": "12.12.22",
"@types/quill": "^2.0.1",
- "@vue/cli-plugin-babel": "^3.8.0",
- "@vue/cli-plugin-e2e-cypress": "^3.8.0",
- "@vue/cli-plugin-eslint": "^3.8.0",
- "@vue/cli-plugin-typescript": "~3.8.1",
- "@vue/cli-plugin-unit-jest": "^3.8.0",
- "@vue/cli-service": "^3.8.0",
- "@vue/eslint-config-standard": "^4.0.0",
- "@vue/eslint-config-typescript": "~3.2.0",
- "@vue/test-utils": "^1.0.0-beta.20",
- "axios": "^0.18.1",
+ "@typescript-eslint/eslint-plugin": "^2.13.0",
+ "@typescript-eslint/parser": "^2.13.0",
+ "@vue/cli-plugin-babel": "^4.1.2",
+ "@vue/cli-plugin-e2e-cypress": "^4.1.2",
+ "@vue/cli-plugin-eslint": "^4.1.2",
+ "@vue/cli-plugin-typescript": "~4.1.2",
+ "@vue/cli-plugin-unit-jest": "^4.1.2",
+ "@vue/cli-service": "^4.1.2",
+ "@vue/eslint-config-standard": "^5.0.1",
+ "@vue/eslint-config-typescript": "~5.0.1",
+ "@vue/test-utils": "^1.0.0-beta.24",
+ "axios": "^0.19.0",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "^10.0.1",
"dateformat": "^3.0.3",
- "element-ui": "~2.4.11",
- "eslint": "^5.8.0",
- "eslint-plugin-vue": "^5.0.0-0",
+ "element-ui": "~2.13.0",
+ "eslint": "^6.8.0",
+ "eslint-plugin-import": "^2.19.1",
+ "eslint-plugin-vue": "^6.1.1",
"file-saver": "^2.0.2",
"flatted": "^2.0.0",
"jquery": "^3.4.1",
@@ -64,18 +68,18 @@
"prismjs": "^1.17.1",
"quill": "^2.0.0-dev.3",
"quill-autoformat": "^0.1.1",
- "sass-loader": "^7.0.1",
+ "sass-loader": "^8.0.0",
"string-template-parser": "^1.2.6",
"ts-jest": "^24.0.2",
"tslint": "^5.17.0",
- "typescript": "~3.5.2",
+ "typescript": "~3.7.4",
"vue": "^2.6.9",
- "vue-cli-plugin-webpack-bundle-analyzer": "^1.3.0",
+ "vue-cli-plugin-webpack-bundle-analyzer": "^2.0.0",
"vue-json-tree": "^0.4.1",
"vue-prism-editor": "^0.3.0",
"vue-router": "^3.0.6",
"vue-template-compiler": "^2.5.17",
- "vue-typed-mixins": "^0.1.0",
+ "vue-typed-mixins": "^0.2.0",
"vuex": "^3.1.1"
}
}
diff --git a/packages/editor-ui/src/components/BinaryDataDisplay.vue b/packages/editor-ui/src/components/BinaryDataDisplay.vue
index b66fdd02eab..c9aca4856d5 100644
--- a/packages/editor-ui/src/components/BinaryDataDisplay.vue
+++ b/packages/editor-ui/src/components/BinaryDataDisplay.vue
@@ -21,14 +21,10 @@
diff --git a/packages/editor-ui/src/components/WorkflowOpen.vue b/packages/editor-ui/src/components/WorkflowOpen.vue
index 6fe447cfb9a..9592ecb4d55 100644
--- a/packages/editor-ui/src/components/WorkflowOpen.vue
+++ b/packages/editor-ui/src/components/WorkflowOpen.vue
@@ -104,13 +104,13 @@ export default mixins(
workflowData.updatedAt = this.convertToDisplayDate(workflowData.updatedAt as number);
});
this.isDataLoading = false;
- }
+ },
)
.catch(
(error: Error) => {
this.$showError(error, 'Problem loading workflows', 'There was a problem loading the workflows:');
this.isDataLoading = false;
- }
+ },
);
},
workflowActiveChanged (data: { id: string, active: boolean }) {
diff --git a/packages/editor-ui/src/components/WorkflowSettings.vue b/packages/editor-ui/src/components/WorkflowSettings.vue
index 9b804e6dc20..178d51a8135 100644
--- a/packages/editor-ui/src/components/WorkflowSettings.vue
+++ b/packages/editor-ui/src/components/WorkflowSettings.vue
@@ -185,7 +185,7 @@ export default mixins(
key: 'none',
value: 'Do not save',
},
- ]
+ ],
);
},
async loadSaveDataSuccessExecutionOptions () {
@@ -204,7 +204,7 @@ export default mixins(
key: 'none',
value: 'Do not save',
},
- ]
+ ],
);
},
async loadSaveManualOptions () {
diff --git a/packages/editor-ui/src/components/mixins/genericHelpers.ts b/packages/editor-ui/src/components/mixins/genericHelpers.ts
index 4b77564a545..a51eecd5479 100644
--- a/packages/editor-ui/src/components/mixins/genericHelpers.ts
+++ b/packages/editor-ui/src/components/mixins/genericHelpers.ts
@@ -63,7 +63,7 @@ export const genericHelpers = mixins(showMessage).extend({
text: 'Loading',
spinner: 'el-icon-loading',
background: 'rgba(255, 255, 255, 0.8)',
- }
+ },
);
},
stopLoading () {
diff --git a/packages/editor-ui/src/components/mixins/pushConnection.ts b/packages/editor-ui/src/components/mixins/pushConnection.ts
index eb4c8fd868b..f32423f6c90 100644
--- a/packages/editor-ui/src/components/mixins/pushConnection.ts
+++ b/packages/editor-ui/src/components/mixins/pushConnection.ts
@@ -122,7 +122,7 @@ export const pushConnection = mixins(
// @ts-ignore
receivedData = JSON.parse(event.data);
} catch (error) {
- console.error('The received push data is not valid JSON.');
+ console.error('The received push data is not valid JSON.'); // eslint-disable-line no-console
return;
}
diff --git a/packages/editor-ui/src/components/mixins/restApi.ts b/packages/editor-ui/src/components/mixins/restApi.ts
index 875e3661530..a72520718e4 100644
--- a/packages/editor-ui/src/components/mixins/restApi.ts
+++ b/packages/editor-ui/src/components/mixins/restApi.ts
@@ -1,7 +1,7 @@
import Vue from 'vue';
import { parse } from 'flatted';
-import axios, { AxiosRequestConfig } from 'axios';
+import axios, { AxiosRequestConfig, Method } from 'axios';
import {
IActivationError,
ICredentialsDecryptedResponse,
@@ -94,7 +94,7 @@ export const restApi = Vue.extend({
restApi (): IRestApi {
const self = this;
return {
- async makeRestApiRequest (method: string, endpoint: string, data?: IDataObject): Promise { // tslint:disable-line:no-any
+ async makeRestApiRequest (method: Method, endpoint: string, data?: IDataObject): Promise { // tslint:disable-line:no-any
try {
const options: AxiosRequestConfig = {
method,
diff --git a/packages/editor-ui/src/components/mixins/workflowHelpers.ts b/packages/editor-ui/src/components/mixins/workflowHelpers.ts
index 6c39c61ce49..e12eeb21a04 100644
--- a/packages/editor-ui/src/components/mixins/workflowHelpers.ts
+++ b/packages/editor-ui/src/components/mixins/workflowHelpers.ts
@@ -370,7 +370,7 @@ export const workflowHelpers = mixins(
{
confirmButtonText: 'Save',
cancelButtonText: 'Cancel',
- }
+ },
)
.then((data) => {
// @ts-ignore
@@ -403,7 +403,7 @@ export const workflowHelpers = mixins(
if (currentWorkflow === undefined || withNewName === true) {
// Workflow is new or is supposed to get saved under a new name
// so create a new entry in database
- workflowData.name = workflowName.trim() as string;
+ workflowData.name = workflowName!.trim() as string;
if (withNewName === true) {
// If an existing workflow gets resaved with a new name
diff --git a/packages/editor-ui/src/components/mixins/workflowSave.ts b/packages/editor-ui/src/components/mixins/workflowSave.ts
index 25963290326..1584d9a7c8e 100644
--- a/packages/editor-ui/src/components/mixins/workflowSave.ts
+++ b/packages/editor-ui/src/components/mixins/workflowSave.ts
@@ -29,7 +29,7 @@ export const workflowSave = mixins(
{
confirmButtonText: 'Save',
cancelButtonText: 'Cancel',
- }
+ },
)
.then((data) => {
// @ts-ignore
@@ -62,7 +62,7 @@ export const workflowSave = mixins(
if (currentWorkflow === undefined || withNewName === true) {
// Workflow is new or is supposed to get saved under a new name
// so create a new entry in database
- workflowData.name = workflowName.trim() as string;
+ workflowData.name = workflowName!.trim() as string;
if (withNewName === true) {
// If an existing workflow gets resaved with a new name
diff --git a/packages/editor-ui/src/main.ts b/packages/editor-ui/src/main.ts
index 9cfa172e336..048a718d96e 100644
--- a/packages/editor-ui/src/main.ts
+++ b/packages/editor-ui/src/main.ts
@@ -34,6 +34,7 @@ import {
faClone,
faCloud,
faCopy,
+ faCut,
faDotCircle,
faEdit,
faEnvelope,
@@ -106,6 +107,7 @@ library.add(faCogs);
library.add(faClone);
library.add(faCloud);
library.add(faCopy);
+library.add(faCut);
library.add(faDotCircle);
library.add(faEdit);
library.add(faEnvelope);
diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue
index ba5abdbb40a..b9db49e03fe 100644
--- a/packages/editor-ui/src/views/NodeView.vue
+++ b/packages/editor-ui/src/views/NodeView.vue
@@ -102,6 +102,7 @@