From db846d3235a360b4b729312b6ffe0d75be08fd45 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ra=C3=BAl=20G=C3=B3mez=20Morales?=
Date: Mon, 16 Sep 2024 08:36:40 +0200
Subject: [PATCH 01/42] fix(editor): Prevent clipboard XSS injection (#10805)
---
.../__tests__/useClipboard.test.ts | 15 ++++++++-
.../editor-ui/src/composables/useClipboard.ts | 3 +-
.../editor-ui/src/composables/useMessage.ts | 31 +++++++++++++------
packages/editor-ui/src/utils/htmlUtils.ts | 11 +++++++
4 files changed, 48 insertions(+), 12 deletions(-)
diff --git a/packages/editor-ui/src/composables/__tests__/useClipboard.test.ts b/packages/editor-ui/src/composables/__tests__/useClipboard.test.ts
index 6c4b977a0b..cf2637fb5a 100644
--- a/packages/editor-ui/src/composables/__tests__/useClipboard.test.ts
+++ b/packages/editor-ui/src/composables/__tests__/useClipboard.test.ts
@@ -1,4 +1,4 @@
-import { render } from '@testing-library/vue';
+import { render, within } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import { defineComponent, h, ref } from 'vue';
import { useClipboard } from '@/composables/useClipboard';
@@ -8,9 +8,13 @@ const testValue = 'This is a test';
const TestComponent = defineComponent({
setup() {
const pasted = ref('');
+ const htmlContent = ref();
const clipboard = useClipboard({
onPaste(data) {
pasted.value = data;
+ if (htmlContent.value) {
+ htmlContent.value.innerHTML = data;
+ }
},
});
@@ -23,6 +27,7 @@ const TestComponent = defineComponent({
},
}),
h('div', { 'data-test-id': 'paste' }, pasted.value),
+ h('div', { 'data-test-id': 'xss-attack', ref: htmlContent }),
]);
},
});
@@ -68,4 +73,12 @@ describe('useClipboard()', () => {
expect(pasteElement.textContent).toEqual(testValue);
});
});
+
+ it('sanitizes HTML', async () => {
+ const unsafeHtml = 'https://www.ex.com/sfefdfd fdf/xdfef.json';
+ const { getByTestId } = render(TestComponent);
+
+ await userEvent.paste(unsafeHtml);
+ expect(within(getByTestId('xss-attack')).queryByRole('img')).not.toBeInTheDocument();
+ });
});
diff --git a/packages/editor-ui/src/composables/useClipboard.ts b/packages/editor-ui/src/composables/useClipboard.ts
index 1cd4c9ef70..9401b95c45 100644
--- a/packages/editor-ui/src/composables/useClipboard.ts
+++ b/packages/editor-ui/src/composables/useClipboard.ts
@@ -1,6 +1,7 @@
import { onBeforeUnmount, onMounted, ref } from 'vue';
import { useClipboard as useClipboardCore } from '@vueuse/core';
import { useDebounce } from '@/composables/useDebounce';
+import { sanitizeIfString } from '@/utils/htmlUtils';
type ClipboardEventFn = (data: string, event?: ClipboardEvent) => void;
@@ -42,7 +43,7 @@ export function useClipboard(
const clipboardData = event.clipboardData;
if (clipboardData !== null) {
- const clipboardValue = clipboardData.getData('text/plain');
+ const clipboardValue = sanitizeIfString(clipboardData.getData('text/plain'));
onPasteCallback.value(clipboardValue, event);
}
}
diff --git a/packages/editor-ui/src/composables/useMessage.ts b/packages/editor-ui/src/composables/useMessage.ts
index e24dc4579b..22bb50fc69 100644
--- a/packages/editor-ui/src/composables/useMessage.ts
+++ b/packages/editor-ui/src/composables/useMessage.ts
@@ -1,5 +1,6 @@
import type { ElMessageBoxOptions, Action, MessageBoxInputData } from 'element-plus';
import { ElMessageBox as MessageBox } from 'element-plus';
+import { sanitizeIfString } from '@/utils/htmlUtils';
export type MessageBoxConfirmResult = 'confirm' | 'cancel';
@@ -28,11 +29,13 @@ export function useMessage() {
};
if (typeof configOrTitle === 'string') {
- return await MessageBox.alert(message, configOrTitle, resolvedConfig).catch(
+ return await MessageBox.alert(sanitizeIfString(message), configOrTitle, resolvedConfig).catch(
handleCancelOrClose,
);
}
- return await MessageBox.alert(message, resolvedConfig).catch(handleCancelOrClose);
+ return await MessageBox.alert(sanitizeIfString(message), resolvedConfig).catch(
+ handleCancelOrClose,
+ );
}
async function confirm(
@@ -50,12 +53,16 @@ export function useMessage() {
};
if (typeof configOrTitle === 'string') {
- return await MessageBox.confirm(message, configOrTitle, resolvedConfig).catch(
- handleCancelOrClose,
- );
+ return await MessageBox.confirm(
+ sanitizeIfString(message),
+ sanitizeIfString(configOrTitle),
+ resolvedConfig,
+ ).catch(handleCancelOrClose);
}
- return await MessageBox.confirm(message, resolvedConfig).catch(handleCancelOrClose);
+ return await MessageBox.confirm(sanitizeIfString(message), resolvedConfig).catch(
+ handleCancelOrClose,
+ );
}
async function prompt(
@@ -70,11 +77,15 @@ export function useMessage() {
};
if (typeof configOrTitle === 'string') {
- return await MessageBox.prompt(message, configOrTitle, resolvedConfig).catch(
- handleCancelOrClosePrompt,
- );
+ return await MessageBox.prompt(
+ sanitizeIfString(message),
+ sanitizeIfString(configOrTitle),
+ resolvedConfig,
+ ).catch(handleCancelOrClosePrompt);
}
- return await MessageBox.prompt(message, resolvedConfig).catch(handleCancelOrClosePrompt);
+ return await MessageBox.prompt(sanitizeIfString(message), resolvedConfig).catch(
+ handleCancelOrClosePrompt,
+ );
}
return {
diff --git a/packages/editor-ui/src/utils/htmlUtils.ts b/packages/editor-ui/src/utils/htmlUtils.ts
index 5da34b3965..2a78582b01 100644
--- a/packages/editor-ui/src/utils/htmlUtils.ts
+++ b/packages/editor-ui/src/utils/htmlUtils.ts
@@ -37,6 +37,17 @@ export function sanitizeHtml(dirtyHtml: string) {
return sanitizedHtml;
}
+/**
+ * Checks if the input is a string and sanitizes it by removing or escaping harmful characters,
+ * returning the original input if it's not a string.
+ */
+export const sanitizeIfString = (message: T): string | T => {
+ if (typeof message === 'string') {
+ return sanitizeHtml(message);
+ }
+ return message;
+};
+
export function setPageTitle(title: string) {
window.document.title = title;
}
From 460fe14ae123d04af58d4c87986ecc3e81d494b8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?=
Date: Mon, 16 Sep 2024 10:29:28 +0200
Subject: [PATCH 02/42] chore: Upgrade to TypeScript 5.6 (#10822)
---
package.json | 2 +-
.../cli/src/controllers/e2e.controller.ts | 1 +
packages/editor-ui/src/views/NodeView.vue | 2 +
pnpm-lock.yaml | 852 +++++++++---------
4 files changed, 430 insertions(+), 427 deletions(-)
diff --git a/package.json b/package.json
index d854a0c577..93ec1b5369 100644
--- a/package.json
+++ b/package.json
@@ -73,7 +73,7 @@
"semver": "^7.5.4",
"tslib": "^2.6.2",
"tsconfig-paths": "^4.2.0",
- "typescript": "^5.5.2",
+ "typescript": "^5.6.2",
"ws": ">=8.17.1"
},
"patchedDependencies": {
diff --git a/packages/cli/src/controllers/e2e.controller.ts b/packages/cli/src/controllers/e2e.controller.ts
index a9e10b15b3..2e4d39acc0 100644
--- a/packages/cli/src/controllers/e2e.controller.ts
+++ b/packages/cli/src/controllers/e2e.controller.ts
@@ -115,6 +115,7 @@ export class E2EController {
) {
license.isFeatureEnabled = (feature: BooleanLicenseFeature) =>
this.enabledFeatures[feature] ?? false;
+ // @ts-expect-error Overriding method
// eslint-disable-next-line @typescript-eslint/unbound-method
license.getFeatureValue = (feature: NumericLicenseFeature) =>
this.numericFeatures[feature] ?? UNLIMITED_LICENSE_QUOTA;
diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue
index b22ce73d3d..a792ff135a 100644
--- a/packages/editor-ui/src/views/NodeView.vue
+++ b/packages/editor-ui/src/views/NodeView.vue
@@ -2926,6 +2926,7 @@ export default defineComponent({
}
if (
+ // @ts-expect-error Deprecated file
// eslint-disable-next-line no-constant-binary-expression
!(this.workflowPermissions.update ?? this.projectPermissions.workflow.update) ??
this.isReadOnlyRoute ??
@@ -2965,6 +2966,7 @@ export default defineComponent({
}
if (
+ // @ts-expect-error Deprecated file
// eslint-disable-next-line no-constant-binary-expression
!(this.workflowPermissions.update ?? this.projectPermissions.workflow.update) ??
this.isReadOnlyRoute ??
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 76e220a796..131970bbf7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -93,7 +93,7 @@ overrides:
semver: ^7.5.4
tslib: ^2.6.2
tsconfig-paths: ^4.2.0
- typescript: ^5.5.2
+ typescript: ^5.6.2
ws: '>=8.17.1'
patchedDependencies:
@@ -134,7 +134,7 @@ importers:
version: 6.0.2
jest:
specifier: ^29.6.2
- version: 29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2))
+ version: 29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2))
jest-environment-jsdom:
specifier: ^29.6.2
version: 29.6.2
@@ -146,7 +146,7 @@ importers:
version: 29.6.2
jest-mock-extended:
specifier: ^3.0.4
- version: 3.0.4(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)))(typescript@5.5.2)
+ version: 3.0.4(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)))(typescript@5.6.2)
nock:
specifier: ^13.3.2
version: 13.3.2
@@ -167,19 +167,19 @@ importers:
version: 7.0.0
ts-jest:
specifier: ^29.1.1
- version: 29.1.1(@babel/core@7.24.0)(@jest/types@29.6.1)(babel-jest@29.6.2(@babel/core@7.24.0))(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)))(typescript@5.5.2)
+ version: 29.1.1(@babel/core@7.24.0)(@jest/types@29.6.1)(babel-jest@29.6.2(@babel/core@7.24.0))(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)))(typescript@5.6.2)
tsc-alias:
specifier: ^1.8.7
version: 1.8.7
tsc-watch:
specifier: ^6.0.4
- version: 6.0.4(typescript@5.5.2)
+ version: 6.0.4(typescript@5.6.2)
turbo:
specifier: 2.0.6
version: 2.0.6
typescript:
- specifier: ^5.5.2
- version: 5.5.2
+ specifier: ^5.6.2
+ version: 5.6.2
zx:
specifier: ^8.1.4
version: 8.1.4
@@ -258,14 +258,14 @@ importers:
specifier: ^1.8.7
version: 1.8.7
typescript:
- specifier: ^5.5.2
- version: 5.5.2
+ specifier: ^5.6.2
+ version: 5.6.2
packages/@n8n/chat:
dependencies:
'@vueuse/core':
specifier: ^10.11.0
- version: 10.11.0(vue@3.4.21(typescript@5.5.2))
+ version: 10.11.0(vue@3.4.21(typescript@5.6.2))
highlight.js:
specifier: catalog:frontend
version: 11.9.0
@@ -277,10 +277,10 @@ importers:
version: 8.3.2
vue:
specifier: catalog:frontend
- version: 3.4.21(typescript@5.5.2)
+ version: 3.4.21(typescript@5.6.2)
vue-markdown-render:
specifier: catalog:frontend
- version: 2.2.1(vue@3.4.21(typescript@5.5.2))
+ version: 2.2.1(vue@3.4.21(typescript@5.6.2))
devDependencies:
'@iconify-json/mdi':
specifier: ^1.1.54
@@ -299,13 +299,13 @@ importers:
version: 5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1)
vite-plugin-dts:
specifier: ^3.9.1
- version: 3.9.1(@types/node@18.16.16)(rollup@4.18.0)(typescript@5.5.2)(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))
+ version: 3.9.1(@types/node@18.16.16)(rollup@4.18.0)(typescript@5.6.2)(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))
vitest:
specifier: catalog:frontend
version: 1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)
vue-tsc:
specifier: catalog:frontend
- version: 2.0.19(typescript@5.5.2)
+ version: 2.0.19(typescript@5.6.2)
packages/@n8n/client-oauth2:
dependencies:
@@ -406,10 +406,10 @@ importers:
version: 0.2.2(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(encoding@0.1.13)(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
'@langchain/community':
specifier: 0.2.32
- version: 0.2.32(ygqwlohwinok7tv3gruzwbtdoi)
+ version: 0.2.32(wjcprhsh6kptjwisshzaqzfaay)
'@langchain/core':
specifier: 'catalog:'
- version: 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ version: 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
'@langchain/google-genai':
specifier: 0.0.26
version: 0.0.26(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(zod@3.23.8)
@@ -427,25 +427,25 @@ importers:
version: 0.0.4(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
'@langchain/openai':
specifier: 0.2.10
- version: 0.2.10(encoding@0.1.13)(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))
+ version: 0.2.10(encoding@0.1.13)(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))
'@langchain/pinecone':
specifier: 0.0.9
- version: 0.0.9(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ version: 0.0.9(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
'@langchain/qdrant':
specifier: 0.0.5
- version: 0.0.5(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(typescript@5.5.2)
+ version: 0.0.5(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(typescript@5.6.2)
'@langchain/redis':
specifier: 0.0.5
- version: 0.0.5(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ version: 0.0.5(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
'@langchain/textsplitters':
specifier: 0.0.3
- version: 0.0.3(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ version: 0.0.3(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
'@mozilla/readability':
specifier: ^0.5.0
version: 0.5.0
'@n8n/typeorm':
specifier: 0.3.20-10
- version: 0.3.20-10(@sentry/node@7.87.0)(ioredis@5.3.2)(mssql@10.0.2)(mysql2@3.11.0)(pg@8.12.0)(redis@4.6.12)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2))
+ version: 0.3.20-10(@sentry/node@7.87.0)(ioredis@5.3.2)(mssql@10.0.2)(mysql2@3.11.0)(pg@8.12.0)(redis@4.6.12)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2))
'@n8n/vm2':
specifier: 3.9.25
version: 3.9.25
@@ -454,7 +454,7 @@ importers:
version: 3.0.0
'@qdrant/js-client-rest':
specifier: 1.9.0
- version: 1.9.0(typescript@5.5.2)
+ version: 1.9.0(typescript@5.6.2)
'@supabase/supabase-js':
specifier: 2.45.3
version: 2.45.3
@@ -463,7 +463,7 @@ importers:
version: 8.11.6
'@xata.io/client':
specifier: 0.28.4
- version: 0.28.4(typescript@5.5.2)
+ version: 0.28.4(typescript@5.6.2)
basic-auth:
specifier: 'catalog:'
version: 2.0.1
@@ -496,7 +496,7 @@ importers:
version: 2.1.0
langchain:
specifier: 0.2.18
- version: 0.2.18(xd7d7p3bjr65bts4h63vmtvguu)
+ version: 0.2.18(7abrqxy75ksyaxunbmagq3jube)
lodash:
specifier: 'catalog:'
version: 4.17.21
@@ -577,7 +577,7 @@ importers:
version: 8.1.4(@types/react@18.0.27)(encoding@0.1.13)(prettier@3.2.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@storybook/addon-interactions':
specifier: ^8.1.4
- version: 8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))
+ version: 8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))
'@storybook/addon-links':
specifier: ^8.1.4
version: 8.1.4(react@18.2.0)
@@ -589,13 +589,13 @@ importers:
version: 8.1.4(@types/react@18.0.27)(encoding@0.1.13)(prettier@3.2.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@storybook/test':
specifier: ^8.1.4
- version: 8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))
+ version: 8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))
'@storybook/vue3':
specifier: ^8.1.4
- version: 8.1.4(encoding@0.1.13)(prettier@3.2.5)(vue@3.4.21(typescript@5.5.2))
+ version: 8.1.4(encoding@0.1.13)(prettier@3.2.5)(vue@3.4.21(typescript@5.6.2))
'@storybook/vue3-vite':
specifier: ^8.1.4
- version: 8.1.4(encoding@0.1.13)(prettier@3.2.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))(vue@3.4.21(typescript@5.5.2))
+ version: 8.1.4(encoding@0.1.13)(prettier@3.2.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))(vue@3.4.21(typescript@5.6.2))
chromatic:
specifier: ^11.4.1
version: 11.4.1
@@ -610,31 +610,31 @@ importers:
version: 8.56.5
'@typescript-eslint/eslint-plugin':
specifier: ^7.2.0
- version: 7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)
+ version: 7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2)
'@typescript-eslint/parser':
specifier: ^7.2.0
- version: 7.2.0(eslint@8.57.0)(typescript@5.5.2)
+ version: 7.2.0(eslint@8.57.0)(typescript@5.6.2)
'@vue/eslint-config-prettier':
specifier: ^9.0.0
version: 9.0.0(@types/eslint@8.56.5)(eslint@8.57.0)(prettier@3.2.5)
'@vue/eslint-config-typescript':
specifier: ^13.0.0
- version: 13.0.0(eslint-plugin-vue@9.23.0(eslint@8.57.0))(eslint@8.57.0)(typescript@5.5.2)
+ version: 13.0.0(eslint-plugin-vue@9.23.0(eslint@8.57.0))(eslint@8.57.0)(typescript@5.6.2)
eslint:
specifier: ^8.57.0
version: 8.57.0
eslint-config-airbnb-typescript:
specifier: ^18.0.0
- version: 18.0.0(@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0)
+ version: 18.0.0(@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2))(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0)
eslint-config-prettier:
specifier: ^9.1.0
version: 9.1.0(eslint@8.57.0)
eslint-import-resolver-typescript:
specifier: ^3.6.1
- version: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0)
+ version: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0)
eslint-plugin-import:
specifier: ^2.29.1
- version: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
+ version: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
eslint-plugin-lodash:
specifier: ^7.4.0
version: 7.4.0(eslint@8.57.0)
@@ -649,7 +649,7 @@ importers:
version: 51.0.1(eslint@8.57.0)
eslint-plugin-unused-imports:
specifier: ^3.1.0
- version: 3.1.0(@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)
+ version: 3.1.0(@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)
eslint-plugin-vue:
specifier: ^9.23.0
version: 9.23.0(eslint@8.57.0)
@@ -688,7 +688,7 @@ importers:
version: link:../@n8n/permissions
'@n8n/typeorm':
specifier: 0.3.20-10
- version: 0.3.20-10(@sentry/node@7.87.0)(ioredis@5.3.2)(mssql@10.0.2)(mysql2@3.11.0)(pg@8.12.0)(redis@4.6.14)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2))
+ version: 0.3.20-10(@sentry/node@7.87.0)(ioredis@5.3.2)(mssql@10.0.2)(mysql2@3.11.0)(pg@8.12.0)(redis@4.6.14)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2))
'@n8n_io/ai-assistant-sdk':
specifier: 1.9.4
version: 1.9.4
@@ -1031,7 +1031,7 @@ importers:
version: 4.15.3(encoding@0.1.13)
ts-essentials:
specifier: ^7.0.3
- version: 7.0.3(typescript@5.5.2)
+ version: 7.0.3(typescript@5.6.2)
packages/core:
dependencies:
@@ -1134,10 +1134,10 @@ importers:
version: 5.15.4
'@fortawesome/vue-fontawesome':
specifier: ^3.0.3
- version: 3.0.3(@fortawesome/fontawesome-svg-core@1.2.36)(vue@3.4.21(typescript@5.5.2))
+ version: 3.0.3(@fortawesome/fontawesome-svg-core@1.2.36)(vue@3.4.21(typescript@5.6.2))
element-plus:
specifier: 2.4.3
- version: 2.4.3(vue@3.4.21(typescript@5.5.2))
+ version: 2.4.3(vue@3.4.21(typescript@5.6.2))
markdown-it:
specifier: ^13.0.2
version: 13.0.2
@@ -1158,13 +1158,13 @@ importers:
version: 2.12.1
vue:
specifier: catalog:frontend
- version: 3.4.21(typescript@5.5.2)
+ version: 3.4.21(typescript@5.6.2)
vue-boring-avatars:
specifier: ^1.3.0
- version: 1.3.0(vue@3.4.21(typescript@5.5.2))
+ version: 1.3.0(vue@3.4.21(typescript@5.6.2))
vue-router:
specifier: ^4.2.2
- version: 4.2.2(vue@3.4.21(typescript@5.5.2))
+ version: 4.2.2(vue@3.4.21(typescript@5.6.2))
xss:
specifier: ^1.0.14
version: 1.0.14
@@ -1174,13 +1174,13 @@ importers:
version: link:../@n8n/storybook
'@testing-library/jest-dom':
specifier: ^6.1.5
- version: 6.1.5(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)))(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1))
+ version: 6.1.5(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)))(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1))
'@testing-library/user-event':
specifier: ^14.5.1
version: 14.5.1(@testing-library/dom@9.3.4)
'@testing-library/vue':
specifier: ^8.0.1
- version: 8.0.1(@vue/compiler-sfc@3.4.21)(@vue/server-renderer@3.4.21(vue@3.4.21(typescript@5.5.2)))(vue@3.4.21(typescript@5.5.2))
+ version: 8.0.1(@vue/compiler-sfc@3.4.21)(@vue/server-renderer@3.4.21(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))
'@types/markdown-it':
specifier: ^13.0.9
version: 13.0.9
@@ -1195,13 +1195,13 @@ importers:
version: 2.11.0
'@vitejs/plugin-vue':
specifier: ^5.0.4
- version: 5.0.4(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))(vue@3.4.21(typescript@5.5.2))
+ version: 5.0.4(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))(vue@3.4.21(typescript@5.6.2))
'@vitest/coverage-v8':
specifier: catalog:frontend
version: 1.6.0(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1))
'@vue/test-utils':
specifier: ^2.4.3
- version: 2.4.3(@vue/server-renderer@3.4.21(vue@3.4.21(typescript@5.5.2)))(vue@3.4.21(typescript@5.5.2))
+ version: 2.4.3(@vue/server-renderer@3.4.21(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))
autoprefixer:
specifier: ^10.4.19
version: 10.4.19(postcss@8.4.38)
@@ -1213,13 +1213,13 @@ importers:
version: 1.64.1
tailwindcss:
specifier: ^3.4.3
- version: 3.4.3(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2))
+ version: 3.4.3(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2))
unplugin-icons:
specifier: ^0.19.0
version: 0.19.0(@vue/compiler-sfc@3.4.21)(vue-template-compiler@2.7.14)
unplugin-vue-components:
specifier: ^0.27.2
- version: 0.27.3(@babel/parser@7.24.6)(rollup@4.18.0)(vue@3.4.21(typescript@5.5.2))
+ version: 0.27.3(@babel/parser@7.24.6)(rollup@4.18.0)(vue@3.4.21(typescript@5.6.2))
vite:
specifier: catalog:frontend
version: 5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1)
@@ -1228,10 +1228,10 @@ importers:
version: 1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)
vitest-mock-extended:
specifier: catalog:frontend
- version: 1.3.1(typescript@5.5.2)(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1))
+ version: 1.3.1(typescript@5.6.2)(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1))
vue-tsc:
specifier: catalog:frontend
- version: 2.0.19(typescript@5.5.2)
+ version: 2.0.19(typescript@5.6.2)
packages/editor-ui:
dependencies:
@@ -1276,7 +1276,7 @@ importers:
version: 5.15.4
'@fortawesome/vue-fontawesome':
specifier: '*'
- version: 3.0.3(@fortawesome/fontawesome-svg-core@1.2.36)(vue@3.4.21(typescript@5.5.2))
+ version: 3.0.3(@fortawesome/fontawesome-svg-core@1.2.36)(vue@3.4.21(typescript@5.6.2))
'@jsplumb/browser-ui':
specifier: ^5.13.2
version: 5.13.2
@@ -1312,25 +1312,25 @@ importers:
version: link:../@n8n/permissions
'@vue-flow/background':
specifier: ^1.3.0
- version: 1.3.0(@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.5.2)))(vue@3.4.21(typescript@5.5.2))
+ version: 1.3.0(@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))
'@vue-flow/controls':
specifier: ^1.1.1
- version: 1.1.1(@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.5.2)))(vue@3.4.21(typescript@5.5.2))
+ version: 1.1.1(@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))
'@vue-flow/core':
specifier: ^1.33.5
- version: 1.33.5(vue@3.4.21(typescript@5.5.2))
+ version: 1.33.5(vue@3.4.21(typescript@5.6.2))
'@vue-flow/minimap':
specifier: ^1.4.0
- version: 1.4.0(@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.5.2)))(vue@3.4.21(typescript@5.5.2))
+ version: 1.4.0(@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))
'@vue-flow/node-resizer':
specifier: ^1.4.0
- version: 1.4.0(@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.5.2)))(vue@3.4.21(typescript@5.5.2))
+ version: 1.4.0(@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))
'@vueuse/components':
specifier: ^10.11.0
- version: 10.11.0(vue@3.4.21(typescript@5.5.2))
+ version: 10.11.0(vue@3.4.21(typescript@5.6.2))
'@vueuse/core':
specifier: ^10.11.0
- version: 10.11.0(vue@3.4.21(typescript@5.5.2))
+ version: 10.11.0(vue@3.4.21(typescript@5.6.2))
axios:
specifier: 'catalog:'
version: 1.7.4(debug@4.3.6)
@@ -1384,13 +1384,13 @@ importers:
version: link:../workflow
pinia:
specifier: ^2.1.6
- version: 2.1.6(typescript@5.5.2)(vue@3.4.21(typescript@5.5.2))
+ version: 2.1.6(typescript@5.6.2)(vue@3.4.21(typescript@5.6.2))
prettier:
specifier: ^3.2.5
version: 3.2.5
qrcode.vue:
specifier: ^3.3.4
- version: 3.3.4(vue@3.4.21(typescript@5.5.2))
+ version: 3.3.4(vue@3.4.21(typescript@5.6.2))
stream-browserify:
specifier: ^3.0.0
version: 3.0.0
@@ -1405,25 +1405,25 @@ importers:
version: 1.2.2
vue:
specifier: catalog:frontend
- version: 3.4.21(typescript@5.5.2)
+ version: 3.4.21(typescript@5.6.2)
vue-agile:
specifier: ^2.0.0
version: 2.0.0
vue-chartjs:
specifier: ^5.2.0
- version: 5.2.0(chart.js@4.4.0)(vue@3.4.21(typescript@5.5.2))
+ version: 5.2.0(chart.js@4.4.0)(vue@3.4.21(typescript@5.6.2))
vue-i18n:
specifier: ^9.2.2
- version: 9.2.2(vue@3.4.21(typescript@5.5.2))
+ version: 9.2.2(vue@3.4.21(typescript@5.6.2))
vue-json-pretty:
specifier: 2.2.4
- version: 2.2.4(vue@3.4.21(typescript@5.5.2))
+ version: 2.2.4(vue@3.4.21(typescript@5.6.2))
vue-markdown-render:
specifier: catalog:frontend
- version: 2.2.1(vue@3.4.21(typescript@5.5.2))
+ version: 2.2.1(vue@3.4.21(typescript@5.6.2))
vue-router:
specifier: ^4.2.2
- version: 4.2.2(vue@3.4.21(typescript@5.5.2))
+ version: 4.2.2(vue@3.4.21(typescript@5.6.2))
vue3-touch-events:
specifier: ^4.1.3
version: 4.1.3
@@ -1439,7 +1439,7 @@ importers:
version: 2.2.230
'@pinia/testing':
specifier: ^0.1.3
- version: 0.1.3(pinia@2.1.6(typescript@5.5.2)(vue@3.4.21(typescript@5.5.2)))(vue@3.4.21(typescript@5.5.2))
+ version: 0.1.3(pinia@2.1.6(typescript@5.6.2)(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))
'@sentry/vite-plugin':
specifier: ^2.5.0
version: 2.5.0(encoding@0.1.13)
@@ -1478,7 +1478,7 @@ importers:
version: 0.19.0(@vue/compiler-sfc@3.4.21)(vue-template-compiler@2.7.14)
unplugin-vue-components:
specifier: ^0.27.2
- version: 0.27.3(@babel/parser@7.24.6)(rollup@4.18.0)(vue@3.4.21(typescript@5.5.2))
+ version: 0.27.3(@babel/parser@7.24.6)(rollup@4.18.0)(vue@3.4.21(typescript@5.6.2))
vite:
specifier: catalog:frontend
version: 5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1)
@@ -1487,10 +1487,10 @@ importers:
version: 1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)
vitest-mock-extended:
specifier: catalog:frontend
- version: 1.3.1(typescript@5.5.2)(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1))
+ version: 1.3.1(typescript@5.6.2)(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1))
vue-tsc:
specifier: catalog:frontend
- version: 2.0.19(typescript@5.5.2)
+ version: 2.0.19(typescript@5.6.2)
packages/node-dev:
dependencies:
@@ -1789,7 +1789,7 @@ importers:
version: 0.4.14
eslint-plugin-n8n-nodes-base:
specifier: ^1.16.2
- version: 1.16.2(eslint@8.57.0)(typescript@5.5.2)
+ version: 1.16.2(eslint@8.57.0)(typescript@5.6.2)
n8n-core:
specifier: workspace:*
version: link:../core
@@ -4451,7 +4451,7 @@ packages:
resolution: {integrity: sha512-YiX/IskbRCoAY2ujyPDI6FBcO0ygAS4pgkGaJ7DcrJFh4SZV2XHs+u0KM7mO72RWJn1eJQFF2PQwxG+401xxJg==}
engines: {node: '>=18.0.0', pnpm: '>=8'}
peerDependencies:
- typescript: ^5.5.2
+ typescript: ^5.6.2
'@qdrant/openapi-typescript-fetch@1.2.6':
resolution: {integrity: sha512-oQG/FejNpItrxRHoyctYvT3rwGZOnK4jr3JdppO/c78ktDvkWiPXPHNsrDf33K9sZdRb6PR7gi4noIapu5q4HA==}
@@ -5355,7 +5355,7 @@ packages:
resolution: {integrity: sha512-wKcLPqy8OvI5GeWJclrzeW2FaEMXFHLO0w6kIsim0XL4QDSGSPTz+aEzVJYmhuwYFbRY/lpMkdWC4ouv7xSwWQ==}
peerDependencies:
'@preact/preset-vite': '*'
- typescript: ^5.5.2
+ typescript: ^5.6.2
vite: ^4.0.0 || ^5.0.0
vite-plugin-glimmerx: '*'
peerDependenciesMeta:
@@ -6255,7 +6255,7 @@ packages:
peerDependencies:
eslint: ^8.56.0
eslint-plugin-vue: ^9.0.0
- typescript: ^5.5.2
+ typescript: ^5.6.2
peerDependenciesMeta:
typescript:
optional: true
@@ -6263,7 +6263,7 @@ packages:
'@vue/language-core@1.8.27':
resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==}
peerDependencies:
- typescript: ^5.5.2
+ typescript: ^5.6.2
peerDependenciesMeta:
typescript:
optional: true
@@ -6271,7 +6271,7 @@ packages:
'@vue/language-core@2.0.19':
resolution: {integrity: sha512-A9EGOnvb51jOvnCYoRLnMP+CcoPlbZVxI9gZXE/y2GksRWM6j/PrLEIC++pnosWTN08tFpJgxhSS//E9v/Sg+Q==}
peerDependencies:
- typescript: ^5.5.2
+ typescript: ^5.6.2
peerDependenciesMeta:
typescript:
optional: true
@@ -6329,7 +6329,7 @@ packages:
'@xata.io/client@0.28.4':
resolution: {integrity: sha512-B02WHIA/ViHya84XvH6JCo13rd5h4S5vVyY2aYi6fIcjDIbCpsSLJ4oGWpdodovRYeAZy9Go4OhdyZwMIRC4BQ==}
peerDependencies:
- typescript: ^5.5.2
+ typescript: ^5.6.2
'@xmldom/xmldom@0.8.6':
resolution: {integrity: sha512-uRjjusqpoqfmRkTaNuLJ2VohVr67Q5YwDATW3VU7PfzTj6IRaihGrYI7zckGZjxQPBIp63nfvJbM+Yu5ICh0Bg==}
@@ -9445,7 +9445,7 @@ packages:
resolution: {integrity: sha512-2ynEZ7IEJNrhrgshklDMhrOdnmW4Nt+PhkyRqZxRgpwMo7JjmFWMzyp0+eSyk+H9KK1QjXI5xTZIw6x7cVDcRg==}
peerDependencies:
jest: ^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0
- typescript: ^5.5.2
+ typescript: ^5.6.2
jest-mock@29.6.2:
resolution: {integrity: sha512-hoSv3lb3byzdKfwqCuT6uTscan471GUECqgNYykg6ob0yiAw3zYc7OrPnI9Qv8Wwoa4lC7AZ9hyS4AiIx5U2zg==}
@@ -11283,7 +11283,7 @@ packages:
resolution: {integrity: sha512-bIU6QuE5qZviMmct5XwCesXelb5VavdOWKWaB17ggk++NUwQWWbP5YnsONTk3b752QkW9sACiR81rorpeOMSvQ==}
peerDependencies:
'@vue/composition-api': ^1.4.0
- typescript: ^5.5.2
+ typescript: ^5.6.2
vue: ^2.6.14 || ^3.3.0
peerDependenciesMeta:
'@vue/composition-api':
@@ -12783,7 +12783,7 @@ packages:
resolution: {integrity: sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==}
engines: {node: '>=16.13.0'}
peerDependencies:
- typescript: ^5.5.2
+ typescript: ^5.6.2
ts-dedent@2.2.0:
resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==}
@@ -12792,12 +12792,12 @@ packages:
ts-essentials@7.0.3:
resolution: {integrity: sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==}
peerDependencies:
- typescript: ^5.5.2
+ typescript: ^5.6.2
ts-essentials@9.4.2:
resolution: {integrity: sha512-mB/cDhOvD7pg3YCLk2rOtejHjjdSi9in/IBYE13S+8WA5FBSraYf4V/ws55uvs0IvQ/l0wBOlXy5yBNZ9Bl8ZQ==}
peerDependencies:
- typescript: ^5.5.2
+ typescript: ^5.6.2
peerDependenciesMeta:
typescript:
optional: true
@@ -12822,7 +12822,7 @@ packages:
babel-jest: ^29.0.0
esbuild: '*'
jest: ^29.0.0
- typescript: ^5.5.2
+ typescript: ^5.6.2
peerDependenciesMeta:
'@babel/core':
optional: true
@@ -12843,7 +12843,7 @@ packages:
'@swc/core': '>=1.2.50'
'@swc/wasm': '>=1.2.50'
'@types/node': ^18.16.16
- typescript: ^5.5.2
+ typescript: ^5.6.2
peerDependenciesMeta:
'@swc/core':
optional: true
@@ -12867,7 +12867,7 @@ packages:
engines: {node: '>=12.12.0'}
hasBin: true
peerDependencies:
- typescript: ^5.5.2
+ typescript: ^5.6.2
tsconfig-paths@4.2.0:
resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==}
@@ -13017,8 +13017,8 @@ packages:
typedi@0.10.0:
resolution: {integrity: sha512-v3UJF8xm68BBj6AF4oQML3ikrfK2c9EmZUyLOfShpJuItAqVBHWP/KtpGinkSsIiP6EZyyb6Z3NXyW9dgS9X1w==}
- typescript@5.5.2:
- resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==}
+ typescript@5.6.2:
+ resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==}
engines: {node: '>=14.17'}
hasBin: true
@@ -13288,7 +13288,7 @@ packages:
resolution: {integrity: sha512-rVp2KM9Ue22NGWB8dNtWEr+KekN3rIgz1tWD050QnRGlriUCmaDwa7qA5zDEjbXg5lAXhYMSBJtx3q3hQIJZSg==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
- typescript: ^5.5.2
+ typescript: ^5.6.2
vite: '*'
peerDependenciesMeta:
vite:
@@ -13325,7 +13325,7 @@ packages:
vitest-mock-extended@1.3.1:
resolution: {integrity: sha512-OpghYjh4BDuQ/Mzs3lFMQ1QRk9D8/2O9T47MLUA5eLn7K4RWIy+MfIivYOWEyxjTENjsBnzgMihDjyNalN/K0Q==}
peerDependencies:
- typescript: ^5.5.2
+ typescript: ^5.6.2
vitest: '>=0.31.1'
vitest@1.6.0:
@@ -13374,7 +13374,7 @@ packages:
vue-component-meta@2.0.19:
resolution: {integrity: sha512-Iv6VWXnlkUyJZvgadxYLcZajb58qUuIrQUePGbm0yZQEKMTb2T09UK57hz35TU4lb7zobierICDKvzInEpOGpg==}
peerDependencies:
- typescript: ^5.5.2
+ typescript: ^5.6.2
peerDependenciesMeta:
typescript:
optional: true
@@ -13455,13 +13455,13 @@ packages:
resolution: {integrity: sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==}
hasBin: true
peerDependencies:
- typescript: ^5.5.2
+ typescript: ^5.6.2
vue-tsc@2.0.19:
resolution: {integrity: sha512-JWay5Zt2/871iodGF72cELIbcAoPyhJxq56mPPh+M2K7IwI688FMrFKc/+DvB05wDWEuCPexQJ6L10zSwzzapg==}
hasBin: true
peerDependencies:
- typescript: ^5.5.2
+ typescript: ^5.6.2
vue3-touch-events@4.1.3:
resolution: {integrity: sha512-uXTclRzn7de1mgiDIZ8N4J/wnWl1vBPLTWr60fqoLXu7ifhDKpl83Q2m9qA20KfEiAy+L4X/xXGc5ptGmdPh4A==}
@@ -13469,7 +13469,7 @@ packages:
vue@3.4.21:
resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==}
peerDependencies:
- typescript: ^5.5.2
+ typescript: ^5.6.2
peerDependenciesMeta:
typescript:
optional: true
@@ -16309,9 +16309,9 @@ snapshots:
'@discoveryjs/json-ext@0.5.7': {}
- '@element-plus/icons-vue@2.3.1(vue@3.4.21(typescript@5.5.2))':
+ '@element-plus/icons-vue@2.3.1(vue@3.4.21(typescript@5.6.2))':
dependencies:
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
'@emotion/is-prop-valid@1.2.1':
dependencies:
@@ -16453,10 +16453,10 @@ snapshots:
dependencies:
'@fortawesome/fontawesome-common-types': 0.2.36
- '@fortawesome/vue-fontawesome@3.0.3(@fortawesome/fontawesome-svg-core@1.2.36)(vue@3.4.21(typescript@5.5.2))':
+ '@fortawesome/vue-fontawesome@3.0.3(@fortawesome/fontawesome-svg-core@1.2.36)(vue@3.4.21(typescript@5.6.2))':
dependencies:
'@fortawesome/fontawesome-svg-core': 1.2.36
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
'@gar/promisify@1.1.3':
optional: true
@@ -16469,8 +16469,8 @@ snapshots:
url-join: 4.0.1
zod: 3.23.8
optionalDependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- langchain: 0.2.18(xd7d7p3bjr65bts4h63vmtvguu)
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ langchain: 0.2.18(7abrqxy75ksyaxunbmagq3jube)
transitivePeerDependencies:
- encoding
@@ -16478,7 +16478,7 @@ snapshots:
dependencies:
'@supercharge/promise-pool': 3.1.0
semver: 7.6.0
- typescript: 5.5.2
+ typescript: 5.6.2
'@google-ai/generativelanguage@2.5.0(encoding@0.1.13)':
dependencies:
@@ -16654,7 +16654,7 @@ snapshots:
jest-util: 29.6.2
slash: 3.0.0
- '@jest/core@29.6.2(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2))':
+ '@jest/core@29.6.2(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2))':
dependencies:
'@jest/console': 29.6.2
'@jest/reporters': 29.6.2
@@ -16668,7 +16668,7 @@ snapshots:
exit: 0.1.2
graceful-fs: 4.2.11
jest-changed-files: 29.5.0
- jest-config: 29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2))
+ jest-config: 29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2))
jest-haste-map: 29.6.2
jest-message-util: 29.6.2
jest-regex-util: 29.4.3
@@ -16893,7 +16893,7 @@ snapshots:
'@langchain/anthropic@0.2.16(encoding@0.1.13)(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))':
dependencies:
'@anthropic-ai/sdk': 0.25.2(encoding@0.1.13)
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
fast-xml-parser: 4.4.1
zod: 3.23.8
zod-to-json-schema: 3.23.2(zod@3.23.8)
@@ -16905,7 +16905,7 @@ snapshots:
'@langchain/cohere@0.2.2(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(encoding@0.1.13)(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))':
dependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
cohere-ai: 7.13.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(encoding@0.1.13)
uuid: 10.0.0
zod: 3.23.8
@@ -16917,16 +16917,16 @@ snapshots:
- langchain
- openai
- '@langchain/community@0.2.32(ygqwlohwinok7tv3gruzwbtdoi)':
+ '@langchain/community@0.2.32(wjcprhsh6kptjwisshzaqzfaay)':
dependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- '@langchain/openai': 0.2.10(encoding@0.1.13)(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/openai': 0.2.10(encoding@0.1.13)(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))
binary-extensions: 2.2.0
expr-eval: 2.0.2
flat: 5.0.2
js-yaml: 4.1.0
- langchain: 0.2.18(xd7d7p3bjr65bts4h63vmtvguu)
- langsmith: 0.1.39(@langchain/core@0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8)))(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ langchain: 0.2.18(7abrqxy75ksyaxunbmagq3jube)
+ langsmith: 0.1.39(@langchain/core@0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8)))(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
uuid: 10.0.0
zod: 3.23.8
zod-to-json-schema: 3.23.2(zod@3.23.8)
@@ -16943,13 +16943,13 @@ snapshots:
'@huggingface/inference': 2.8.0
'@mozilla/readability': 0.5.0
'@pinecone-database/pinecone': 3.0.0
- '@qdrant/js-client-rest': 1.9.0(typescript@5.5.2)
+ '@qdrant/js-client-rest': 1.9.0(typescript@5.6.2)
'@smithy/eventstream-codec': 2.2.0
'@smithy/protocol-http': 3.3.0
'@smithy/signature-v4': 2.2.1
'@smithy/util-utf8': 2.3.0
'@supabase/supabase-js': 2.45.3
- '@xata.io/client': 0.28.4(typescript@5.5.2)
+ '@xata.io/client': 0.28.4(typescript@5.6.2)
cheerio: 1.0.0-rc.12
cohere-ai: 7.13.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(encoding@0.1.13)
crypto-js: 4.2.0
@@ -16987,6 +16987,23 @@ snapshots:
- pyodide
- supports-color
+ '@langchain/core@0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))':
+ dependencies:
+ ansi-styles: 5.2.0
+ camelcase: 6.3.0
+ decamelize: 1.2.0
+ js-tiktoken: 1.0.12
+ langsmith: 0.1.51(@langchain/core@0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8)))(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ mustache: 4.2.0
+ p-queue: 6.6.2
+ p-retry: 4.6.2
+ uuid: 10.0.0
+ zod: 3.23.8
+ zod-to-json-schema: 3.23.2(zod@3.23.8)
+ transitivePeerDependencies:
+ - langchain
+ - openai
+
'@langchain/core@0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0(zod@3.23.8)))(openai@4.58.0(zod@3.23.8))':
dependencies:
ansi-styles: 5.2.0
@@ -17039,26 +17056,9 @@ snapshots:
- langchain
- openai
- '@langchain/core@0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))':
+ '@langchain/google-common@0.0.27(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(zod@3.23.8)':
dependencies:
- ansi-styles: 5.2.0
- camelcase: 6.3.0
- decamelize: 1.2.0
- js-tiktoken: 1.0.12
- langsmith: 0.1.51(@langchain/core@0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8)))(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- mustache: 4.2.0
- p-queue: 6.6.2
- p-retry: 4.6.2
- uuid: 10.0.0
- zod: 3.23.8
- zod-to-json-schema: 3.23.2(zod@3.23.8)
- transitivePeerDependencies:
- - langchain
- - openai
-
- '@langchain/google-common@0.0.27(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(zod@3.23.8)':
- dependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
uuid: 10.0.0
zod-to-json-schema: 3.23.2(zod@3.23.8)
transitivePeerDependencies:
@@ -17066,10 +17066,10 @@ snapshots:
- openai
- zod
- '@langchain/google-gauth@0.0.27(encoding@0.1.13)(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(zod@3.23.8)':
+ '@langchain/google-gauth@0.0.27(encoding@0.1.13)(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(zod@3.23.8)':
dependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- '@langchain/google-common': 0.0.27(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(zod@3.23.8)
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/google-common': 0.0.27(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(zod@3.23.8)
google-auth-library: 8.9.0(encoding@0.1.13)
transitivePeerDependencies:
- encoding
@@ -17081,7 +17081,7 @@ snapshots:
'@langchain/google-genai@0.0.26(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(zod@3.23.8)':
dependencies:
'@google/generative-ai': 0.7.1
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
zod-to-json-schema: 3.23.2(zod@3.23.8)
transitivePeerDependencies:
- langchain
@@ -17090,8 +17090,8 @@ snapshots:
'@langchain/google-vertexai@0.0.27(encoding@0.1.13)(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(zod@3.23.8)':
dependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- '@langchain/google-gauth': 0.0.27(encoding@0.1.13)(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(zod@3.23.8)
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/google-gauth': 0.0.27(encoding@0.1.13)(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(zod@3.23.8)
transitivePeerDependencies:
- encoding
- langchain
@@ -17101,8 +17101,8 @@ snapshots:
'@langchain/groq@0.0.17(encoding@0.1.13)(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))':
dependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- '@langchain/openai': 0.2.10(encoding@0.1.13)(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/openai': 0.2.10(encoding@0.1.13)(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))
groq-sdk: 0.5.0(encoding@0.1.13)
zod: 3.23.8
zod-to-json-schema: 3.23.2(zod@3.23.8)
@@ -17114,7 +17114,7 @@ snapshots:
'@langchain/mistralai@0.0.29(encoding@0.1.13)(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))':
dependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
'@mistralai/mistralai': 0.4.0(encoding@0.1.13)
uuid: 10.0.0
zod: 3.23.8
@@ -17126,16 +17126,16 @@ snapshots:
'@langchain/ollama@0.0.4(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))':
dependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
ollama: 0.5.6
uuid: 10.0.0
transitivePeerDependencies:
- langchain
- openai
- '@langchain/openai@0.2.10(encoding@0.1.13)(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))':
+ '@langchain/openai@0.2.10(encoding@0.1.13)(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))':
dependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
js-tiktoken: 1.0.12
openai: 4.58.0(encoding@0.1.13)(zod@3.23.8)
zod: 3.23.8
@@ -17171,9 +17171,9 @@ snapshots:
- supports-color
optional: true
- '@langchain/pinecone@0.0.9(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))':
+ '@langchain/pinecone@0.0.9(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))':
dependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
'@pinecone-database/pinecone': 3.0.0
flat: 5.0.2
uuid: 10.0.0
@@ -17181,24 +17181,32 @@ snapshots:
- langchain
- openai
- '@langchain/qdrant@0.0.5(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(typescript@5.5.2)':
+ '@langchain/qdrant@0.0.5(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(typescript@5.6.2)':
dependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- '@qdrant/js-client-rest': 1.9.0(typescript@5.5.2)
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@qdrant/js-client-rest': 1.9.0(typescript@5.6.2)
uuid: 9.0.1
transitivePeerDependencies:
- langchain
- openai
- typescript
- '@langchain/redis@0.0.5(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))':
+ '@langchain/redis@0.0.5(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))':
dependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
redis: 4.6.13
transitivePeerDependencies:
- langchain
- openai
+ '@langchain/textsplitters@0.0.3(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))':
+ dependencies:
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ js-tiktoken: 1.0.12
+ transitivePeerDependencies:
+ - langchain
+ - openai
+
'@langchain/textsplitters@0.0.3(langchain@0.2.18(axios@1.7.4)(openai@4.58.0(zod@3.23.8)))(openai@4.58.0(zod@3.23.8))':
dependencies:
'@langchain/core': 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0(zod@3.23.8)))(openai@4.58.0(zod@3.23.8))
@@ -17217,14 +17225,6 @@ snapshots:
- openai
optional: true
- '@langchain/textsplitters@0.0.3(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))':
- dependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- js-tiktoken: 1.0.12
- transitivePeerDependencies:
- - langchain
- - openai
-
'@lezer/common@1.1.0': {}
'@lezer/common@1.2.1': {}
@@ -17296,7 +17296,7 @@ snapshots:
resolve: 1.22.8
semver: 7.6.0
source-map: 0.6.1
- typescript: 5.5.2
+ typescript: 5.6.2
transitivePeerDependencies:
- '@types/node'
@@ -17372,7 +17372,7 @@ snapshots:
esprima-next: 5.8.4
recast: 0.22.0
- '@n8n/typeorm@0.3.20-10(@sentry/node@7.87.0)(ioredis@5.3.2)(mssql@10.0.2)(mysql2@3.11.0)(pg@8.12.0)(redis@4.6.12)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2))':
+ '@n8n/typeorm@0.3.20-10(@sentry/node@7.87.0)(ioredis@5.3.2)(mssql@10.0.2)(mysql2@3.11.0)(pg@8.12.0)(redis@4.6.12)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2))':
dependencies:
'@n8n/p-retry': 6.2.0-2
'@sqltools/formatter': 1.2.5
@@ -17399,11 +17399,11 @@ snapshots:
pg: 8.12.0
redis: 4.6.12
sqlite3: 5.1.7
- ts-node: 10.9.2(@types/node@18.16.16)(typescript@5.5.2)
+ ts-node: 10.9.2(@types/node@18.16.16)(typescript@5.6.2)
transitivePeerDependencies:
- supports-color
- '@n8n/typeorm@0.3.20-10(@sentry/node@7.87.0)(ioredis@5.3.2)(mssql@10.0.2)(mysql2@3.11.0)(pg@8.12.0)(redis@4.6.14)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2))':
+ '@n8n/typeorm@0.3.20-10(@sentry/node@7.87.0)(ioredis@5.3.2)(mssql@10.0.2)(mysql2@3.11.0)(pg@8.12.0)(redis@4.6.14)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2))':
dependencies:
'@n8n/p-retry': 6.2.0-2
'@sqltools/formatter': 1.2.5
@@ -17430,7 +17430,7 @@ snapshots:
pg: 8.12.0
redis: 4.6.14
sqlite3: 5.1.7
- ts-node: 10.9.2(@types/node@18.16.16)(typescript@5.5.2)
+ ts-node: 10.9.2(@types/node@18.16.16)(typescript@5.6.2)
transitivePeerDependencies:
- supports-color
@@ -17547,10 +17547,10 @@ snapshots:
cross-fetch: 3.1.8(encoding@0.1.13)
encoding: 0.1.13
- '@pinia/testing@0.1.3(pinia@2.1.6(typescript@5.5.2)(vue@3.4.21(typescript@5.5.2)))(vue@3.4.21(typescript@5.5.2))':
+ '@pinia/testing@0.1.3(pinia@2.1.6(typescript@5.6.2)(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))':
dependencies:
- pinia: 2.1.6(typescript@5.5.2)(vue@3.4.21(typescript@5.5.2))
- vue-demi: 0.14.5(vue@3.4.21(typescript@5.5.2))
+ pinia: 2.1.6(typescript@5.6.2)(vue@3.4.21(typescript@5.6.2))
+ vue-demi: 0.14.5(vue@3.4.21(typescript@5.6.2))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
@@ -17583,11 +17583,11 @@ snapshots:
'@protobufjs/utf8@1.1.0': {}
- '@qdrant/js-client-rest@1.9.0(typescript@5.5.2)':
+ '@qdrant/js-client-rest@1.9.0(typescript@5.6.2)':
dependencies:
'@qdrant/openapi-typescript-fetch': 1.2.6
'@sevinf/maybe': 0.5.0
- typescript: 5.5.2
+ typescript: 5.6.2
undici: 5.28.4
'@qdrant/openapi-typescript-fetch@1.2.6': {}
@@ -18852,11 +18852,11 @@ snapshots:
dependencies:
'@storybook/global': 5.0.0
- '@storybook/addon-interactions@8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))':
+ '@storybook/addon-interactions@8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))':
dependencies:
'@storybook/global': 5.0.0
'@storybook/instrumenter': 8.1.4
- '@storybook/test': 8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))
+ '@storybook/test': 8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))
'@storybook/types': 8.1.4
polished: 4.2.2
ts-dedent: 2.2.0
@@ -18952,7 +18952,7 @@ snapshots:
- prettier
- supports-color
- '@storybook/builder-vite@8.1.4(encoding@0.1.13)(prettier@3.2.5)(typescript@5.5.2)(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))':
+ '@storybook/builder-vite@8.1.4(encoding@0.1.13)(prettier@3.2.5)(typescript@5.6.2)(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))':
dependencies:
'@storybook/channels': 8.1.4
'@storybook/client-logger': 8.1.4
@@ -18973,7 +18973,7 @@ snapshots:
ts-dedent: 2.2.0
vite: 5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1)
optionalDependencies:
- typescript: 5.5.2
+ typescript: 5.6.2
transitivePeerDependencies:
- encoding
- prettier
@@ -19304,14 +19304,14 @@ snapshots:
- prettier
- supports-color
- '@storybook/test@8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))':
+ '@storybook/test@8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))':
dependencies:
'@storybook/client-logger': 8.1.4
'@storybook/core-events': 8.1.4
'@storybook/instrumenter': 8.1.4
'@storybook/preview-api': 8.1.4
'@testing-library/dom': 9.3.4
- '@testing-library/jest-dom': 6.4.2(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))
+ '@testing-library/jest-dom': 6.4.2(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))
'@testing-library/user-event': 14.5.2(@testing-library/dom@9.3.4)
'@vitest/expect': 1.3.1
'@vitest/spy': 1.3.1
@@ -19339,18 +19339,18 @@ snapshots:
'@types/express': 4.17.21
file-system-cache: 2.3.0
- '@storybook/vue3-vite@8.1.4(encoding@0.1.13)(prettier@3.2.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))(vue@3.4.21(typescript@5.5.2))':
+ '@storybook/vue3-vite@8.1.4(encoding@0.1.13)(prettier@3.2.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))(vue@3.4.21(typescript@5.6.2))':
dependencies:
- '@storybook/builder-vite': 8.1.4(encoding@0.1.13)(prettier@3.2.5)(typescript@5.5.2)(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))
+ '@storybook/builder-vite': 8.1.4(encoding@0.1.13)(prettier@3.2.5)(typescript@5.6.2)(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))
'@storybook/core-server': 8.1.4(encoding@0.1.13)(prettier@3.2.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@storybook/types': 8.1.4
- '@storybook/vue3': 8.1.4(encoding@0.1.13)(prettier@3.2.5)(vue@3.4.21(typescript@5.5.2))
+ '@storybook/vue3': 8.1.4(encoding@0.1.13)(prettier@3.2.5)(vue@3.4.21(typescript@5.6.2))
find-package-json: 1.2.0
magic-string: 0.30.8
- typescript: 5.5.2
+ typescript: 5.6.2
vite: 5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1)
- vue-component-meta: 2.0.19(typescript@5.5.2)
- vue-docgen-api: 4.76.0(vue@3.4.21(typescript@5.5.2))
+ vue-component-meta: 2.0.19(typescript@5.6.2)
+ vue-docgen-api: 4.76.0(vue@3.4.21(typescript@5.6.2))
transitivePeerDependencies:
- '@preact/preset-vite'
- bufferutil
@@ -19363,7 +19363,7 @@ snapshots:
- vite-plugin-glimmerx
- vue
- '@storybook/vue3@8.1.4(encoding@0.1.13)(prettier@3.2.5)(vue@3.4.21(typescript@5.5.2))':
+ '@storybook/vue3@8.1.4(encoding@0.1.13)(prettier@3.2.5)(vue@3.4.21(typescript@5.6.2))':
dependencies:
'@storybook/docs-tools': 8.1.4(encoding@0.1.13)(prettier@3.2.5)
'@storybook/global': 5.0.0
@@ -19373,7 +19373,7 @@ snapshots:
lodash: 4.17.21
ts-dedent: 2.2.0
type-fest: 2.19.0
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
vue-component-type-helpers: 2.1.6
transitivePeerDependencies:
- encoding
@@ -19458,7 +19458,7 @@ snapshots:
lz-string: 1.5.0
pretty-format: 27.5.1
- '@testing-library/jest-dom@6.1.5(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)))(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1))':
+ '@testing-library/jest-dom@6.1.5(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)))(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1))':
dependencies:
'@adobe/css-tools': 4.3.2
'@babel/runtime': 7.22.6
@@ -19471,10 +19471,10 @@ snapshots:
optionalDependencies:
'@jest/globals': 29.6.2
'@types/jest': 29.5.3
- jest: 29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2))
+ jest: 29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2))
vitest: 1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)
- '@testing-library/jest-dom@6.4.2(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))':
+ '@testing-library/jest-dom@6.4.2(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))':
dependencies:
'@adobe/css-tools': 4.3.2
'@babel/runtime': 7.23.6
@@ -19487,7 +19487,7 @@ snapshots:
optionalDependencies:
'@jest/globals': 29.6.2
'@types/jest': 29.5.3
- jest: 29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2))
+ jest: 29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2))
vitest: 1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)
'@testing-library/user-event@14.5.1(@testing-library/dom@9.3.4)':
@@ -19498,13 +19498,13 @@ snapshots:
dependencies:
'@testing-library/dom': 9.3.4
- '@testing-library/vue@8.0.1(@vue/compiler-sfc@3.4.21)(@vue/server-renderer@3.4.21(vue@3.4.21(typescript@5.5.2)))(vue@3.4.21(typescript@5.5.2))':
+ '@testing-library/vue@8.0.1(@vue/compiler-sfc@3.4.21)(@vue/server-renderer@3.4.21(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))':
dependencies:
'@babel/runtime': 7.23.6
'@testing-library/dom': 9.3.3
'@vue/compiler-sfc': 3.4.21
- '@vue/test-utils': 2.4.3(@vue/server-renderer@3.4.21(vue@3.4.21(typescript@5.5.2)))(vue@3.4.21(typescript@5.5.2))
- vue: 3.4.21(typescript@5.5.2)
+ '@vue/test-utils': 2.4.3(@vue/server-renderer@3.4.21(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))
+ vue: 3.4.21(typescript@5.6.2)
transitivePeerDependencies:
- '@vue/server-renderer'
@@ -20018,13 +20018,13 @@ snapshots:
'@types/node': 18.16.16
optional: true
- '@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)':
+ '@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2)':
dependencies:
'@eslint-community/regexpp': 4.6.2
- '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.5.2)
+ '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.6.2)
'@typescript-eslint/scope-manager': 7.2.0
- '@typescript-eslint/type-utils': 7.2.0(eslint@8.57.0)(typescript@5.5.2)
- '@typescript-eslint/utils': 7.2.0(eslint@8.57.0)(typescript@5.5.2)
+ '@typescript-eslint/type-utils': 7.2.0(eslint@8.57.0)(typescript@5.6.2)
+ '@typescript-eslint/utils': 7.2.0(eslint@8.57.0)(typescript@5.6.2)
'@typescript-eslint/visitor-keys': 7.2.0
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.57.0
@@ -20032,22 +20032,22 @@ snapshots:
ignore: 5.2.4
natural-compare: 1.4.0
semver: 7.6.0
- ts-api-utils: 1.0.1(typescript@5.5.2)
+ ts-api-utils: 1.0.1(typescript@5.6.2)
optionalDependencies:
- typescript: 5.5.2
+ typescript: 5.6.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2)':
+ '@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2)':
dependencies:
'@typescript-eslint/scope-manager': 7.2.0
'@typescript-eslint/types': 7.2.0
- '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.5.2)
+ '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.6.2)
'@typescript-eslint/visitor-keys': 7.2.0
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.57.0
optionalDependencies:
- typescript: 5.5.2
+ typescript: 5.6.2
transitivePeerDependencies:
- supports-color
@@ -20061,15 +20061,15 @@ snapshots:
'@typescript-eslint/types': 7.2.0
'@typescript-eslint/visitor-keys': 7.2.0
- '@typescript-eslint/type-utils@7.2.0(eslint@8.57.0)(typescript@5.5.2)':
+ '@typescript-eslint/type-utils@7.2.0(eslint@8.57.0)(typescript@5.6.2)':
dependencies:
- '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.5.2)
- '@typescript-eslint/utils': 7.2.0(eslint@8.57.0)(typescript@5.5.2)
+ '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.6.2)
+ '@typescript-eslint/utils': 7.2.0(eslint@8.57.0)(typescript@5.6.2)
debug: 4.3.6(supports-color@8.1.1)
eslint: 8.57.0
- ts-api-utils: 1.0.1(typescript@5.5.2)
+ ts-api-utils: 1.0.1(typescript@5.6.2)
optionalDependencies:
- typescript: 5.5.2
+ typescript: 5.6.2
transitivePeerDependencies:
- supports-color
@@ -20077,7 +20077,7 @@ snapshots:
'@typescript-eslint/types@7.2.0': {}
- '@typescript-eslint/typescript-estree@6.21.0(typescript@5.5.2)':
+ '@typescript-eslint/typescript-estree@6.21.0(typescript@5.6.2)':
dependencies:
'@typescript-eslint/types': 6.21.0
'@typescript-eslint/visitor-keys': 6.21.0
@@ -20086,13 +20086,13 @@ snapshots:
is-glob: 4.0.3
minimatch: 9.0.3
semver: 7.6.0
- ts-api-utils: 1.0.1(typescript@5.5.2)
+ ts-api-utils: 1.0.1(typescript@5.6.2)
optionalDependencies:
- typescript: 5.5.2
+ typescript: 5.6.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/typescript-estree@7.2.0(typescript@5.5.2)':
+ '@typescript-eslint/typescript-estree@7.2.0(typescript@5.6.2)':
dependencies:
'@typescript-eslint/types': 7.2.0
'@typescript-eslint/visitor-keys': 7.2.0
@@ -20101,34 +20101,34 @@ snapshots:
is-glob: 4.0.3
minimatch: 9.0.3
semver: 7.6.0
- ts-api-utils: 1.0.1(typescript@5.5.2)
+ ts-api-utils: 1.0.1(typescript@5.6.2)
optionalDependencies:
- typescript: 5.5.2
+ typescript: 5.6.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@6.21.0(eslint@8.57.0)(typescript@5.5.2)':
+ '@typescript-eslint/utils@6.21.0(eslint@8.57.0)(typescript@5.6.2)':
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
'@types/json-schema': 7.0.15
'@types/semver': 7.5.0
'@typescript-eslint/scope-manager': 6.21.0
'@typescript-eslint/types': 6.21.0
- '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.2)
+ '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.6.2)
eslint: 8.57.0
semver: 7.6.0
transitivePeerDependencies:
- supports-color
- typescript
- '@typescript-eslint/utils@7.2.0(eslint@8.57.0)(typescript@5.5.2)':
+ '@typescript-eslint/utils@7.2.0(eslint@8.57.0)(typescript@5.6.2)':
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
'@types/json-schema': 7.0.15
'@types/semver': 7.5.0
'@typescript-eslint/scope-manager': 7.2.0
'@typescript-eslint/types': 7.2.0
- '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.5.2)
+ '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.6.2)
eslint: 8.57.0
semver: 7.6.0
transitivePeerDependencies:
@@ -20147,10 +20147,10 @@ snapshots:
'@ungap/structured-clone@1.2.0': {}
- '@vitejs/plugin-vue@5.0.4(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))(vue@3.4.21(typescript@5.5.2))':
+ '@vitejs/plugin-vue@5.0.4(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))(vue@3.4.21(typescript@5.6.2))':
dependencies:
vite: 5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1)
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
'@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1))':
dependencies:
@@ -20243,39 +20243,39 @@ snapshots:
'@volar/language-core': 2.2.5
path-browserify: 1.0.1
- '@vue-flow/background@1.3.0(@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.5.2)))(vue@3.4.21(typescript@5.5.2))':
+ '@vue-flow/background@1.3.0(@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))':
dependencies:
- '@vue-flow/core': 1.33.5(vue@3.4.21(typescript@5.5.2))
- vue: 3.4.21(typescript@5.5.2)
+ '@vue-flow/core': 1.33.5(vue@3.4.21(typescript@5.6.2))
+ vue: 3.4.21(typescript@5.6.2)
- '@vue-flow/controls@1.1.1(@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.5.2)))(vue@3.4.21(typescript@5.5.2))':
+ '@vue-flow/controls@1.1.1(@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))':
dependencies:
- '@vue-flow/core': 1.33.5(vue@3.4.21(typescript@5.5.2))
- vue: 3.4.21(typescript@5.5.2)
+ '@vue-flow/core': 1.33.5(vue@3.4.21(typescript@5.6.2))
+ vue: 3.4.21(typescript@5.6.2)
- '@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.5.2))':
+ '@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.6.2))':
dependencies:
- '@vueuse/core': 10.11.0(vue@3.4.21(typescript@5.5.2))
+ '@vueuse/core': 10.11.0(vue@3.4.21(typescript@5.6.2))
d3-drag: 3.0.0
d3-selection: 3.0.0
d3-zoom: 3.0.0
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
transitivePeerDependencies:
- '@vue/composition-api'
- '@vue-flow/minimap@1.4.0(@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.5.2)))(vue@3.4.21(typescript@5.5.2))':
+ '@vue-flow/minimap@1.4.0(@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))':
dependencies:
- '@vue-flow/core': 1.33.5(vue@3.4.21(typescript@5.5.2))
+ '@vue-flow/core': 1.33.5(vue@3.4.21(typescript@5.6.2))
d3-selection: 3.0.0
d3-zoom: 3.0.0
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
- '@vue-flow/node-resizer@1.4.0(@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.5.2)))(vue@3.4.21(typescript@5.5.2))':
+ '@vue-flow/node-resizer@1.4.0(@vue-flow/core@1.33.5(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))':
dependencies:
- '@vue-flow/core': 1.33.5(vue@3.4.21(typescript@5.5.2))
+ '@vue-flow/core': 1.33.5(vue@3.4.21(typescript@5.6.2))
d3-drag: 3.0.0
d3-selection: 3.0.0
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
'@vue/compiler-core@3.4.21':
dependencies:
@@ -20320,19 +20320,19 @@ snapshots:
transitivePeerDependencies:
- '@types/eslint'
- '@vue/eslint-config-typescript@13.0.0(eslint-plugin-vue@9.23.0(eslint@8.57.0))(eslint@8.57.0)(typescript@5.5.2)':
+ '@vue/eslint-config-typescript@13.0.0(eslint-plugin-vue@9.23.0(eslint@8.57.0))(eslint@8.57.0)(typescript@5.6.2)':
dependencies:
- '@typescript-eslint/eslint-plugin': 7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)
- '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.5.2)
+ '@typescript-eslint/eslint-plugin': 7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2)
+ '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.6.2)
eslint: 8.57.0
eslint-plugin-vue: 9.23.0(eslint@8.57.0)
vue-eslint-parser: 9.4.2(eslint@8.57.0)
optionalDependencies:
- typescript: 5.5.2
+ typescript: 5.6.2
transitivePeerDependencies:
- supports-color
- '@vue/language-core@1.8.27(typescript@5.5.2)':
+ '@vue/language-core@1.8.27(typescript@5.6.2)':
dependencies:
'@volar/language-core': 1.11.1
'@volar/source-map': 1.11.1
@@ -20344,9 +20344,9 @@ snapshots:
path-browserify: 1.0.1
vue-template-compiler: 2.7.14
optionalDependencies:
- typescript: 5.5.2
+ typescript: 5.6.2
- '@vue/language-core@2.0.19(typescript@5.5.2)':
+ '@vue/language-core@2.0.19(typescript@5.6.2)':
dependencies:
'@volar/language-core': 2.2.5
'@vue/compiler-dom': 3.4.21
@@ -20356,7 +20356,7 @@ snapshots:
path-browserify: 1.0.1
vue-template-compiler: 2.7.14
optionalDependencies:
- typescript: 5.5.2
+ typescript: 5.6.2
'@vue/reactivity@3.4.21':
dependencies:
@@ -20373,47 +20373,47 @@ snapshots:
'@vue/shared': 3.4.21
csstype: 3.1.3
- '@vue/server-renderer@3.4.21(vue@3.4.21(typescript@5.5.2))':
+ '@vue/server-renderer@3.4.21(vue@3.4.21(typescript@5.6.2))':
dependencies:
'@vue/compiler-ssr': 3.4.21
'@vue/shared': 3.4.21
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
'@vue/shared@3.4.21': {}
- '@vue/test-utils@2.4.3(@vue/server-renderer@3.4.21(vue@3.4.21(typescript@5.5.2)))(vue@3.4.21(typescript@5.5.2))':
+ '@vue/test-utils@2.4.3(@vue/server-renderer@3.4.21(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))':
dependencies:
js-beautify: 1.14.9
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
vue-component-type-helpers: 1.8.25
optionalDependencies:
- '@vue/server-renderer': 3.4.21(vue@3.4.21(typescript@5.5.2))
+ '@vue/server-renderer': 3.4.21(vue@3.4.21(typescript@5.6.2))
- '@vueuse/components@10.11.0(vue@3.4.21(typescript@5.5.2))':
+ '@vueuse/components@10.11.0(vue@3.4.21(typescript@5.6.2))':
dependencies:
- '@vueuse/core': 10.11.0(vue@3.4.21(typescript@5.5.2))
- '@vueuse/shared': 10.11.0(vue@3.4.21(typescript@5.5.2))
- vue-demi: 0.14.8(vue@3.4.21(typescript@5.5.2))
+ '@vueuse/core': 10.11.0(vue@3.4.21(typescript@5.6.2))
+ '@vueuse/shared': 10.11.0(vue@3.4.21(typescript@5.6.2))
+ vue-demi: 0.14.8(vue@3.4.21(typescript@5.6.2))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
- '@vueuse/core@10.11.0(vue@3.4.21(typescript@5.5.2))':
+ '@vueuse/core@10.11.0(vue@3.4.21(typescript@5.6.2))':
dependencies:
'@types/web-bluetooth': 0.0.20
'@vueuse/metadata': 10.11.0
- '@vueuse/shared': 10.11.0(vue@3.4.21(typescript@5.5.2))
- vue-demi: 0.14.8(vue@3.4.21(typescript@5.5.2))
+ '@vueuse/shared': 10.11.0(vue@3.4.21(typescript@5.6.2))
+ vue-demi: 0.14.8(vue@3.4.21(typescript@5.6.2))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
- '@vueuse/core@9.13.0(vue@3.4.21(typescript@5.5.2))':
+ '@vueuse/core@9.13.0(vue@3.4.21(typescript@5.6.2))':
dependencies:
'@types/web-bluetooth': 0.0.16
'@vueuse/metadata': 9.13.0
- '@vueuse/shared': 9.13.0(vue@3.4.21(typescript@5.5.2))
- vue-demi: 0.14.8(vue@3.4.21(typescript@5.5.2))
+ '@vueuse/shared': 9.13.0(vue@3.4.21(typescript@5.6.2))
+ vue-demi: 0.14.8(vue@3.4.21(typescript@5.6.2))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
@@ -20422,25 +20422,25 @@ snapshots:
'@vueuse/metadata@9.13.0': {}
- '@vueuse/shared@10.11.0(vue@3.4.21(typescript@5.5.2))':
+ '@vueuse/shared@10.11.0(vue@3.4.21(typescript@5.6.2))':
dependencies:
- vue-demi: 0.14.8(vue@3.4.21(typescript@5.5.2))
+ vue-demi: 0.14.8(vue@3.4.21(typescript@5.6.2))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
- '@vueuse/shared@9.13.0(vue@3.4.21(typescript@5.5.2))':
+ '@vueuse/shared@9.13.0(vue@3.4.21(typescript@5.6.2))':
dependencies:
- vue-demi: 0.14.8(vue@3.4.21(typescript@5.5.2))
+ vue-demi: 0.14.8(vue@3.4.21(typescript@5.6.2))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
'@vvo/tzdb@6.141.0': {}
- '@xata.io/client@0.28.4(typescript@5.5.2)':
+ '@xata.io/client@0.28.4(typescript@5.6.2)':
dependencies:
- typescript: 5.5.2
+ typescript: 5.6.2
'@xmldom/xmldom@0.8.6': {}
@@ -22109,15 +22109,15 @@ snapshots:
electron-to-chromium@1.4.703: {}
- element-plus@2.4.3(vue@3.4.21(typescript@5.5.2)):
+ element-plus@2.4.3(vue@3.4.21(typescript@5.6.2)):
dependencies:
'@ctrl/tinycolor': 3.6.0
- '@element-plus/icons-vue': 2.3.1(vue@3.4.21(typescript@5.5.2))
+ '@element-plus/icons-vue': 2.3.1(vue@3.4.21(typescript@5.6.2))
'@floating-ui/dom': 1.4.5
'@popperjs/core': '@sxzz/popperjs-es@2.11.7'
'@types/lodash': 4.14.195
'@types/lodash-es': 4.17.6
- '@vueuse/core': 9.13.0(vue@3.4.21(typescript@5.5.2))
+ '@vueuse/core': 9.13.0(vue@3.4.21(typescript@5.6.2))
async-validator: 4.2.5
dayjs: 1.11.10
escape-html: 1.0.3
@@ -22126,7 +22126,7 @@ snapshots:
lodash-unified: 1.0.3(@types/lodash-es@4.17.6)(lodash-es@4.17.21)(lodash@4.17.21)
memoize-one: 6.0.0
normalize-wheel-es: 1.2.0
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
transitivePeerDependencies:
- '@vue/composition-api'
@@ -22399,21 +22399,21 @@ snapshots:
optionalDependencies:
source-map: 0.6.1
- eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0):
+ eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0):
dependencies:
confusing-browser-globals: 1.0.11
eslint: 8.57.0
- eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
+ eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
object.assign: 4.1.5
object.entries: 1.1.5
semver: 7.6.0
- eslint-config-airbnb-typescript@18.0.0(@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0):
+ eslint-config-airbnb-typescript@18.0.0(@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2))(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0):
dependencies:
- '@typescript-eslint/eslint-plugin': 7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)
- '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.5.2)
+ '@typescript-eslint/eslint-plugin': 7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2)
+ '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.6.2)
eslint: 8.57.0
- eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0)
+ eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0)
transitivePeerDependencies:
- eslint-plugin-import
@@ -22431,13 +22431,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0):
+ eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0):
dependencies:
debug: 4.3.4(supports-color@8.1.1)
enhanced-resolve: 5.13.0
eslint: 8.57.0
- eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
- eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
+ eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
+ eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
fast-glob: 3.3.2
get-tsconfig: 4.5.0
is-core-module: 2.13.1
@@ -22448,14 +22448,14 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
- eslint-module-utils@2.8.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0):
+ eslint-module-utils@2.8.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0):
dependencies:
debug: 3.2.7(supports-color@5.5.0)
optionalDependencies:
- '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.5.2)
+ '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.6.2)
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0)
+ eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0)
transitivePeerDependencies:
- supports-color
@@ -22464,7 +22464,7 @@ snapshots:
eslint: 8.57.0
globals: 13.20.0
- eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
+ eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
dependencies:
array-includes: 3.1.7
array.prototype.findlastindex: 1.2.3
@@ -22474,7 +22474,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
+ eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
hasown: 2.0.2
is-core-module: 2.13.1
is-glob: 4.0.3
@@ -22485,7 +22485,7 @@ snapshots:
semver: 7.6.0
tsconfig-paths: 4.2.0
optionalDependencies:
- '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.5.2)
+ '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.6.2)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
@@ -22500,9 +22500,9 @@ snapshots:
eslint-plugin-n8n-local-rules@1.0.0: {}
- eslint-plugin-n8n-nodes-base@1.16.2(eslint@8.57.0)(typescript@5.5.2):
+ eslint-plugin-n8n-nodes-base@1.16.2(eslint@8.57.0)(typescript@5.6.2):
dependencies:
- '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.5.2)
+ '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.6.2)
camel-case: 4.1.2
eslint-plugin-local: 1.0.0
indefinite: 2.5.1
@@ -22547,12 +22547,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-plugin-unused-imports@3.1.0(@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0):
+ eslint-plugin-unused-imports@3.1.0(@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0):
dependencies:
eslint: 8.57.0
eslint-rule-composer: 0.3.0
optionalDependencies:
- '@typescript-eslint/eslint-plugin': 7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)
+ '@typescript-eslint/eslint-plugin': 7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2)
eslint-plugin-vue@9.23.0(eslint@8.57.0):
dependencies:
@@ -24066,16 +24066,16 @@ snapshots:
- babel-plugin-macros
- supports-color
- jest-cli@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)):
+ jest-cli@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)):
dependencies:
- '@jest/core': 29.6.2(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2))
+ '@jest/core': 29.6.2(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2))
'@jest/test-result': 29.6.2
'@jest/types': 29.6.1
chalk: 4.1.2
exit: 0.1.2
graceful-fs: 4.2.11
import-local: 3.1.0
- jest-config: 29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2))
+ jest-config: 29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2))
jest-util: 29.6.2
jest-validate: 29.6.2
prompts: 2.4.2
@@ -24086,7 +24086,7 @@ snapshots:
- supports-color
- ts-node
- jest-config@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)):
+ jest-config@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)):
dependencies:
'@babel/core': 7.24.0
'@jest/test-sequencer': 29.6.2
@@ -24112,7 +24112,7 @@ snapshots:
strip-json-comments: 3.1.1
optionalDependencies:
'@types/node': 18.16.16
- ts-node: 10.9.2(@types/node@18.16.16)(typescript@5.5.2)
+ ts-node: 10.9.2(@types/node@18.16.16)(typescript@5.6.2)
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
@@ -24230,11 +24230,11 @@ snapshots:
slash: 3.0.0
stack-utils: 2.0.6
- jest-mock-extended@3.0.4(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)))(typescript@5.5.2):
+ jest-mock-extended@3.0.4(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)))(typescript@5.6.2):
dependencies:
- jest: 29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2))
- ts-essentials: 7.0.3(typescript@5.5.2)
- typescript: 5.5.2
+ jest: 29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2))
+ ts-essentials: 7.0.3(typescript@5.6.2)
+ typescript: 5.6.2
jest-mock@29.6.2:
dependencies:
@@ -24390,12 +24390,12 @@ snapshots:
merge-stream: 2.0.0
supports-color: 8.1.1
- jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)):
+ jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)):
dependencies:
- '@jest/core': 29.6.2(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2))
+ '@jest/core': 29.6.2(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2))
'@jest/types': 29.6.1
import-local: 3.1.0
- jest-cli: 29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2))
+ jest-cli: 29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2))
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
@@ -24682,6 +24682,56 @@ snapshots:
kuler@2.0.0: {}
+ langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube):
+ dependencies:
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/openai': 0.2.10(encoding@0.1.13)(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))
+ '@langchain/textsplitters': 0.0.3(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ binary-extensions: 2.2.0
+ js-tiktoken: 1.0.12
+ js-yaml: 4.1.0
+ jsonpointer: 5.0.1
+ langsmith: 0.1.51(@langchain/core@0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8)))(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ openapi-types: 12.1.3
+ p-retry: 4.6.2
+ uuid: 10.0.0
+ yaml: 2.3.4
+ zod: 3.23.8
+ zod-to-json-schema: 3.23.2(zod@3.23.8)
+ optionalDependencies:
+ '@aws-sdk/client-s3': 3.478.0
+ '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0)
+ '@azure/storage-blob': 12.18.0(encoding@0.1.13)
+ '@langchain/anthropic': 0.2.16(encoding@0.1.13)(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/cohere': 0.2.2(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(encoding@0.1.13)(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/community': 0.2.32(wjcprhsh6kptjwisshzaqzfaay)
+ '@langchain/google-genai': 0.0.26(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(zod@3.23.8)
+ '@langchain/google-vertexai': 0.0.27(encoding@0.1.13)(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(zod@3.23.8)
+ '@langchain/groq': 0.0.17(encoding@0.1.13)(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/mistralai': 0.0.29(encoding@0.1.13)(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@langchain/ollama': 0.0.4(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ '@pinecone-database/pinecone': 3.0.0
+ '@supabase/supabase-js': 2.45.3
+ '@xata.io/client': 0.28.4(typescript@5.6.2)
+ axios: 1.7.4(debug@4.3.6)
+ cheerio: 1.0.0-rc.12
+ d3-dsv: 2.0.0
+ epub2: 3.0.2(ts-toolbelt@9.6.0)
+ fast-xml-parser: 4.4.1
+ handlebars: 4.7.8
+ html-to-text: 9.0.5
+ ignore: 5.2.4
+ ioredis: 5.3.2
+ jsdom: 23.0.1
+ mammoth: 1.7.2
+ pdf-parse: 1.1.1
+ redis: 4.6.12
+ ws: 8.17.1
+ transitivePeerDependencies:
+ - encoding
+ - openai
+ - supports-color
+
langchain@0.2.18(axios@1.7.4)(openai@4.58.0(zod@3.23.8)):
dependencies:
'@langchain/core': 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0(zod@3.23.8)))(openai@4.58.0(zod@3.23.8))
@@ -24730,57 +24780,7 @@ snapshots:
- supports-color
optional: true
- langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu):
- dependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- '@langchain/openai': 0.2.10(encoding@0.1.13)(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))
- '@langchain/textsplitters': 0.0.3(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- binary-extensions: 2.2.0
- js-tiktoken: 1.0.12
- js-yaml: 4.1.0
- jsonpointer: 5.0.1
- langsmith: 0.1.51(@langchain/core@0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8)))(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- openapi-types: 12.1.3
- p-retry: 4.6.2
- uuid: 10.0.0
- yaml: 2.3.4
- zod: 3.23.8
- zod-to-json-schema: 3.23.2(zod@3.23.8)
- optionalDependencies:
- '@aws-sdk/client-s3': 3.478.0
- '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0)
- '@azure/storage-blob': 12.18.0(encoding@0.1.13)
- '@langchain/anthropic': 0.2.16(encoding@0.1.13)(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- '@langchain/cohere': 0.2.2(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(encoding@0.1.13)(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- '@langchain/community': 0.2.32(ygqwlohwinok7tv3gruzwbtdoi)
- '@langchain/google-genai': 0.0.26(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(zod@3.23.8)
- '@langchain/google-vertexai': 0.0.27(encoding@0.1.13)(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))(zod@3.23.8)
- '@langchain/groq': 0.0.17(encoding@0.1.13)(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- '@langchain/mistralai': 0.0.29(encoding@0.1.13)(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- '@langchain/ollama': 0.0.4(langchain@0.2.18)(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- '@pinecone-database/pinecone': 3.0.0
- '@supabase/supabase-js': 2.45.3
- '@xata.io/client': 0.28.4(typescript@5.5.2)
- axios: 1.7.4(debug@4.3.6)
- cheerio: 1.0.0-rc.12
- d3-dsv: 2.0.0
- epub2: 3.0.2(ts-toolbelt@9.6.0)
- fast-xml-parser: 4.4.1
- handlebars: 4.7.8
- html-to-text: 9.0.5
- ignore: 5.2.4
- ioredis: 5.3.2
- jsdom: 23.0.1
- mammoth: 1.7.2
- pdf-parse: 1.1.1
- redis: 4.6.12
- ws: 8.17.1
- transitivePeerDependencies:
- - encoding
- - openai
- - supports-color
-
- langsmith@0.1.39(@langchain/core@0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8)))(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8)):
+ langsmith@0.1.39(@langchain/core@0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8)))(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8)):
dependencies:
'@types/uuid': 9.0.7
commander: 10.0.1
@@ -24788,8 +24788,21 @@ snapshots:
p-retry: 4.6.2
uuid: 9.0.1
optionalDependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- langchain: 0.2.18(xd7d7p3bjr65bts4h63vmtvguu)
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ langchain: 0.2.18(7abrqxy75ksyaxunbmagq3jube)
+ openai: 4.58.0(encoding@0.1.13)(zod@3.23.8)
+
+ langsmith@0.1.51(@langchain/core@0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8)))(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8)):
+ dependencies:
+ '@types/uuid': 10.0.0
+ commander: 10.0.1
+ p-queue: 6.6.2
+ p-retry: 4.6.2
+ semver: 7.6.0
+ uuid: 10.0.0
+ optionalDependencies:
+ '@langchain/core': 0.2.31(langchain@0.2.18(7abrqxy75ksyaxunbmagq3jube))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
+ langchain: 0.2.18(7abrqxy75ksyaxunbmagq3jube)
openai: 4.58.0(encoding@0.1.13)(zod@3.23.8)
langsmith@0.1.51(@langchain/core@0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0(zod@3.23.8)))(openai@4.58.0(zod@3.23.8)))(langchain@0.2.18(axios@1.7.4)(openai@4.58.0(zod@3.23.8)))(openai@4.58.0(zod@3.23.8)):
@@ -24832,19 +24845,6 @@ snapshots:
langchain: 0.2.18(axios@1.7.4)(openai@4.58.0)
openai: 4.58.0(zod@3.23.8)
- langsmith@0.1.51(@langchain/core@0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8)))(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8)):
- dependencies:
- '@types/uuid': 10.0.0
- commander: 10.0.1
- p-queue: 6.6.2
- p-retry: 4.6.2
- semver: 7.6.0
- uuid: 10.0.0
- optionalDependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(xd7d7p3bjr65bts4h63vmtvguu))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
- langchain: 0.2.18(xd7d7p3bjr65bts4h63vmtvguu)
- openai: 4.58.0(encoding@0.1.13)(zod@3.23.8)
-
lazy-ass@1.6.0: {}
lazy-universal-dotenv@4.0.0:
@@ -26466,13 +26466,13 @@ snapshots:
pify@4.0.1: {}
- pinia@2.1.6(typescript@5.5.2)(vue@3.4.21(typescript@5.5.2)):
+ pinia@2.1.6(typescript@5.6.2)(vue@3.4.21(typescript@5.6.2)):
dependencies:
'@vue/devtools-api': 6.5.0
- vue: 3.4.21(typescript@5.5.2)
- vue-demi: 0.14.5(vue@3.4.21(typescript@5.5.2))
+ vue: 3.4.21(typescript@5.6.2)
+ vue-demi: 0.14.5(vue@3.4.21(typescript@5.6.2))
optionalDependencies:
- typescript: 5.5.2
+ typescript: 5.6.2
pirates@4.0.6: {}
@@ -26528,13 +26528,13 @@ snapshots:
camelcase-css: 2.0.1
postcss: 8.4.38
- postcss-load-config@4.0.2(postcss@8.4.38)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)):
+ postcss-load-config@4.0.2(postcss@8.4.38)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)):
dependencies:
lilconfig: 3.1.2
yaml: 2.3.4
optionalDependencies:
postcss: 8.4.38
- ts-node: 10.9.2(@types/node@18.16.16)(typescript@5.5.2)
+ ts-node: 10.9.2(@types/node@18.16.16)(typescript@5.6.2)
postcss-nested@6.0.1(postcss@8.4.38):
dependencies:
@@ -26831,9 +26831,9 @@ snapshots:
dependencies:
long: 4.0.0
- qrcode.vue@3.3.4(vue@3.4.21(typescript@5.5.2)):
+ qrcode.vue@3.3.4(vue@3.4.21(typescript@5.6.2)):
dependencies:
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
qs@6.10.4:
dependencies:
@@ -28070,7 +28070,7 @@ snapshots:
syslog-client@1.1.1: {}
- tailwindcss@3.4.3(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)):
+ tailwindcss@3.4.3(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)):
dependencies:
'@alloc/quick-lru': 5.2.0
arg: 5.0.2
@@ -28089,7 +28089,7 @@ snapshots:
postcss: 8.4.38
postcss-import: 15.1.0(postcss@8.4.38)
postcss-js: 4.0.1(postcss@8.4.38)
- postcss-load-config: 4.0.2(postcss@8.4.38)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2))
+ postcss-load-config: 4.0.2(postcss@8.4.38)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2))
postcss-nested: 6.0.1(postcss@8.4.38)
postcss-selector-parser: 6.0.16
resolve: 1.22.8
@@ -28300,19 +28300,19 @@ snapshots:
triple-beam@1.3.0: {}
- ts-api-utils@1.0.1(typescript@5.5.2):
+ ts-api-utils@1.0.1(typescript@5.6.2):
dependencies:
- typescript: 5.5.2
+ typescript: 5.6.2
ts-dedent@2.2.0: {}
- ts-essentials@7.0.3(typescript@5.5.2):
+ ts-essentials@7.0.3(typescript@5.6.2):
dependencies:
- typescript: 5.5.2
+ typescript: 5.6.2
- ts-essentials@9.4.2(typescript@5.5.2):
+ ts-essentials@9.4.2(typescript@5.6.2):
optionalDependencies:
- typescript: 5.5.2
+ typescript: 5.6.2
ts-ics@1.2.2(date-fns@2.30.0)(lodash@4.17.21)(zod@3.23.8):
dependencies:
@@ -28323,17 +28323,17 @@ snapshots:
ts-interface-checker@0.1.13: {}
- ts-jest@29.1.1(@babel/core@7.24.0)(@jest/types@29.6.1)(babel-jest@29.6.2(@babel/core@7.24.0))(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2)))(typescript@5.5.2):
+ ts-jest@29.1.1(@babel/core@7.24.0)(@jest/types@29.6.1)(babel-jest@29.6.2(@babel/core@7.24.0))(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)))(typescript@5.6.2):
dependencies:
bs-logger: 0.2.6
fast-json-stable-stringify: 2.1.0
- jest: 29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2))
+ jest: 29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2))
jest-util: 29.5.0
json5: 2.2.3
lodash.memoize: 4.1.2
make-error: 1.3.6
semver: 7.6.0
- typescript: 5.5.2
+ typescript: 5.6.2
yargs-parser: 21.1.1
optionalDependencies:
'@babel/core': 7.24.0
@@ -28342,7 +28342,7 @@ snapshots:
ts-map@1.0.3: {}
- ts-node@10.9.2(@types/node@18.16.16)(typescript@5.5.2):
+ ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.11
@@ -28356,7 +28356,7 @@ snapshots:
create-require: 1.1.1
diff: 4.0.2
make-error: 1.3.6
- typescript: 5.5.2
+ typescript: 5.6.2
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
optional: true
@@ -28379,13 +28379,13 @@ snapshots:
normalize-path: 3.0.0
plimit-lit: 1.4.1
- tsc-watch@6.0.4(typescript@5.5.2):
+ tsc-watch@6.0.4(typescript@5.6.2):
dependencies:
cross-spawn: 7.0.3
node-cleanup: 2.1.2
ps-tree: 1.2.0
string-argv: 0.3.1
- typescript: 5.5.2
+ typescript: 5.6.2
tsconfig-paths@4.2.0:
dependencies:
@@ -28532,7 +28532,7 @@ snapshots:
typedi@0.10.0(patch_hash=sk6omkefrosihg7lmqbzh7vfxe): {}
- typescript@5.5.2: {}
+ typescript@5.6.2: {}
uc.micro@1.0.6: {}
@@ -28632,7 +28632,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- unplugin-vue-components@0.27.3(@babel/parser@7.24.6)(rollup@4.18.0)(vue@3.4.21(typescript@5.5.2)):
+ unplugin-vue-components@0.27.3(@babel/parser@7.24.6)(rollup@4.18.0)(vue@3.4.21(typescript@5.6.2)):
dependencies:
'@antfu/utils': 0.7.10
'@rollup/pluginutils': 5.1.0(rollup@4.18.0)
@@ -28644,7 +28644,7 @@ snapshots:
minimatch: 9.0.5
mlly: 1.7.1
unplugin: 1.11.0
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
optionalDependencies:
'@babel/parser': 7.24.6
transitivePeerDependencies:
@@ -28793,16 +28793,16 @@ snapshots:
- supports-color
- terser
- vite-plugin-dts@3.9.1(@types/node@18.16.16)(rollup@4.18.0)(typescript@5.5.2)(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1)):
+ vite-plugin-dts@3.9.1(@types/node@18.16.16)(rollup@4.18.0)(typescript@5.6.2)(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1)):
dependencies:
'@microsoft/api-extractor': 7.43.0(@types/node@18.16.16)
'@rollup/pluginutils': 5.1.0(rollup@4.18.0)
- '@vue/language-core': 1.8.27(typescript@5.5.2)
+ '@vue/language-core': 1.8.27(typescript@5.6.2)
debug: 4.3.5
kolorist: 1.8.0
magic-string: 0.30.8
- typescript: 5.5.2
- vue-tsc: 1.8.27(typescript@5.5.2)
+ typescript: 5.6.2
+ vue-tsc: 1.8.27(typescript@5.6.2)
optionalDependencies:
vite: 5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1)
transitivePeerDependencies:
@@ -28821,10 +28821,10 @@ snapshots:
sass: 1.64.1
terser: 5.16.1
- vitest-mock-extended@1.3.1(typescript@5.5.2)(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)):
+ vitest-mock-extended@1.3.1(typescript@5.6.2)(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)):
dependencies:
- ts-essentials: 9.4.2(typescript@5.5.2)
- typescript: 5.5.2
+ ts-essentials: 9.4.2(typescript@5.6.2)
+ typescript: 5.6.2
vitest: 1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)
vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1):
@@ -28868,23 +28868,23 @@ snapshots:
lodash.orderby: 4.6.0
lodash.throttle: 4.1.1
- vue-boring-avatars@1.3.0(vue@3.4.21(typescript@5.5.2)):
+ vue-boring-avatars@1.3.0(vue@3.4.21(typescript@5.6.2)):
dependencies:
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
- vue-chartjs@5.2.0(chart.js@4.4.0)(vue@3.4.21(typescript@5.5.2)):
+ vue-chartjs@5.2.0(chart.js@4.4.0)(vue@3.4.21(typescript@5.6.2)):
dependencies:
chart.js: 4.4.0
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
- vue-component-meta@2.0.19(typescript@5.5.2):
+ vue-component-meta@2.0.19(typescript@5.6.2):
dependencies:
'@volar/typescript': 2.2.5
- '@vue/language-core': 2.0.19(typescript@5.5.2)
+ '@vue/language-core': 2.0.19(typescript@5.6.2)
path-browserify: 1.0.1
vue-component-type-helpers: 2.0.19
optionalDependencies:
- typescript: 5.5.2
+ typescript: 5.6.2
vue-component-type-helpers@1.8.25: {}
@@ -28892,15 +28892,15 @@ snapshots:
vue-component-type-helpers@2.1.6: {}
- vue-demi@0.14.5(vue@3.4.21(typescript@5.5.2)):
+ vue-demi@0.14.5(vue@3.4.21(typescript@5.6.2)):
dependencies:
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
- vue-demi@0.14.8(vue@3.4.21(typescript@5.5.2)):
+ vue-demi@0.14.8(vue@3.4.21(typescript@5.6.2)):
dependencies:
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
- vue-docgen-api@4.76.0(vue@3.4.21(typescript@5.5.2)):
+ vue-docgen-api@4.76.0(vue@3.4.21(typescript@5.6.2)):
dependencies:
'@babel/parser': 7.24.0
'@babel/types': 7.24.0
@@ -28913,8 +28913,8 @@ snapshots:
pug: 3.0.3
recast: 0.23.6
ts-map: 1.0.3
- vue: 3.4.21(typescript@5.5.2)
- vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.4.21(typescript@5.5.2))
+ vue: 3.4.21(typescript@5.6.2)
+ vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.4.21(typescript@5.6.2))
vue-eslint-parser@9.4.2(eslint@8.57.0):
dependencies:
@@ -28929,62 +28929,62 @@ snapshots:
transitivePeerDependencies:
- supports-color
- vue-i18n@9.2.2(vue@3.4.21(typescript@5.5.2)):
+ vue-i18n@9.2.2(vue@3.4.21(typescript@5.6.2)):
dependencies:
'@intlify/core-base': 9.2.2
'@intlify/shared': 9.2.2
'@intlify/vue-devtools': 9.2.2
'@vue/devtools-api': 6.4.5
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
- vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.4.21(typescript@5.5.2)):
+ vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.4.21(typescript@5.6.2)):
dependencies:
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
- vue-json-pretty@2.2.4(vue@3.4.21(typescript@5.5.2)):
+ vue-json-pretty@2.2.4(vue@3.4.21(typescript@5.6.2)):
dependencies:
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
- vue-markdown-render@2.2.1(vue@3.4.21(typescript@5.5.2)):
+ vue-markdown-render@2.2.1(vue@3.4.21(typescript@5.6.2)):
dependencies:
markdown-it: 13.0.2
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
- vue-router@4.2.2(vue@3.4.21(typescript@5.5.2)):
+ vue-router@4.2.2(vue@3.4.21(typescript@5.6.2)):
dependencies:
'@vue/devtools-api': 6.5.0
- vue: 3.4.21(typescript@5.5.2)
+ vue: 3.4.21(typescript@5.6.2)
vue-template-compiler@2.7.14:
dependencies:
de-indent: 1.0.2
he: 1.2.0
- vue-tsc@1.8.27(typescript@5.5.2):
+ vue-tsc@1.8.27(typescript@5.6.2):
dependencies:
'@volar/typescript': 1.11.1
- '@vue/language-core': 1.8.27(typescript@5.5.2)
+ '@vue/language-core': 1.8.27(typescript@5.6.2)
semver: 7.6.0
- typescript: 5.5.2
+ typescript: 5.6.2
- vue-tsc@2.0.19(typescript@5.5.2):
+ vue-tsc@2.0.19(typescript@5.6.2):
dependencies:
'@volar/typescript': 2.2.5
- '@vue/language-core': 2.0.19(typescript@5.5.2)
+ '@vue/language-core': 2.0.19(typescript@5.6.2)
semver: 7.6.0
- typescript: 5.5.2
+ typescript: 5.6.2
vue3-touch-events@4.1.3: {}
- vue@3.4.21(typescript@5.5.2):
+ vue@3.4.21(typescript@5.6.2):
dependencies:
'@vue/compiler-dom': 3.4.21
'@vue/compiler-sfc': 3.4.21
'@vue/runtime-dom': 3.4.21
- '@vue/server-renderer': 3.4.21(vue@3.4.21(typescript@5.5.2))
+ '@vue/server-renderer': 3.4.21(vue@3.4.21(typescript@5.6.2))
'@vue/shared': 3.4.21
optionalDependencies:
- typescript: 5.5.2
+ typescript: 5.6.2
w3c-keyname@2.2.6: {}
From 0e61d7c2e8b26a5e856b02591f79b93e94bf9028 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?=
Date: Mon, 16 Sep 2024 10:58:23 +0200
Subject: [PATCH 03/42] refactor(core): Remove unused disable directives from
backend packages (#10826)
---
.../__tests__/source-control-export.service.test.ts | 2 +-
.../eventbus/event-message-classes/abstract-event-message.ts | 2 --
packages/cli/src/events/telemetry-event-relay.ts | 2 --
3 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/packages/cli/src/environments/source-control/__tests__/source-control-export.service.test.ts b/packages/cli/src/environments/source-control/__tests__/source-control-export.service.test.ts
index 82f1000671..b04d8a2239 100644
--- a/packages/cli/src/environments/source-control/__tests__/source-control-export.service.test.ts
+++ b/packages/cli/src/environments/source-control/__tests__/source-control-export.service.test.ts
@@ -14,7 +14,7 @@ import type { SourceControlledFile } from '../types/source-controlled-file';
// https://github.com/jestjs/jest/issues/4715
function deepSpyOn(object: O, methodName: M) {
const spy = jest.fn();
- // eslint-disable-next-line @typescript-eslint/ban-types
+
const originalMethod = object[methodName];
if (typeof originalMethod !== 'function') {
diff --git a/packages/cli/src/eventbus/event-message-classes/abstract-event-message.ts b/packages/cli/src/eventbus/event-message-classes/abstract-event-message.ts
index ac260b2c52..176d064269 100644
--- a/packages/cli/src/eventbus/event-message-classes/abstract-event-message.ts
+++ b/packages/cli/src/eventbus/event-message-classes/abstract-event-message.ts
@@ -21,12 +21,10 @@ function modifyUnderscoredKeys(
result[key] = modifier(input[key]);
}
} else {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
result[key] = input[key];
}
} else if (typeof input[key] === 'object') {
if (Array.isArray(input[key])) {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
result[key] = input[key].map((item: any) => {
if (typeof item === 'object' && !Array.isArray(item)) {
return modifyUnderscoredKeys(item, modifier);
diff --git a/packages/cli/src/events/telemetry-event-relay.ts b/packages/cli/src/events/telemetry-event-relay.ts
index 1544ff85ec..82beb17198 100644
--- a/packages/cli/src/events/telemetry-event-relay.ts
+++ b/packages/cli/src/events/telemetry-event-relay.ts
@@ -950,8 +950,6 @@ export class TelemetryEventRelay extends EventRelay {
}: RelayEventMap['user-submitted-personalization-survey']) {
const personalizationSurveyData = { user_id: userId } as Record;
- // ESlint is wrong here
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
for (const [camelCaseKey, value] of Object.entries(answers)) {
if (value) {
personalizationSurveyData[snakeCase(camelCaseKey)] = value;
From a7ed8ee9094fa9882c4d05e41c3fb89f47702302 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?=
Date: Mon, 16 Sep 2024 10:58:32 +0200
Subject: [PATCH 04/42] refactor: Remove unused disable directives from
`nodes-base` (#10825)
---
packages/nodes-base/nodes/AiTransform/AiTransform.node.ts | 1 -
.../nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts | 4 ++--
packages/nodes-base/nodes/Cron/Cron.node.ts | 2 +-
.../nodes-base/nodes/EmailReadImap/v1/EmailReadImapV1.node.ts | 2 +-
.../nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts | 2 +-
packages/nodes-base/nodes/Form/v1/FormTriggerV1.node.ts | 2 +-
packages/nodes-base/nodes/Form/v2/FormTriggerV2.node.ts | 2 +-
.../nodes/Google/Analytics/v1/GoogleAnalyticsV1.node.ts | 1 -
packages/nodes-base/nodes/Interval/Interval.node.ts | 2 +-
.../nodes/SplitInBatches/v2/SplitInBatchesV2.node.ts | 2 +-
.../nodes/SplitInBatches/v3/SplitInBatchesV3.node.ts | 2 +-
packages/nodes-base/nodes/Start/Start.node.ts | 2 +-
packages/nodes-base/nodes/StickyNote/StickyNote.node.ts | 4 ++--
packages/nodes-base/nodes/StopAndError/StopAndError.node.ts | 2 +-
packages/nodes-base/nodes/Webflow/V1/WebflowTriggerV1.node.ts | 2 +-
packages/nodes-base/nodes/Webflow/V2/WebflowTriggerV2.node.ts | 2 +-
packages/nodes-base/nodes/Webhook/Webhook.node.ts | 2 +-
packages/nodes-base/utils/descriptions.ts | 1 -
18 files changed, 17 insertions(+), 20 deletions(-)
diff --git a/packages/nodes-base/nodes/AiTransform/AiTransform.node.ts b/packages/nodes-base/nodes/AiTransform/AiTransform.node.ts
index 573c34ad62..ac68f13f0e 100644
--- a/packages/nodes-base/nodes/AiTransform/AiTransform.node.ts
+++ b/packages/nodes-base/nodes/AiTransform/AiTransform.node.ts
@@ -1,4 +1,3 @@
-/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
import {
NodeOperationError,
NodeConnectionType,
diff --git a/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts b/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts
index 99f866394f..87b0c88c72 100644
--- a/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts
+++ b/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts
@@ -22,11 +22,11 @@ export class CompareDatasets implements INodeType {
version: [1, 2, 2.1, 2.2, 2.3],
description: 'Compare two inputs for changes',
defaults: { name: 'Compare Datasets' },
- // eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
+
inputs: [NodeConnectionType.Main, NodeConnectionType.Main],
inputNames: ['Input A', 'Input B'],
requiredInputs: 1,
- // eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
+
outputs: [
NodeConnectionType.Main,
NodeConnectionType.Main,
diff --git a/packages/nodes-base/nodes/Cron/Cron.node.ts b/packages/nodes-base/nodes/Cron/Cron.node.ts
index ebb471a99d..df026c2a5f 100644
--- a/packages/nodes-base/nodes/Cron/Cron.node.ts
+++ b/packages/nodes-base/nodes/Cron/Cron.node.ts
@@ -23,7 +23,7 @@ export class Cron implements INodeType {
name: 'Cron',
color: '#29a568',
},
- // eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
+
inputs: [],
outputs: [NodeConnectionType.Main],
properties: [
diff --git a/packages/nodes-base/nodes/EmailReadImap/v1/EmailReadImapV1.node.ts b/packages/nodes-base/nodes/EmailReadImap/v1/EmailReadImapV1.node.ts
index 419191e17e..29cac4a002 100644
--- a/packages/nodes-base/nodes/EmailReadImap/v1/EmailReadImapV1.node.ts
+++ b/packages/nodes-base/nodes/EmailReadImap/v1/EmailReadImapV1.node.ts
@@ -82,7 +82,7 @@ const versionDescription: INodeTypeDescription = {
activationHint:
"Once you’ve finished building your workflow, activate it to have it also listen continuously (you just won’t see those executions here).",
},
- // eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
+
inputs: [],
outputs: [NodeConnectionType.Main],
credentials: [
diff --git a/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts b/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts
index 796a269e88..bfb6d50ed3 100644
--- a/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts
+++ b/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts
@@ -86,7 +86,7 @@ const versionDescription: INodeTypeDescription = {
activationHint:
"Once you’ve finished building your workflow, activate it to have it also listen continuously (you just won’t see those executions here).",
},
- // eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
+
inputs: [],
outputs: [NodeConnectionType.Main],
credentials: [
diff --git a/packages/nodes-base/nodes/Form/v1/FormTriggerV1.node.ts b/packages/nodes-base/nodes/Form/v1/FormTriggerV1.node.ts
index b0363cce73..f68e0ac86d 100644
--- a/packages/nodes-base/nodes/Form/v1/FormTriggerV1.node.ts
+++ b/packages/nodes-base/nodes/Form/v1/FormTriggerV1.node.ts
@@ -27,7 +27,7 @@ const descriptionV1: INodeTypeDescription = {
defaults: {
name: 'n8n Form Trigger',
},
- // eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
+
inputs: [],
outputs: [NodeConnectionType.Main],
webhooks: [
diff --git a/packages/nodes-base/nodes/Form/v2/FormTriggerV2.node.ts b/packages/nodes-base/nodes/Form/v2/FormTriggerV2.node.ts
index 8792d0d6a1..e6ba8c619d 100644
--- a/packages/nodes-base/nodes/Form/v2/FormTriggerV2.node.ts
+++ b/packages/nodes-base/nodes/Form/v2/FormTriggerV2.node.ts
@@ -38,7 +38,7 @@ const descriptionV2: INodeTypeDescription = {
defaults: {
name: 'n8n Form Trigger',
},
- // eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
+
inputs: [],
outputs: [NodeConnectionType.Main],
webhooks: [
diff --git a/packages/nodes-base/nodes/Google/Analytics/v1/GoogleAnalyticsV1.node.ts b/packages/nodes-base/nodes/Google/Analytics/v1/GoogleAnalyticsV1.node.ts
index cb643737ee..72a07b2f64 100644
--- a/packages/nodes-base/nodes/Google/Analytics/v1/GoogleAnalyticsV1.node.ts
+++ b/packages/nodes-base/nodes/Google/Analytics/v1/GoogleAnalyticsV1.node.ts
@@ -1,4 +1,3 @@
-/* eslint-disable n8n-nodes-base/node-filename-against-convention */
import {
type IExecuteFunctions,
type IDataObject,
diff --git a/packages/nodes-base/nodes/Interval/Interval.node.ts b/packages/nodes-base/nodes/Interval/Interval.node.ts
index be97325277..10d60b93c5 100644
--- a/packages/nodes-base/nodes/Interval/Interval.node.ts
+++ b/packages/nodes-base/nodes/Interval/Interval.node.ts
@@ -22,7 +22,7 @@ export class Interval implements INodeType {
name: 'Interval',
color: '#00FF00',
},
- // eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
+
inputs: [],
outputs: [NodeConnectionType.Main],
properties: [
diff --git a/packages/nodes-base/nodes/SplitInBatches/v2/SplitInBatchesV2.node.ts b/packages/nodes-base/nodes/SplitInBatches/v2/SplitInBatchesV2.node.ts
index f80095e5f3..d6c8b7fbb3 100644
--- a/packages/nodes-base/nodes/SplitInBatches/v2/SplitInBatchesV2.node.ts
+++ b/packages/nodes-base/nodes/SplitInBatches/v2/SplitInBatchesV2.node.ts
@@ -20,7 +20,7 @@ export class SplitInBatchesV2 implements INodeType {
color: '#007755',
},
inputs: [NodeConnectionType.Main],
- // eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
+
outputs: [NodeConnectionType.Main, NodeConnectionType.Main],
outputNames: ['loop', 'done'],
properties: [
diff --git a/packages/nodes-base/nodes/SplitInBatches/v3/SplitInBatchesV3.node.ts b/packages/nodes-base/nodes/SplitInBatches/v3/SplitInBatchesV3.node.ts
index fde5bc9d33..5c6edcc59d 100644
--- a/packages/nodes-base/nodes/SplitInBatches/v3/SplitInBatchesV3.node.ts
+++ b/packages/nodes-base/nodes/SplitInBatches/v3/SplitInBatchesV3.node.ts
@@ -21,7 +21,7 @@ export class SplitInBatchesV3 implements INodeType {
color: '#007755',
},
inputs: [NodeConnectionType.Main],
- // eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
+
outputs: [NodeConnectionType.Main, NodeConnectionType.Main],
outputNames: ['done', 'loop'],
properties: [
diff --git a/packages/nodes-base/nodes/Start/Start.node.ts b/packages/nodes-base/nodes/Start/Start.node.ts
index b59c225b0a..4405e9def7 100644
--- a/packages/nodes-base/nodes/Start/Start.node.ts
+++ b/packages/nodes-base/nodes/Start/Start.node.ts
@@ -20,7 +20,7 @@ export class Start implements INodeType {
name: 'Start',
color: '#00e000',
},
- // eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
+
inputs: [],
outputs: [NodeConnectionType.Main],
properties: [
diff --git a/packages/nodes-base/nodes/StickyNote/StickyNote.node.ts b/packages/nodes-base/nodes/StickyNote/StickyNote.node.ts
index 06106e0dad..3be50c00cb 100644
--- a/packages/nodes-base/nodes/StickyNote/StickyNote.node.ts
+++ b/packages/nodes-base/nodes/StickyNote/StickyNote.node.ts
@@ -17,9 +17,9 @@ export class StickyNote implements INodeType {
name: 'Sticky Note',
color: '#FFD233',
},
- // eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
+
inputs: [],
- // eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
+
outputs: [],
properties: [
{
diff --git a/packages/nodes-base/nodes/StopAndError/StopAndError.node.ts b/packages/nodes-base/nodes/StopAndError/StopAndError.node.ts
index 4c44504920..7007f81530 100644
--- a/packages/nodes-base/nodes/StopAndError/StopAndError.node.ts
+++ b/packages/nodes-base/nodes/StopAndError/StopAndError.node.ts
@@ -26,7 +26,7 @@ export class StopAndError implements INodeType {
color: '#ff0000',
},
inputs: [NodeConnectionType.Main],
- // eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
+
outputs: [],
properties: [
{
diff --git a/packages/nodes-base/nodes/Webflow/V1/WebflowTriggerV1.node.ts b/packages/nodes-base/nodes/Webflow/V1/WebflowTriggerV1.node.ts
index 87fafeffd7..ab7f7df9f2 100644
--- a/packages/nodes-base/nodes/Webflow/V1/WebflowTriggerV1.node.ts
+++ b/packages/nodes-base/nodes/Webflow/V1/WebflowTriggerV1.node.ts
@@ -26,7 +26,7 @@ export class WebflowTriggerV1 implements INodeType {
defaults: {
name: 'Webflow Trigger',
},
- // eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
+
inputs: [],
outputs: [NodeConnectionType.Main],
credentials: [
diff --git a/packages/nodes-base/nodes/Webflow/V2/WebflowTriggerV2.node.ts b/packages/nodes-base/nodes/Webflow/V2/WebflowTriggerV2.node.ts
index 0038f0bd87..618f359fc5 100644
--- a/packages/nodes-base/nodes/Webflow/V2/WebflowTriggerV2.node.ts
+++ b/packages/nodes-base/nodes/Webflow/V2/WebflowTriggerV2.node.ts
@@ -26,7 +26,7 @@ export class WebflowTriggerV2 implements INodeType {
defaults: {
name: 'Webflow Trigger',
},
- // eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
+
inputs: [],
outputs: [NodeConnectionType.Main],
credentials: [
diff --git a/packages/nodes-base/nodes/Webhook/Webhook.node.ts b/packages/nodes-base/nodes/Webhook/Webhook.node.ts
index a237ce9ada..9938f4eaa1 100644
--- a/packages/nodes-base/nodes/Webhook/Webhook.node.ts
+++ b/packages/nodes-base/nodes/Webhook/Webhook.node.ts
@@ -65,7 +65,7 @@ export class Webhook extends Node {
activationHint:
"Once you've finished building your workflow, run it without having to click this button by using the production webhook URL.",
},
- // eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
+
inputs: [],
outputs: `={{(${configuredOutputs})($parameter)}}`,
credentials: credentialsProperty(this.authPropertyName),
diff --git a/packages/nodes-base/utils/descriptions.ts b/packages/nodes-base/utils/descriptions.ts
index d2fa59ac11..65deaaf6ac 100644
--- a/packages/nodes-base/utils/descriptions.ts
+++ b/packages/nodes-base/utils/descriptions.ts
@@ -43,7 +43,6 @@ export const looseTypeValidationProperty: INodeProperties = {
};
export const appendAttributionOption: INodeProperties = {
- // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
displayName: 'Append n8n Attribution',
name: 'appendAttribution',
type: 'boolean',
From 3be31e27edc6e71400bde23f992ba98b2365bcff Mon Sep 17 00:00:00 2001
From: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com>
Date: Mon, 16 Sep 2024 12:11:50 +0200
Subject: [PATCH 05/42] fix: Fix telemetry causing console error (#10828)
---
.../stores/__tests__/assistant.store.test.ts | 75 +++++++++++++++++++
.../editor-ui/src/stores/assistant.store.ts | 2 +-
2 files changed, 76 insertions(+), 1 deletion(-)
diff --git a/packages/editor-ui/src/stores/__tests__/assistant.store.test.ts b/packages/editor-ui/src/stores/__tests__/assistant.store.test.ts
index fad5bae0c7..4ca4337ab3 100644
--- a/packages/editor-ui/src/stores/__tests__/assistant.store.test.ts
+++ b/packages/editor-ui/src/stores/__tests__/assistant.store.test.ts
@@ -16,12 +16,23 @@ import { DEFAULT_POSTHOG_SETTINGS } from './posthog.test';
import { AI_ASSISTANT_EXPERIMENT } from '@/constants';
import { reactive } from 'vue';
import * as chatAPI from '@/api/assistant';
+import * as telemetryModule from '@/composables/useTelemetry';
+import type { Telemetry } from '@/plugins/telemetry';
let settingsStore: ReturnType;
let posthogStore: ReturnType;
const apiSpy = vi.spyOn(chatAPI, 'chatWithAssistant');
+const track = vi.fn();
+const spy = vi.spyOn(telemetryModule, 'useTelemetry');
+spy.mockImplementation(
+ () =>
+ ({
+ track,
+ }) as unknown as Telemetry,
+);
+
const setAssistantEnabled = (enabled: boolean) => {
settingsStore.setSettings(
merge({}, defaultSettings, {
@@ -63,6 +74,7 @@ describe('AI Assistant store', () => {
};
posthogStore = usePostHog();
posthogStore.init();
+ track.mockReset();
});
it('initializes with default values', () => {
@@ -316,4 +328,67 @@ describe('AI Assistant store', () => {
await assistantStore.initErrorHelper(context);
expect(apiSpy).toHaveBeenCalled();
});
+
+ it('should call telemetry for opening assistant with error', async () => {
+ const context: ChatRequest.ErrorContext = {
+ error: {
+ description: '',
+ message: 'Hey',
+ name: 'NodeOperationError',
+ },
+ node: {
+ id: '1',
+ type: 'n8n-nodes-base.stopAndError',
+ typeVersion: 1,
+ name: 'Stop and Error',
+ position: [250, 250],
+ parameters: {},
+ },
+ };
+ const mockSessionId = 'test';
+
+ const assistantStore = useAssistantStore();
+ apiSpy.mockImplementation((_ctx, _payload, onMessage) => {
+ onMessage({
+ messages: [],
+ sessionId: mockSessionId,
+ });
+ });
+
+ await assistantStore.initErrorHelper(context);
+ expect(apiSpy).toHaveBeenCalled();
+ expect(assistantStore.currentSessionId).toEqual(mockSessionId);
+
+ assistantStore.trackUserOpenedAssistant({
+ task: 'error',
+ source: 'error',
+ has_existing_session: true,
+ });
+ expect(track).toHaveBeenCalledWith(
+ 'Assistant session started',
+ {
+ chat_session_id: 'test',
+ node_type: 'n8n-nodes-base.stopAndError',
+ task: 'error',
+ credential_type: undefined,
+ },
+ {
+ withPostHog: true,
+ },
+ );
+
+ expect(track).toHaveBeenCalledWith('User opened assistant', {
+ chat_session_id: 'test',
+ error: {
+ description: '',
+ message: 'Hey',
+ name: 'NodeOperationError',
+ },
+ has_existing_session: true,
+ node_type: 'n8n-nodes-base.stopAndError',
+ source: 'error',
+ task: 'error',
+ workflow_id: '__EMPTY__',
+ });
+ });
});
diff --git a/packages/editor-ui/src/stores/assistant.store.ts b/packages/editor-ui/src/stores/assistant.store.ts
index 386df83adf..0bd883ad51 100644
--- a/packages/editor-ui/src/stores/assistant.store.ts
+++ b/packages/editor-ui/src/stores/assistant.store.ts
@@ -580,7 +580,7 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
workflow_id: workflowsStore.workflowId,
node_type: chatSessionError.value?.node?.type,
error: chatSessionError.value?.error,
- chat_session_id: currentSessionId,
+ chat_session_id: currentSessionId.value,
});
}
From 22c1890139c89e74df67b9673a1d0c85d647eb9d Mon Sep 17 00:00:00 2001
From: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com>
Date: Mon, 16 Sep 2024 12:27:44 +0200
Subject: [PATCH 06/42] fix: Prevent copying workflow when copying outside of
canvas (#10813)
---
cypress/e2e/45-ai-assistant.cy.ts | 55 ++++
.../aiAssistant/code_snippet_response.json | 28 ++
cypress/pages/features/ai-assistant.ts | 1 +
.../AskAssistantChat/AskAssistantChat.vue | 1 +
.../__tests__/AskAssistantChat.spec.ts | 20 ++
.../AskAssistantChat.spec.ts.snap | 242 ++++++++++++++++++
.../AskAssistant/AskAssistantChat.vue | 2 +
7 files changed, 349 insertions(+)
create mode 100644 cypress/fixtures/aiAssistant/code_snippet_response.json
diff --git a/cypress/e2e/45-ai-assistant.cy.ts b/cypress/e2e/45-ai-assistant.cy.ts
index 01d07cbd2b..7bf97eeaf9 100644
--- a/cypress/e2e/45-ai-assistant.cy.ts
+++ b/cypress/e2e/45-ai-assistant.cy.ts
@@ -374,3 +374,58 @@ describe('AI Assistant Credential Help', () => {
aiAssistant.getters.credentialEditAssistantButton().should('be.disabled');
});
});
+
+describe('General help', () => {
+ beforeEach(() => {
+ aiAssistant.actions.enableAssistant();
+ wf.actions.visit();
+ });
+
+ it('assistant returns code snippet', () => {
+ cy.intercept('POST', '/rest/ai-assistant/chat', {
+ statusCode: 200,
+ fixture: 'aiAssistant/code_snippet_response.json',
+ }).as('chatRequest');
+
+ aiAssistant.getters.askAssistantFloatingButton().should('be.visible');
+ aiAssistant.getters.askAssistantFloatingButton().click();
+ aiAssistant.getters.askAssistantChat().should('be.visible');
+ aiAssistant.getters.placeholderMessage().should('be.visible');
+ aiAssistant.getters.chatInput().should('be.visible');
+
+ aiAssistant.getters.chatInput().type('Show me an expression');
+ aiAssistant.getters.sendMessageButton().click();
+
+ aiAssistant.getters.chatMessagesAll().should('have.length', 3);
+ aiAssistant.getters.chatMessagesUser().eq(0).should('contain.text', 'Show me an expression');
+
+ aiAssistant.getters
+ .chatMessagesAssistant()
+ .eq(0)
+ .should('contain.text', 'To use expressions in n8n, follow these steps:');
+
+ aiAssistant.getters
+ .chatMessagesAssistant()
+ .eq(0)
+ .should(
+ 'include.html',
+ `[
+ {
+ "headers": {
+ "host": "n8n.instance.address",
+ ...
+ },
+ "params": {},
+ "query": {},
+ "body": {
+ "name": "Jim",
+ "age": 30,
+ "city": "New York"
+ }
+ }
+]
+
`,
+ );
+ aiAssistant.getters.codeSnippet().should('have.text', '{{$json.body.city}}');
+ });
+});
diff --git a/cypress/fixtures/aiAssistant/code_snippet_response.json b/cypress/fixtures/aiAssistant/code_snippet_response.json
new file mode 100644
index 0000000000..b05f212de1
--- /dev/null
+++ b/cypress/fixtures/aiAssistant/code_snippet_response.json
@@ -0,0 +1,28 @@
+{
+ "sessionId": "f1d19ed5-0d55-4bad-b49a-f0c56bd6f76f-705b5dbf-12d4-4805-87a3-1e5b3c716d29-W1JgVNrpfitpSNF9rAjB4",
+ "messages": [
+ {
+ "role": "assistant",
+ "type": "message",
+ "text": "To use expressions in n8n, follow these steps:\n\n1. Hover over the parameter where you want to use an expression.\n2. Select **Expressions** in the **Fixed/Expression** toggle.\n3. Write your expression in the parameter, or select **Open expression editor** to open the expressions editor. You can browse the available data in the **Variable selector**. All expressions have the format `{{ your expression here }}`.\n\n### Example: Get data from webhook body\n\nIf your webhook data looks like this:\n\n```json\n[\n {\n \"headers\": {\n \"host\": \"n8n.instance.address\",\n ...\n },\n \"params\": {},\n \"query\": {},\n \"body\": {\n \"name\": \"Jim\",\n \"age\": 30,\n \"city\": \"New York\"\n }\n }\n]\n```\n\nYou can use the following expression to get the value of `city`:\n\n```js\n{{$json.body.city}}\n```\n\nThis expression accesses the incoming JSON-formatted data using n8n's custom `$json` variable and finds the value of `city` (in this example, \"New York\").",
+ "codeSnippet": "{{$json.body.city}}"
+ },
+ {
+ "role": "assistant",
+ "type": "message",
+ "text": "Did this answer solve your question?",
+ "quickReplies": [
+ {
+ "text": "Yes, thanks",
+ "type": "all-good",
+ "isFeedback": true
+ },
+ {
+ "text": "No, I am still stuck",
+ "type": "still-stuck",
+ "isFeedback": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/cypress/pages/features/ai-assistant.ts b/cypress/pages/features/ai-assistant.ts
index 8434074737..ea77724dcf 100644
--- a/cypress/pages/features/ai-assistant.ts
+++ b/cypress/pages/features/ai-assistant.ts
@@ -37,6 +37,7 @@ export class AIAssistant extends BasePage {
cy.getByTestId('node-error-view-ask-assistant-button').find('button').first(),
credentialEditAssistantButton: () =>
cy.getByTestId('credentail-edit-ask-assistant-button').find('button').first(),
+ codeSnippet: () => cy.getByTestId('assistant-code-snippet'),
};
actions = {
diff --git a/packages/design-system/src/components/AskAssistantChat/AskAssistantChat.vue b/packages/design-system/src/components/AskAssistantChat/AskAssistantChat.vue
index 1fb5d42c63..de2f5e1768 100644
--- a/packages/design-system/src/components/AskAssistantChat/AskAssistantChat.vue
+++ b/packages/design-system/src/components/AskAssistantChat/AskAssistantChat.vue
@@ -171,6 +171,7 @@ function growInput() {
{
});
expect(container).toMatchSnapshot();
});
+ it('renders message with code snippet', () => {
+ const { container } = render(AskAssistantChat, {
+ props: {
+ user: { firstName: 'Kobi', lastName: 'Dog' },
+ messages: [
+ {
+ id: '1',
+ type: 'text',
+ role: 'assistant',
+ content:
+ 'Hi Max! Here is my top solution to fix the error in your **Transform data** node👇',
+ codeSnippet:
+ "node.on('input', function(msg) {\n if (msg.seed) { dummyjson.seed = msg.seed; }\n try {\n var value = dummyjson.parse(node.template, {mockdata: msg});\n if (node.syntax === 'json') {\n try { value = JSON.parse(value); }\n catch(e) { node.error(RED._('datagen.errors.json-error')); }\n }\n if (node.fieldType === 'msg') {\n RED.util.setMessageProperty(msg,node.field,value);\n }\n else if (node.fieldType === 'flow') {\n node.context().flow.set(node.field,value);\n }\n else if (node.fieldType === 'global') {\n node.context().global.set(node.field,value);\n }\n node.send(msg);\n }\n catch(e) {",
+ read: false,
+ },
+ ],
+ },
+ });
+ expect(container).toMatchSnapshot();
+ });
});
diff --git a/packages/design-system/src/components/AskAssistantChat/__tests__/__snapshots__/AskAssistantChat.spec.ts.snap b/packages/design-system/src/components/AskAssistantChat/__tests__/__snapshots__/AskAssistantChat.spec.ts.snap
index c7aa8da5cb..8baba648f9 100644
--- a/packages/design-system/src/components/AskAssistantChat/__tests__/__snapshots__/AskAssistantChat.spec.ts.snap
+++ b/packages/design-system/src/components/AskAssistantChat/__tests__/__snapshots__/AskAssistantChat.spec.ts.snap
@@ -1180,6 +1180,248 @@ exports[`AskAssistantChat > renders end of session chat correctly 1`] = `
`;
+exports[`AskAssistantChat > renders message with code snippet 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Hi Max! Here is my top solution to fix the error in your
+
+ Transform data
+
+ node👇
+
+
+
+
+
+
+
+ node.on('input', function(msg) {
+
+
+if (msg.seed) { dummyjson.seed = msg.seed; }
+
+
+try {
+
+
+var value = dummyjson.parse(node.template, {mockdata: msg});
+
+
+if (node.syntax === 'json') {
+
+
+try { value = JSON.parse(value); }
+
+
+catch(e) { node.error(RED._('datagen.errors.json-error')); }
+
+
+}
+
+
+if (node.fieldType === 'msg') {
+
+
+RED.util.setMessageProperty(msg,node.field,value);
+
+
+}
+
+
+else if (node.fieldType === 'flow') {
+
+
+node.context().flow.set(node.field,value);
+
+
+}
+
+
+else if (node.fieldType === 'global') {
+
+
+node.context().global.set(node.field,value);
+
+
+}
+
+
+node.send(msg);
+
+
+}
+
+
+catch(e) {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
exports[`AskAssistantChat > renders streaming chat correctly 1`] = `
Date: Mon, 16 Sep 2024 13:32:40 +0300
Subject: [PATCH 07/42] ci(benchmark): Always perform az login before teardown
(#10827)
---
.github/workflows/benchmark-nightly.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/benchmark-nightly.yml b/.github/workflows/benchmark-nightly.yml
index a90cd12650..a144eb21b0 100644
--- a/.github/workflows/benchmark-nightly.yml
+++ b/.github/workflows/benchmark-nightly.yml
@@ -84,6 +84,7 @@ jobs:
# We need to login again because the access token expires
- name: Azure login
+ if: always()
uses: azure/login@v2.1.1
with:
client-id: ${{ env.ARM_CLIENT_ID }}
From 25c8a328a8e7918bf82f458d3598fc2c726b25ef Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?=
=?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?=
=?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?=
Date: Mon, 16 Sep 2024 13:37:14 +0200
Subject: [PATCH 08/42] refactor(core): Move `instanceType` to
`InstanceSettings` (no-changelog) (#10640)
---
packages/cli/src/__tests__/license.test.ts | 26 ++++++++++++-------
packages/cli/src/abstract-server.ts | 4 +--
packages/cli/src/commands/base-command.ts | 18 +++----------
packages/cli/src/commands/start.ts | 1 -
packages/cli/src/commands/webhook.ts | 1 -
packages/cli/src/commands/worker.ts | 1 -
packages/cli/src/config/schema.ts | 6 -----
.../cli/src/databases/utils/generators.ts | 5 ++--
packages/cli/src/interfaces.ts | 2 --
packages/cli/src/license.ts | 13 +++++-----
.../scaling/__tests__/scaling.service.test.ts | 20 +++++++-------
packages/cli/src/scaling/scaling.service.ts | 11 ++++----
.../__tests__/orchestration.service.test.ts | 4 ++-
.../cli/src/services/orchestration.service.ts | 4 +--
.../main/handle-command-message-main.ts | 3 ++-
.../webhook/handle-command-message-webhook.ts | 3 ++-
.../webhook/orchestration.webhook.service.ts | 2 +-
.../worker/orchestration.worker.service.ts | 2 +-
packages/cli/src/services/pruning.service.ts | 13 +++-------
packages/cli/src/workflow-runner.ts | 5 ++--
.../integration/commands/worker.cmd.test.ts | 3 +++
.../cli/test/integration/webhooks.api.test.ts | 6 ++---
.../cli/test/integration/webhooks.test.ts | 8 +++---
packages/core/src/InstanceSettings.ts | 11 ++++++++
packages/core/src/index.ts | 2 +-
25 files changed, 85 insertions(+), 89 deletions(-)
diff --git a/packages/cli/src/__tests__/license.test.ts b/packages/cli/src/__tests__/license.test.ts
index 83c33645ab..6f2e06752f 100644
--- a/packages/cli/src/__tests__/license.test.ts
+++ b/packages/cli/src/__tests__/license.test.ts
@@ -1,13 +1,11 @@
import { LicenseManager } from '@n8n_io/license-sdk';
import { mock } from 'jest-mock-extended';
-import { InstanceSettings } from 'n8n-core';
+import type { InstanceSettings } from 'n8n-core';
import config from '@/config';
import { N8N_VERSION } from '@/constants';
import { License } from '@/license';
-import { Logger } from '@/logger';
-import { OrchestrationService } from '@/services/orchestration.service';
-import { mockInstance } from '@test/mocking';
+import type { Logger } from '@/logger';
jest.mock('@n8n_io/license-sdk');
@@ -27,9 +25,11 @@ describe('License', () => {
});
let license: License;
- const logger = mockInstance(Logger);
- const instanceSettings = mockInstance(InstanceSettings, { instanceId: MOCK_INSTANCE_ID });
- mockInstance(OrchestrationService);
+ const logger = mock();
+ const instanceSettings = mock({
+ instanceId: MOCK_INSTANCE_ID,
+ instanceType: 'main',
+ });
beforeEach(async () => {
license = new License(logger, instanceSettings, mock(), mock(), mock());
@@ -56,8 +56,14 @@ describe('License', () => {
});
test('initializes license manager for worker', async () => {
- license = new License(logger, instanceSettings, mock(), mock(), mock());
- await license.init('worker');
+ license = new License(
+ logger,
+ mock({ instanceType: 'worker' }),
+ mock(),
+ mock(),
+ mock(),
+ );
+ await license.init();
expect(LicenseManager).toHaveBeenCalledWith({
autoRenewEnabled: false,
autoRenewOffset: MOCK_RENEW_OFFSET,
@@ -265,7 +271,7 @@ describe('License', () => {
await license.reinit();
- expect(initSpy).toHaveBeenCalledWith('main', true);
+ expect(initSpy).toHaveBeenCalledWith(true);
expect(LicenseManager.prototype.reset).toHaveBeenCalled();
expect(LicenseManager.prototype.initialize).toHaveBeenCalled();
diff --git a/packages/cli/src/abstract-server.ts b/packages/cli/src/abstract-server.ts
index b5892d173b..3c60a3e48d 100644
--- a/packages/cli/src/abstract-server.ts
+++ b/packages/cli/src/abstract-server.ts
@@ -5,6 +5,7 @@ import { engine as expressHandlebars } from 'express-handlebars';
import { readFile } from 'fs/promises';
import type { Server } from 'http';
import isbot from 'isbot';
+import type { InstanceType } from 'n8n-core';
import { Container, Service } from 'typedi';
import config from '@/config';
@@ -12,7 +13,6 @@ import { N8N_VERSION, TEMPLATES_DIR, inDevelopment, inTest } from '@/constants';
import * as Db from '@/db';
import { OnShutdown } from '@/decorators/on-shutdown';
import { ExternalHooks } from '@/external-hooks';
-import { N8nInstanceType } from '@/interfaces';
import { Logger } from '@/logger';
import { rawBodyReader, bodyParser, corsMiddleware } from '@/middlewares';
import { send, sendErrorResponse } from '@/response-helper';
@@ -61,7 +61,7 @@ export abstract class AbstractServer {
readonly uniqueInstanceId: string;
- constructor(instanceType: N8nInstanceType = 'main') {
+ constructor(instanceType: Exclude) {
this.app = express();
this.app.disable('x-powered-by');
diff --git a/packages/cli/src/commands/base-command.ts b/packages/cli/src/commands/base-command.ts
index 2f995e610c..857ca231d4 100644
--- a/packages/cli/src/commands/base-command.ts
+++ b/packages/cli/src/commands/base-command.ts
@@ -17,7 +17,6 @@ import { TelemetryEventRelay } from '@/events/telemetry-event-relay';
import { initExpressionEvaluator } from '@/expression-evaluator';
import { ExternalHooks } from '@/external-hooks';
import { ExternalSecretsManager } from '@/external-secrets/external-secrets-manager.ee';
-import type { N8nInstanceType } from '@/interfaces';
import { License } from '@/license';
import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials';
import { Logger } from '@/logger';
@@ -33,9 +32,7 @@ export abstract class BaseCommand extends Command {
protected nodeTypes: NodeTypes;
- protected instanceSettings: InstanceSettings;
-
- private instanceType: N8nInstanceType = 'main';
+ protected instanceSettings: InstanceSettings = Container.get(InstanceSettings);
queueModeId: string;
@@ -62,9 +59,6 @@ export abstract class BaseCommand extends Command {
process.once('SIGTERM', this.onTerminationSignal('SIGTERM'));
process.once('SIGINT', this.onTerminationSignal('SIGINT'));
- // Make sure the settings exist
- this.instanceSettings = Container.get(InstanceSettings);
-
this.nodeTypes = Container.get(NodeTypes);
await Container.get(LoadNodesAndCredentials).init();
@@ -128,17 +122,13 @@ export abstract class BaseCommand extends Command {
await Container.get(TelemetryEventRelay).init();
}
- protected setInstanceType(instanceType: N8nInstanceType) {
- this.instanceType = instanceType;
- config.set('generic.instanceType', instanceType);
- }
-
protected setInstanceQueueModeId() {
if (config.get('redis.queueModeId')) {
this.queueModeId = config.get('redis.queueModeId');
return;
}
- this.queueModeId = generateHostInstanceId(this.instanceType);
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
+ this.queueModeId = generateHostInstanceId(this.instanceSettings.instanceType!);
config.set('redis.queueModeId', this.queueModeId);
}
@@ -278,7 +268,7 @@ export abstract class BaseCommand extends Command {
async initLicense(): Promise {
this.license = Container.get(License);
- await this.license.init(this.instanceType ?? 'main');
+ await this.license.init();
const activationKey = config.getEnv('license.activationKey');
diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts
index 1bcf5bd9cb..539934bd99 100644
--- a/packages/cli/src/commands/start.ts
+++ b/packages/cli/src/commands/start.ts
@@ -69,7 +69,6 @@ export class Start extends BaseCommand {
constructor(argv: string[], cmdConfig: Config) {
super(argv, cmdConfig);
- this.setInstanceType('main');
this.setInstanceQueueModeId();
}
diff --git a/packages/cli/src/commands/webhook.ts b/packages/cli/src/commands/webhook.ts
index 655ccee366..e8a47e10e0 100644
--- a/packages/cli/src/commands/webhook.ts
+++ b/packages/cli/src/commands/webhook.ts
@@ -25,7 +25,6 @@ export class Webhook extends BaseCommand {
constructor(argv: string[], cmdConfig: Config) {
super(argv, cmdConfig);
- this.setInstanceType('webhook');
if (this.queueModeId) {
this.logger.debug(`Webhook Instance queue mode id: ${this.queueModeId}`);
}
diff --git a/packages/cli/src/commands/worker.ts b/packages/cli/src/commands/worker.ts
index c4637e03d9..38bde018ea 100644
--- a/packages/cli/src/commands/worker.ts
+++ b/packages/cli/src/commands/worker.ts
@@ -78,7 +78,6 @@ export class Worker extends BaseCommand {
);
}
- this.setInstanceType('worker');
this.setInstanceQueueModeId();
}
diff --git a/packages/cli/src/config/schema.ts b/packages/cli/src/config/schema.ts
index ec3d401b65..1f1132b630 100644
--- a/packages/cli/src/config/schema.ts
+++ b/packages/cli/src/config/schema.ts
@@ -175,12 +175,6 @@ export const schema = {
env: 'GENERIC_TIMEZONE',
},
- instanceType: {
- doc: 'Type of n8n instance',
- format: ['main', 'webhook', 'worker'] as const,
- default: 'main',
- },
-
releaseChannel: {
doc: 'N8N release channel',
format: ['stable', 'beta', 'nightly', 'dev'] as const,
diff --git a/packages/cli/src/databases/utils/generators.ts b/packages/cli/src/databases/utils/generators.ts
index 3bc24712a2..85a1d980a4 100644
--- a/packages/cli/src/databases/utils/generators.ts
+++ b/packages/cli/src/databases/utils/generators.ts
@@ -1,14 +1,13 @@
+import type { InstanceType } from 'n8n-core';
import { ALPHABET } from 'n8n-workflow';
import { customAlphabet } from 'nanoid';
-import type { N8nInstanceType } from '@/interfaces';
-
const nanoid = customAlphabet(ALPHABET, 16);
export function generateNanoId() {
return nanoid();
}
-export function generateHostInstanceId(instanceType: N8nInstanceType) {
+export function generateHostInstanceId(instanceType: InstanceType) {
return `${instanceType}-${nanoid()}`;
}
diff --git a/packages/cli/src/interfaces.ts b/packages/cli/src/interfaces.ts
index 4d54e185a9..aeb2573d55 100644
--- a/packages/cli/src/interfaces.ts
+++ b/packages/cli/src/interfaces.ts
@@ -422,5 +422,3 @@ export abstract class SecretsProvider {
abstract hasSecret(name: string): boolean;
abstract getSecretNames(): string[];
}
-
-export type N8nInstanceType = 'main' | 'webhook' | 'worker';
diff --git a/packages/cli/src/license.ts b/packages/cli/src/license.ts
index 6e4ced8751..7f6ef42f3c 100644
--- a/packages/cli/src/license.ts
+++ b/packages/cli/src/license.ts
@@ -17,7 +17,7 @@ import {
SETTINGS_LICENSE_CERT_KEY,
UNLIMITED_LICENSE_QUOTA,
} from './constants';
-import type { BooleanLicenseFeature, N8nInstanceType, NumericLicenseFeature } from './interfaces';
+import type { BooleanLicenseFeature, NumericLicenseFeature } from './interfaces';
import type { RedisServicePubSubPublisher } from './services/redis/redis-service-pub-sub-publisher';
import { RedisService } from './services/redis.service';
@@ -46,8 +46,8 @@ export class License {
/**
* Whether this instance should renew the license - on init and periodically.
*/
- private renewalEnabled(instanceType: N8nInstanceType) {
- if (instanceType !== 'main') return false;
+ private renewalEnabled() {
+ if (this.instanceSettings.instanceType !== 'main') return false;
const autoRenewEnabled = config.getEnv('license.autoRenewEnabled');
@@ -63,7 +63,7 @@ export class License {
return autoRenewEnabled;
}
- async init(instanceType: N8nInstanceType = 'main', forceRecreate = false) {
+ async init(forceRecreate = false) {
if (this.manager && !forceRecreate) {
this.logger.warn('License manager already initialized or shutting down');
return;
@@ -73,6 +73,7 @@ export class License {
return;
}
+ const { instanceType } = this.instanceSettings;
const isMainInstance = instanceType === 'main';
const server = config.getEnv('license.serverUrl');
const offlineMode = !isMainInstance;
@@ -90,7 +91,7 @@ export class License {
? async () => await this.licenseMetricsService.collectPassthroughData()
: async () => ({});
- const renewalEnabled = this.renewalEnabled(instanceType);
+ const renewalEnabled = this.renewalEnabled();
try {
this.manager = new LicenseManager({
@@ -399,7 +400,7 @@ export class License {
async reinit() {
this.manager?.reset();
- await this.init('main', true);
+ await this.init(true);
this.logger.debug('License reinitialized');
}
}
diff --git a/packages/cli/src/scaling/__tests__/scaling.service.test.ts b/packages/cli/src/scaling/__tests__/scaling.service.test.ts
index 8a95042697..9beae22af6 100644
--- a/packages/cli/src/scaling/__tests__/scaling.service.test.ts
+++ b/packages/cli/src/scaling/__tests__/scaling.service.test.ts
@@ -5,7 +5,6 @@ import { InstanceSettings } from 'n8n-core';
import { ApplicationError } from 'n8n-workflow';
import Container from 'typedi';
-import config from '@/config';
import type { OrchestrationService } from '@/services/orchestration.service';
import { mockInstance } from '@test/mocking';
@@ -70,7 +69,8 @@ describe('ScalingService', () => {
beforeEach(() => {
jest.clearAllMocks();
- config.set('generic.instanceType', 'main');
+ // @ts-expect-error readonly property
+ instanceSettings.instanceType = 'main';
instanceSettings.markAsLeader();
scalingService = new ScalingService(
@@ -128,8 +128,8 @@ describe('ScalingService', () => {
describe('if worker', () => {
it('should set up queue + listeners', async () => {
- // @ts-expect-error Private field
- scalingService.instanceType = 'worker';
+ // @ts-expect-error readonly property
+ instanceSettings.instanceType = 'worker';
await scalingService.setupQueue();
@@ -141,8 +141,8 @@ describe('ScalingService', () => {
describe('webhook', () => {
it('should set up a queue + listeners', async () => {
- // @ts-expect-error Private field
- scalingService.instanceType = 'webhook';
+ // @ts-expect-error readonly property
+ instanceSettings.instanceType = 'webhook';
await scalingService.setupQueue();
@@ -155,8 +155,8 @@ describe('ScalingService', () => {
describe('setupWorker', () => {
it('should set up a worker with concurrency', async () => {
- // @ts-expect-error Private field
- scalingService.instanceType = 'worker';
+ // @ts-expect-error readonly property
+ instanceSettings.instanceType = 'worker';
await scalingService.setupQueue();
const concurrency = 5;
@@ -172,8 +172,8 @@ describe('ScalingService', () => {
});
it('should throw if called before queue is ready', async () => {
- // @ts-expect-error Private field
- scalingService.instanceType = 'worker';
+ // @ts-expect-error readonly property
+ instanceSettings.instanceType = 'worker';
expect(() => scalingService.setupWorker(5)).toThrow();
});
diff --git a/packages/cli/src/scaling/scaling.service.ts b/packages/cli/src/scaling/scaling.service.ts
index 2aa06a691b..3e3461908b 100644
--- a/packages/cli/src/scaling/scaling.service.ts
+++ b/packages/cli/src/scaling/scaling.service.ts
@@ -31,8 +31,6 @@ import type {
export class ScalingService {
private queue: JobQueue;
- private readonly instanceType = config.getEnv('generic.instanceType');
-
constructor(
private readonly logger: Logger,
private readonly activeExecutions: ActiveExecutions,
@@ -211,9 +209,10 @@ export class ScalingService {
throw error;
});
- if (this.instanceType === 'main' || this.instanceType === 'webhook') {
+ const { instanceType } = this.instanceSettings;
+ if (instanceType === 'main' || instanceType === 'webhook') {
this.registerMainOrWebhookListeners();
- } else if (this.instanceType === 'worker') {
+ } else if (instanceType === 'worker') {
this.registerWorkerListeners();
}
}
@@ -295,7 +294,7 @@ export class ScalingService {
}
private assertWorker() {
- if (this.instanceType === 'worker') return;
+ if (this.instanceSettings.instanceType === 'worker') return;
throw new ApplicationError('This method must be called on a `worker` instance');
}
@@ -311,7 +310,7 @@ export class ScalingService {
get isQueueMetricsEnabled() {
return (
this.globalConfig.endpoints.metrics.includeQueueMetrics &&
- this.instanceType === 'main' &&
+ this.instanceSettings.instanceType === 'main' &&
!this.orchestrationService.isMultiMainSetupEnabled
);
}
diff --git a/packages/cli/src/services/__tests__/orchestration.service.test.ts b/packages/cli/src/services/__tests__/orchestration.service.test.ts
index 3ec7e4cf78..493453d308 100644
--- a/packages/cli/src/services/__tests__/orchestration.service.test.ts
+++ b/packages/cli/src/services/__tests__/orchestration.service.test.ts
@@ -34,7 +34,6 @@ let queueModeId: string;
function setDefaultConfig() {
config.set('executions.mode', 'queue');
- config.set('generic.instanceType', 'main');
}
const workerRestartEventBusResponse: RedisServiceWorkerResponseObject = {
@@ -73,6 +72,9 @@ describe('Orchestration Service', () => {
});
setDefaultConfig();
queueModeId = config.get('redis.queueModeId');
+
+ // @ts-expect-error readonly property
+ instanceSettings.instanceType = 'main';
});
beforeEach(() => {
diff --git a/packages/cli/src/services/orchestration.service.ts b/packages/cli/src/services/orchestration.service.ts
index d1e4b37ef7..b95191efb8 100644
--- a/packages/cli/src/services/orchestration.service.ts
+++ b/packages/cli/src/services/orchestration.service.ts
@@ -14,7 +14,7 @@ import { RedisService } from './redis.service';
export class OrchestrationService {
constructor(
private readonly logger: Logger,
- private readonly instanceSettings: InstanceSettings,
+ protected readonly instanceSettings: InstanceSettings,
private readonly redisService: RedisService,
readonly multiMainSetup: MultiMainSetup,
) {}
@@ -31,7 +31,7 @@ export class OrchestrationService {
return (
config.getEnv('executions.mode') === 'queue' &&
config.getEnv('multiMainSetup.enabled') &&
- config.getEnv('generic.instanceType') === 'main' &&
+ this.instanceSettings.instanceType === 'main' &&
this.isMultiMainSetupLicensed
);
}
diff --git a/packages/cli/src/services/orchestration/main/handle-command-message-main.ts b/packages/cli/src/services/orchestration/main/handle-command-message-main.ts
index 9193e344bb..15917e11b7 100644
--- a/packages/cli/src/services/orchestration/main/handle-command-message-main.ts
+++ b/packages/cli/src/services/orchestration/main/handle-command-message-main.ts
@@ -1,3 +1,4 @@
+import { InstanceSettings } from 'n8n-core';
import { Container } from 'typedi';
import { ActiveWorkflowManager } from '@/active-workflow-manager';
@@ -17,7 +18,7 @@ import { debounceMessageReceiver, messageToRedisServiceCommandObject } from '../
// eslint-disable-next-line complexity
export async function handleCommandMessageMain(messageString: string) {
const queueModeId = config.getEnv('redis.queueModeId');
- const isMainInstance = config.getEnv('generic.instanceType') === 'main';
+ const isMainInstance = Container.get(InstanceSettings).instanceType === 'main';
const message = messageToRedisServiceCommandObject(messageString);
const logger = Container.get(Logger);
diff --git a/packages/cli/src/services/orchestration/webhook/handle-command-message-webhook.ts b/packages/cli/src/services/orchestration/webhook/handle-command-message-webhook.ts
index 8a69505957..542b8f1f52 100644
--- a/packages/cli/src/services/orchestration/webhook/handle-command-message-webhook.ts
+++ b/packages/cli/src/services/orchestration/webhook/handle-command-message-webhook.ts
@@ -1,3 +1,4 @@
+import { InstanceSettings } from 'n8n-core';
import Container from 'typedi';
import { Logger } from 'winston';
@@ -11,7 +12,7 @@ import { messageToRedisServiceCommandObject, debounceMessageReceiver } from '../
export async function handleCommandMessageWebhook(messageString: string) {
const queueModeId = config.getEnv('redis.queueModeId');
- const isMainInstance = config.getEnv('generic.instanceType') === 'main';
+ const isMainInstance = Container.get(InstanceSettings).instanceType === 'main';
const message = messageToRedisServiceCommandObject(messageString);
const logger = Container.get(Logger);
diff --git a/packages/cli/src/services/orchestration/webhook/orchestration.webhook.service.ts b/packages/cli/src/services/orchestration/webhook/orchestration.webhook.service.ts
index 84ba61a904..6b1c86fc6a 100644
--- a/packages/cli/src/services/orchestration/webhook/orchestration.webhook.service.ts
+++ b/packages/cli/src/services/orchestration/webhook/orchestration.webhook.service.ts
@@ -10,7 +10,7 @@ export class OrchestrationWebhookService extends OrchestrationService {
return (
this.isInitialized &&
config.get('executions.mode') === 'queue' &&
- config.get('generic.instanceType') === 'webhook'
+ this.instanceSettings.instanceType === 'webhook'
);
}
}
diff --git a/packages/cli/src/services/orchestration/worker/orchestration.worker.service.ts b/packages/cli/src/services/orchestration/worker/orchestration.worker.service.ts
index ae000fd501..1d0d822aeb 100644
--- a/packages/cli/src/services/orchestration/worker/orchestration.worker.service.ts
+++ b/packages/cli/src/services/orchestration/worker/orchestration.worker.service.ts
@@ -10,7 +10,7 @@ export class OrchestrationWorkerService extends OrchestrationService {
return (
this.isInitialized &&
config.get('executions.mode') === 'queue' &&
- config.get('generic.instanceType') === 'worker'
+ this.instanceSettings.instanceType === 'worker'
);
}
}
diff --git a/packages/cli/src/services/pruning.service.ts b/packages/cli/src/services/pruning.service.ts
index d541e5898c..b0ebc99dfd 100644
--- a/packages/cli/src/services/pruning.service.ts
+++ b/packages/cli/src/services/pruning.service.ts
@@ -48,19 +48,12 @@ export class PruningService {
}
private isPruningEnabled() {
- if (
- !config.getEnv('executions.pruneData') ||
- inTest ||
- config.get('generic.instanceType') !== 'main'
- ) {
+ const { instanceType, isFollower } = this.instanceSettings;
+ if (!config.getEnv('executions.pruneData') || inTest || instanceType !== 'main') {
return false;
}
- if (
- config.getEnv('multiMainSetup.enabled') &&
- config.getEnv('generic.instanceType') === 'main' &&
- this.instanceSettings.isFollower
- ) {
+ if (config.getEnv('multiMainSetup.enabled') && instanceType === 'main' && isFollower) {
return false;
}
diff --git a/packages/cli/src/workflow-runner.ts b/packages/cli/src/workflow-runner.ts
index a10646ac0b..aea8bfc15e 100644
--- a/packages/cli/src/workflow-runner.ts
+++ b/packages/cli/src/workflow-runner.ts
@@ -3,7 +3,7 @@
/* eslint-disable @typescript-eslint/no-shadow */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { GlobalConfig } from '@n8n/config';
-import { WorkflowExecute } from 'n8n-core';
+import { InstanceSettings, WorkflowExecute } from 'n8n-core';
import type {
ExecutionError,
IDeferredPromise,
@@ -54,6 +54,7 @@ export class WorkflowRunner {
private readonly nodeTypes: NodeTypes,
private readonly permissionChecker: PermissionChecker,
private readonly eventService: EventService,
+ private readonly instanceSettings: InstanceSettings,
) {}
/** The process did error */
@@ -150,7 +151,7 @@ export class WorkflowRunner {
// since these calls are now done by the worker directly
if (
this.executionsMode !== 'queue' ||
- config.getEnv('generic.instanceType') === 'worker' ||
+ this.instanceSettings.instanceType === 'worker' ||
data.executionMode === 'manual'
) {
const postExecutePromise = this.activeExecutions.getPostExecutePromise(executionId);
diff --git a/packages/cli/test/integration/commands/worker.cmd.test.ts b/packages/cli/test/integration/commands/worker.cmd.test.ts
index 679eb8c892..31cdf282ec 100644
--- a/packages/cli/test/integration/commands/worker.cmd.test.ts
+++ b/packages/cli/test/integration/commands/worker.cmd.test.ts
@@ -1,3 +1,5 @@
+process.argv[2] = 'worker';
+
import { BinaryDataService } from 'n8n-core';
import { Worker } from '@/commands/worker';
@@ -27,6 +29,7 @@ const logStreamingEventRelay = mockInstance(LogStreamingEventRelay);
const orchestrationHandlerWorkerService = mockInstance(OrchestrationHandlerWorkerService);
const scalingService = mockInstance(ScalingService);
const orchestrationWorkerService = mockInstance(OrchestrationWorkerService);
+
const command = setupTestCommand(Worker);
test('worker initializes all its components', async () => {
diff --git a/packages/cli/test/integration/webhooks.api.test.ts b/packages/cli/test/integration/webhooks.api.test.ts
index 75ada5b820..3e10418256 100644
--- a/packages/cli/test/integration/webhooks.api.test.ts
+++ b/packages/cli/test/integration/webhooks.api.test.ts
@@ -7,12 +7,12 @@ import {
} from 'n8n-workflow';
import { agent as testAgent } from 'supertest';
-import { AbstractServer } from '@/abstract-server';
import type { WorkflowEntity } from '@/databases/entities/workflow-entity';
import { ExternalHooks } from '@/external-hooks';
import { NodeTypes } from '@/node-types';
import { Push } from '@/push';
import { Telemetry } from '@/telemetry';
+import { WebhookServer } from '@/webhooks/webhook-server';
import { createUser } from './shared/db/users';
import { createWorkflow } from './shared/db/workflows';
@@ -49,7 +49,7 @@ describe('Webhook API', () => {
await initActiveWorkflowManager();
- const server = new (class extends AbstractServer {})();
+ const server = new WebhookServer();
await server.start();
agent = testAgent(server.app);
});
@@ -152,7 +152,7 @@ describe('Webhook API', () => {
await initActiveWorkflowManager();
- const server = new (class extends AbstractServer {})();
+ const server = new WebhookServer();
await server.start();
agent = testAgent(server.app);
});
diff --git a/packages/cli/test/integration/webhooks.test.ts b/packages/cli/test/integration/webhooks.test.ts
index e4a6ca1025..165822aa84 100644
--- a/packages/cli/test/integration/webhooks.test.ts
+++ b/packages/cli/test/integration/webhooks.test.ts
@@ -4,12 +4,12 @@ import { agent as testAgent } from 'supertest';
import type SuperAgentTest from 'supertest/lib/agent';
import Container from 'typedi';
-import { AbstractServer } from '@/abstract-server';
import { ExternalHooks } from '@/external-hooks';
import { WaitingForms } from '@/waiting-forms';
import { LiveWebhooks } from '@/webhooks/live-webhooks';
import { TestWebhooks } from '@/webhooks/test-webhooks';
import { WaitingWebhooks } from '@/webhooks/waiting-webhooks';
+import { WebhookServer } from '@/webhooks/webhook-server';
import type { IWebhookResponseCallbackData } from '@/webhooks/webhook.types';
import { mockInstance } from '@test/mocking';
@@ -26,9 +26,9 @@ describe('WebhookServer', () => {
mockInstance(WaitingForms);
beforeAll(async () => {
- const server = new (class extends AbstractServer {
- testWebhooksEnabled = true;
- })();
+ const server = new WebhookServer();
+ // @ts-expect-error: testWebhooksEnabled is private
+ server.testWebhooksEnabled = true;
await server.start();
agent = testAgent(server.app);
});
diff --git a/packages/core/src/InstanceSettings.ts b/packages/core/src/InstanceSettings.ts
index fb57cbc36b..44f4b0c336 100644
--- a/packages/core/src/InstanceSettings.ts
+++ b/packages/core/src/InstanceSettings.ts
@@ -16,6 +16,8 @@ type Settings = ReadOnlySettings & WritableSettings;
type InstanceRole = 'unset' | 'leader' | 'follower';
+export type InstanceType = 'main' | 'webhook' | 'worker';
+
const inTest = process.env.NODE_ENV === 'test';
@Service()
@@ -40,6 +42,15 @@ export class InstanceSettings {
readonly instanceId = this.generateInstanceId();
+ readonly instanceType: InstanceType;
+
+ constructor() {
+ const command = process.argv[2];
+ this.instanceType = ['webhook', 'worker'].includes(command)
+ ? (command as InstanceType)
+ : 'main';
+ }
+
/**
* A main is:
* - `unset` during bootup,
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index 3a988ecc4d..c6b8450a4f 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -10,7 +10,7 @@ export * from './Constants';
export * from './Credentials';
export * from './DirectoryLoader';
export * from './Interfaces';
-export { InstanceSettings } from './InstanceSettings';
+export { InstanceSettings, InstanceType } from './InstanceSettings';
export * from './NodeExecuteFunctions';
export * from './WorkflowExecute';
export { NodeExecuteFunctions };
From 7c23101ab8c12b735a17deb35637f3f12c00aeb0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?=
=?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?=
=?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?=
Date: Mon, 16 Sep 2024 15:17:33 +0200
Subject: [PATCH 09/42] fix(editor): Render image binary-data using img tags
(#10829)
Co-authored-by: Milorad Filipovic
---
.../src/components/BinaryDataDisplayEmbed.vue | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/packages/editor-ui/src/components/BinaryDataDisplayEmbed.vue b/packages/editor-ui/src/components/BinaryDataDisplayEmbed.vue
index fe822821e2..fbb7a68736 100644
--- a/packages/editor-ui/src/components/BinaryDataDisplayEmbed.vue
+++ b/packages/editor-ui/src/components/BinaryDataDisplayEmbed.vue
@@ -63,6 +63,7 @@ onMounted(async () => {
{{ $locale.baseText('binaryDataDisplay.yourBrowserDoesNotSupport') }}
+
{
From 7d35ebc61ee991449523a1244f9b8dda19f963db Mon Sep 17 00:00:00 2001
From: Tomi Turtiainen <10324676+tomi@users.noreply.github.com>
Date: Mon, 16 Sep 2024 21:09:44 +0300
Subject: [PATCH 10/42] ci: Ignore certain paths for e2e tests for PRs
(no-changelog) (#10533)
---
.github/workflows/chromatic.yml | 42 +++++++++++++++++++----
.github/workflows/ci-postgres-mysql.yml | 3 +-
.github/workflows/e2e-tests-pr.yml | 44 +++++++++++++++++++++----
3 files changed, 75 insertions(+), 14 deletions(-)
diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml
index d2ebc05320..7c5682076a 100644
--- a/.github/workflows/chromatic.yml
+++ b/.github/workflows/chromatic.yml
@@ -4,19 +4,49 @@ on:
workflow_dispatch:
pull_request_review:
types: [submitted]
- branches:
- - 'master'
- paths:
- - packages/design-system/**
- - .github/workflows/chromatic.yml
concurrency:
group: chromatic-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
+ get-metadata:
+ name: Get Metadata
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out current commit
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
+ fetch-depth: 2
+
+ - name: Determine changed files
+ uses: tomi/paths-filter-action@v3.0.2
+ id: changed
+ if: github.event_name == 'pull_request_review'
+ with:
+ filters: |
+ design_system:
+ - packages/design-system/**
+ - .github/workflows/chromatic.yml
+
+ outputs:
+ design_system_files_changed: ${{ steps.changed.outputs.design_system == 'true' }}
+ is_community_pr: ${{ contains(github.event.pull_request.labels.*.name, 'community') }}
+ is_pr_target_master: ${{ github.event.pull_request.base.ref == 'master' }}
+ is_dispatch: ${{ github.event_name == 'workflow_dispatch' }}
+ is_pr_approved: ${{ github.event.review.state == 'approved' }}
+
chromatic:
- if: ${{ github.event.review.state == 'approved' && !contains(github.event.pull_request.labels.*.name, 'community') }}
+ needs: [get-metadata]
+ if: |
+ needs.get-metadata.outputs.is_dispatch == 'true' ||
+ (
+ needs.get-metadata.outputs.design_system_files_changed == 'true' &&
+ needs.get-metadata.outputs.is_community_pr == 'false' &&
+ needs.get-metadata.outputs.is_pr_target_master == 'true' &&
+ needs.get-metadata.outputs.is_pr_approved == 'true'
+ )
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.1.1
diff --git a/.github/workflows/ci-postgres-mysql.yml b/.github/workflows/ci-postgres-mysql.yml
index 1d645ae774..23088633b5 100644
--- a/.github/workflows/ci-postgres-mysql.yml
+++ b/.github/workflows/ci-postgres-mysql.yml
@@ -10,8 +10,6 @@ on:
- .github/workflows/ci-postgres-mysql.yml
pull_request_review:
types: [submitted]
- branches:
- - 'release/*'
concurrency:
group: db-${{ github.event.pull_request.number || github.ref }}
@@ -21,6 +19,7 @@ jobs:
build:
name: Install & Build
runs-on: ubuntu-latest
+ if: github.event_name != 'pull_request_review' || startsWith(github.event.pull_request.base.ref, 'release/')
steps:
- uses: actions/checkout@v4.1.1
- run: corepack enable
diff --git a/.github/workflows/e2e-tests-pr.yml b/.github/workflows/e2e-tests-pr.yml
index 3d2f122638..047ce2d13a 100644
--- a/.github/workflows/e2e-tests-pr.yml
+++ b/.github/workflows/e2e-tests-pr.yml
@@ -3,19 +3,51 @@ name: PR E2E
on:
pull_request_review:
types: [submitted]
- branches:
- - 'master'
- - 'release/*'
concurrency:
group: e2e-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
+ get-metadata:
+ name: Get Metadata
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out current commit
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
+ fetch-depth: 2
+
+ - name: Determine changed files
+ uses: tomi/paths-filter-action@v3.0.2
+ id: changed
+ with:
+ filters: |
+ not_ignored:
+ - '!.devcontainer/**'
+ - '!.github/*'
+ - '!.github/scripts/*'
+ - '!.github/workflows/benchmark-*'
+ - '!.github/workflows/check-*'
+ - '!.vscode/**'
+ - '!docker/**'
+ - '!packages/@n8n/benchmark/**'
+ - '!**/*.md'
+ predicate-quantifier: 'every'
+
+ outputs:
+ # The workflow should run when:
+ # - It has changes to files that are not ignored
+ # - It is not a community PR
+ # - It is targeting master or a release branch
+ should_run: ${{ steps.changed.outputs.not_ignored == 'true' && !contains(github.event.pull_request.labels.*.name, 'community') && (github.event.pull_request.base.ref == 'master' || startsWith(github.event.pull_request.base.ref, 'release/')) }}
+
run-e2e-tests:
name: E2E [Electron/Node 18]
uses: ./.github/workflows/e2e-reusable.yml
- if: ${{ github.event.review.state == 'approved' && !contains(github.event.pull_request.labels.*.name, 'community') }}
+ needs: [get-metadata]
+ if: ${{ github.event.review.state == 'approved' && needs.get-metadata.outputs.should_run == 'true' }}
with:
pr_number: ${{ github.event.pull_request.number }}
user: ${{ github.event.pull_request.user.login || 'PR User' }}
@@ -25,11 +57,11 @@ jobs:
post-e2e-tests:
runs-on: ubuntu-latest
name: E2E [Electron/Node 18] - Checks
- needs: [run-e2e-tests]
+ needs: [get-metadata, run-e2e-tests]
if: always()
steps:
- name: E2E success comment
- if: ${{!contains(github.event.pull_request.labels.*.name, 'community') && needs.run-e2e-tests.outputs.tests_passed == 'true' }}
+ if: ${{ needs.get-metadata.outputs.should_run == 'true' && needs.run-e2e-tests.outputs.tests_passed == 'true' }}
uses: peter-evans/create-or-update-comment@v4.0.0
with:
issue-number: ${{ github.event.pull_request.number }}
From a6e10649850a562b72ea417866d7e9c887484895 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?=
Date: Tue, 17 Sep 2024 08:47:03 +0200
Subject: [PATCH 11/42] test(core): Fix license mock in worker test (#10824)
---
packages/cli/test/integration/commands/worker.cmd.test.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/cli/test/integration/commands/worker.cmd.test.ts b/packages/cli/test/integration/commands/worker.cmd.test.ts
index 31cdf282ec..726c78537e 100644
--- a/packages/cli/test/integration/commands/worker.cmd.test.ts
+++ b/packages/cli/test/integration/commands/worker.cmd.test.ts
@@ -23,7 +23,7 @@ mockInstance(LoadNodesAndCredentials);
const binaryDataService = mockInstance(BinaryDataService);
const externalHooks = mockInstance(ExternalHooks);
const externalSecretsManager = mockInstance(ExternalSecretsManager);
-const license = mockInstance(License);
+const license = mockInstance(License, { loadCertStr: async () => '' });
const messageEventBus = mockInstance(MessageEventBus);
const logStreamingEventRelay = mockInstance(LogStreamingEventRelay);
const orchestrationHandlerWorkerService = mockInstance(OrchestrationHandlerWorkerService);
From d14bb3630027363d02dcd4a81b1b01bd83c34311 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?=
Date: Tue, 17 Sep 2024 09:15:09 +0200
Subject: [PATCH 12/42] test(core): Mock filesystem in tests (#10823)
---
.../source-control/source-control-export.service.ee.ts | 2 +-
.../source-control/source-control-import.service.ee.ts | 2 +-
packages/cli/test/integration/webhooks.api.test.ts | 2 ++
packages/cli/test/setup-mocks.ts | 2 ++
4 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/packages/cli/src/environments/source-control/source-control-export.service.ee.ts b/packages/cli/src/environments/source-control/source-control-export.service.ee.ts
index 6fb904b4a0..321534ff6c 100644
--- a/packages/cli/src/environments/source-control/source-control-export.service.ee.ts
+++ b/packages/cli/src/environments/source-control/source-control-export.service.ee.ts
@@ -1,7 +1,7 @@
import { rmSync } from 'fs';
-import { writeFile as fsWriteFile, rm as fsRm } from 'fs/promises';
import { Credentials, InstanceSettings } from 'n8n-core';
import { ApplicationError, type ICredentialDataDecryptedObject } from 'n8n-workflow';
+import { writeFile as fsWriteFile, rm as fsRm } from 'node:fs/promises';
import path from 'path';
import Container, { Service } from 'typedi';
diff --git a/packages/cli/src/environments/source-control/source-control-import.service.ee.ts b/packages/cli/src/environments/source-control/source-control-import.service.ee.ts
index bf69975e2b..fb491f39d2 100644
--- a/packages/cli/src/environments/source-control/source-control-import.service.ee.ts
+++ b/packages/cli/src/environments/source-control/source-control-import.service.ee.ts
@@ -1,9 +1,9 @@
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
import { In } from '@n8n/typeorm';
import glob from 'fast-glob';
-import { readFile as fsReadFile } from 'fs/promises';
import { Credentials, InstanceSettings } from 'n8n-core';
import { ApplicationError, jsonParse, ErrorReporterProxy as ErrorReporter } from 'n8n-workflow';
+import { readFile as fsReadFile } from 'node:fs/promises';
import path from 'path';
import { Container, Service } from 'typedi';
diff --git a/packages/cli/test/integration/webhooks.api.test.ts b/packages/cli/test/integration/webhooks.api.test.ts
index 3e10418256..f1da8e3ab0 100644
--- a/packages/cli/test/integration/webhooks.api.test.ts
+++ b/packages/cli/test/integration/webhooks.api.test.ts
@@ -21,6 +21,8 @@ import type { SuperAgentTest } from './shared/types';
import { initActiveWorkflowManager } from './shared/utils';
import { mockInstance } from '../shared/mocking';
+jest.unmock('node:fs');
+
mockInstance(Telemetry);
describe('Webhook API', () => {
diff --git a/packages/cli/test/setup-mocks.ts b/packages/cli/test/setup-mocks.ts
index 63d60cd522..ddf886f557 100644
--- a/packages/cli/test/setup-mocks.ts
+++ b/packages/cli/test/setup-mocks.ts
@@ -5,3 +5,5 @@ jest.mock('@n8n_io/license-sdk');
jest.mock('@/telemetry');
jest.mock('@/eventbus/message-event-bus/message-event-bus');
jest.mock('@/push');
+jest.mock('node:fs');
+jest.mock('node:fs/promises');
From e6d84db89930afc16f4a08fae87d8af4a059e6d7 Mon Sep 17 00:00:00 2001
From: Eugene
Date: Tue, 17 Sep 2024 09:18:45 +0200
Subject: [PATCH 13/42] fix(Google Vertex Chat Model Node): Clean service
account private key (#10770)
---
.../LmChatGoogleVertex/LmChatGoogleVertex.node.ts | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/packages/@n8n/nodes-langchain/nodes/llms/LmChatGoogleVertex/LmChatGoogleVertex.node.ts b/packages/@n8n/nodes-langchain/nodes/llms/LmChatGoogleVertex/LmChatGoogleVertex.node.ts
index 044428c01a..55ccda90d2 100644
--- a/packages/@n8n/nodes-langchain/nodes/llms/LmChatGoogleVertex/LmChatGoogleVertex.node.ts
+++ b/packages/@n8n/nodes-langchain/nodes/llms/LmChatGoogleVertex/LmChatGoogleVertex.node.ts
@@ -12,6 +12,7 @@ import {
import { ChatVertexAI } from '@langchain/google-vertexai';
import type { SafetySetting } from '@google/generative-ai';
import { ProjectsClient } from '@google-cloud/resource-manager';
+import { formatPrivateKey } from 'n8n-nodes-base/dist/utils/utilities';
import { getConnectionHintNoticeField } from '../../../utils/sharedFields';
import { N8nLlmTracing } from '../N8nLlmTracing';
import { additionalOptions } from '../gemini-common/additional-options';
@@ -97,11 +98,13 @@ export class LmChatGoogleVertex implements INodeType {
const results: Array<{ name: string; value: string }> = [];
const credentials = await this.getCredentials('googleApi');
+ const privateKey = formatPrivateKey(credentials.privateKey as string);
+ const email = (credentials.email as string).trim();
const client = new ProjectsClient({
credentials: {
- client_email: credentials.email as string,
- private_key: credentials.privateKey as string,
+ client_email: email,
+ private_key: privateKey,
},
});
@@ -123,6 +126,8 @@ export class LmChatGoogleVertex implements INodeType {
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise {
const credentials = await this.getCredentials('googleApi');
+ const privateKey = formatPrivateKey(credentials.privateKey as string);
+ const email = (credentials.email as string).trim();
const modelName = this.getNodeParameter('modelName', itemIndex) as string;
@@ -153,8 +158,8 @@ export class LmChatGoogleVertex implements INodeType {
authOptions: {
projectId,
credentials: {
- client_email: credentials.email as string,
- private_key: credentials.privateKey as string,
+ client_email: email,
+ private_key: privateKey,
},
},
model: modelName,
From 94aa680c9b5b8bb3f7f345a8f0402d745d1f839f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?=
Date: Tue, 17 Sep 2024 10:14:12 +0200
Subject: [PATCH 14/42] refactor(core): Set up worker server (#10814)
---
packages/cli/src/commands/worker.ts | 120 +---------------
...redentials-overwrites-already-set.error.ts | 7 +
.../cli/src/errors/non-json-body.error.ts | 7 +
packages/cli/src/errors/port-taken.error.ts | 9 ++
.../scaling/__tests__/worker-server.test.ts | 127 +++++++++++++++++
packages/cli/src/scaling/worker-server.ts | 129 ++++++++++++++++++
6 files changed, 285 insertions(+), 114 deletions(-)
create mode 100644 packages/cli/src/errors/credentials-overwrites-already-set.error.ts
create mode 100644 packages/cli/src/errors/non-json-body.error.ts
create mode 100644 packages/cli/src/errors/port-taken.error.ts
create mode 100644 packages/cli/src/scaling/__tests__/worker-server.test.ts
create mode 100644 packages/cli/src/scaling/worker-server.ts
diff --git a/packages/cli/src/commands/worker.ts b/packages/cli/src/commands/worker.ts
index 38bde018ea..89e131bc0d 100644
--- a/packages/cli/src/commands/worker.ts
+++ b/packages/cli/src/commands/worker.ts
@@ -1,22 +1,15 @@
import { Flags, type Config } from '@oclif/core';
-import express from 'express';
-import http from 'http';
import { ApplicationError } from 'n8n-workflow';
import { Container } from 'typedi';
import config from '@/config';
import { N8N_VERSION, inTest } from '@/constants';
-import { CredentialsOverwrites } from '@/credentials-overwrites';
-import * as Db from '@/db';
-import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error';
import { EventMessageGeneric } from '@/eventbus/event-message-classes/event-message-generic';
import { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus';
import { LogStreamingEventRelay } from '@/events/log-streaming-event-relay';
-import type { ICredentialsOverwrite } from '@/interfaces';
-import { rawBodyReader, bodyParser } from '@/middlewares';
-import * as ResponseHelper from '@/response-helper';
import { JobProcessor } from '@/scaling/job-processor';
import type { ScalingService } from '@/scaling/scaling.service';
+import { WorkerServer } from '@/scaling/worker-server';
import { OrchestrationHandlerWorkerService } from '@/services/orchestration/worker/orchestration.handler.worker.service';
import { OrchestrationWorkerService } from '@/services/orchestration/worker/orchestration.worker.service';
import type { RedisServicePubSubSubscriber } from '@/services/redis/redis-service-pub-sub-subscriber';
@@ -164,118 +157,17 @@ export class Worker extends BaseCommand {
this.jobProcessor = Container.get(JobProcessor);
}
- async setupHealthMonitor() {
- const { port } = this.globalConfig.queue.health;
-
- const app = express();
- app.disable('x-powered-by');
-
- const server = http.createServer(app);
-
- app.get('/healthz/readiness', async (_req, res) => {
- return Db.connectionState.connected && Db.connectionState.migrated
- ? res.status(200).send({ status: 'ok' })
- : res.status(503).send({ status: 'error' });
- });
-
- app.get(
- '/healthz',
-
- async (_req: express.Request, res: express.Response) => {
- this.logger.debug('Health check started!');
-
- const connection = Db.getConnection();
-
- try {
- if (!connection.isInitialized) {
- // Connection is not active
- throw new ApplicationError('No active database connection');
- }
- // DB ping
- await connection.query('SELECT 1');
- } catch (e) {
- this.logger.error('No Database connection!', e as Error);
- const error = new ServiceUnavailableError('No Database connection!');
- return ResponseHelper.sendErrorResponse(res, error);
- }
-
- // Just to be complete, generally will the worker stop automatically
- // if it loses the connection to redis
- try {
- // Redis ping
- await this.scalingService.pingQueue();
- } catch (e) {
- this.logger.error('No Redis connection!', e as Error);
- const error = new ServiceUnavailableError('No Redis connection!');
- return ResponseHelper.sendErrorResponse(res, error);
- }
-
- // Everything fine
- const responseData = {
- status: 'ok',
- };
-
- this.logger.debug('Health check completed successfully!');
-
- ResponseHelper.sendSuccessResponse(res, responseData, true, 200);
- },
- );
-
- let presetCredentialsLoaded = false;
-
- const endpointPresetCredentials = this.globalConfig.credentials.overwrite.endpoint;
- if (endpointPresetCredentials !== '') {
- // POST endpoint to set preset credentials
- app.post(
- `/${endpointPresetCredentials}`,
- rawBodyReader,
- bodyParser,
- async (req: express.Request, res: express.Response) => {
- if (!presetCredentialsLoaded) {
- const body = req.body as ICredentialsOverwrite;
-
- if (req.contentType !== 'application/json') {
- ResponseHelper.sendErrorResponse(
- res,
- new Error(
- 'Body must be a valid JSON, make sure the content-type is application/json',
- ),
- );
- return;
- }
-
- Container.get(CredentialsOverwrites).setData(body);
- presetCredentialsLoaded = true;
- ResponseHelper.sendSuccessResponse(res, { success: true }, true, 200);
- } else {
- ResponseHelper.sendErrorResponse(res, new Error('Preset credentials can be set once'));
- }
- },
- );
- }
-
- server.on('error', (error: Error & { code: string }) => {
- if (error.code === 'EADDRINUSE') {
- this.logger.error(
- `n8n's port ${port} is already in use. Do you have the n8n main process running on that port?`,
- );
- process.exit(1);
- }
- });
-
- await new Promise((resolve) => server.listen(port, () => resolve()));
- await this.externalHooks?.run('worker.ready');
- this.logger.info(`\nn8n worker health check via, port ${port}`);
- }
-
async run() {
this.logger.info('\nn8n worker is now ready');
this.logger.info(` * Version: ${N8N_VERSION}`);
this.logger.info(` * Concurrency: ${this.concurrency}`);
this.logger.info('');
- if (this.globalConfig.queue.health.active) {
- await this.setupHealthMonitor();
+ if (
+ this.globalConfig.queue.health.active ||
+ this.globalConfig.credentials.overwrite.endpoint !== ''
+ ) {
+ await Container.get(WorkerServer).init();
}
if (!inTest && process.stdout.isTTY) {
diff --git a/packages/cli/src/errors/credentials-overwrites-already-set.error.ts b/packages/cli/src/errors/credentials-overwrites-already-set.error.ts
new file mode 100644
index 0000000000..4c7534c4c1
--- /dev/null
+++ b/packages/cli/src/errors/credentials-overwrites-already-set.error.ts
@@ -0,0 +1,7 @@
+import { ApplicationError } from 'n8n-workflow';
+
+export class CredentialsOverwritesAlreadySetError extends ApplicationError {
+ constructor() {
+ super('Credentials overwrites may not be set more than once.');
+ }
+}
diff --git a/packages/cli/src/errors/non-json-body.error.ts b/packages/cli/src/errors/non-json-body.error.ts
new file mode 100644
index 0000000000..cd2086dbf5
--- /dev/null
+++ b/packages/cli/src/errors/non-json-body.error.ts
@@ -0,0 +1,7 @@
+import { ApplicationError } from 'n8n-workflow';
+
+export class NonJsonBodyError extends ApplicationError {
+ constructor() {
+ super('Body must be valid JSON. Please make sure `content-type` is `application/json`.');
+ }
+}
diff --git a/packages/cli/src/errors/port-taken.error.ts b/packages/cli/src/errors/port-taken.error.ts
new file mode 100644
index 0000000000..30c63a679f
--- /dev/null
+++ b/packages/cli/src/errors/port-taken.error.ts
@@ -0,0 +1,9 @@
+import { ApplicationError } from 'n8n-workflow';
+
+export class PortTakenError extends ApplicationError {
+ constructor(port: number) {
+ super(
+ `Port ${port} is already in use. Do you already have the n8n main process running on that port?`,
+ );
+ }
+}
diff --git a/packages/cli/src/scaling/__tests__/worker-server.test.ts b/packages/cli/src/scaling/__tests__/worker-server.test.ts
new file mode 100644
index 0000000000..7628420975
--- /dev/null
+++ b/packages/cli/src/scaling/__tests__/worker-server.test.ts
@@ -0,0 +1,127 @@
+import type { GlobalConfig } from '@n8n/config';
+import type express from 'express';
+import { mock } from 'jest-mock-extended';
+import { AssertionError } from 'node:assert';
+import * as http from 'node:http';
+
+import config from '@/config';
+import { PortTakenError } from '@/errors/port-taken.error';
+import type { ExternalHooks } from '@/external-hooks';
+import { bodyParser, rawBodyReader } from '@/middlewares';
+
+import { WorkerServer } from '../worker-server';
+
+const app = mock();
+
+jest.mock('node:http');
+jest.mock('express', () => ({ __esModule: true, default: () => app }));
+
+const addressInUseError = () => {
+ const error: NodeJS.ErrnoException = new Error('Port already in use');
+ error.code = 'EADDRINUSE';
+
+ return error;
+};
+
+describe('WorkerServer', () => {
+ let globalConfig: GlobalConfig;
+
+ const externalHooks = mock();
+
+ beforeEach(() => {
+ config.set('generic.instanceType', 'worker');
+ globalConfig = mock({
+ queue: {
+ health: { active: true, port: 5678 },
+ },
+ credentials: {
+ overwrite: { endpoint: '' },
+ },
+ });
+ jest.restoreAllMocks();
+ });
+
+ describe('constructor', () => {
+ it('should throw if non-worker instance type', () => {
+ config.set('generic.instanceType', 'webhook');
+
+ expect(
+ () => new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks),
+ ).toThrowError(AssertionError);
+ });
+
+ it('should throw if port taken', async () => {
+ const server = mock();
+
+ jest.spyOn(http, 'createServer').mockReturnValue(server);
+
+ server.on.mockImplementation((event: string, callback: (arg?: unknown) => void) => {
+ if (event === 'error') callback(addressInUseError());
+ return server;
+ });
+
+ expect(
+ () => new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks),
+ ).toThrowError(PortTakenError);
+ });
+
+ it('should set up `/healthz` if health check is enabled', async () => {
+ jest.spyOn(http, 'createServer').mockReturnValue(mock());
+
+ new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks);
+
+ expect(app.get).toHaveBeenCalledWith('/healthz', expect.any(Function));
+ });
+
+ it('should not set up `/healthz` if health check is disabled', async () => {
+ globalConfig.queue.health.active = false;
+
+ jest.spyOn(http, 'createServer').mockReturnValue(mock());
+
+ new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks);
+
+ expect(app.get).not.toHaveBeenCalled();
+ });
+
+ it('should set up `/:endpoint` if overwrites endpoint is set', async () => {
+ jest.spyOn(http, 'createServer').mockReturnValue(mock());
+
+ const CREDENTIALS_OVERWRITE_ENDPOINT = 'credentials/overwrites';
+ globalConfig.credentials.overwrite.endpoint = CREDENTIALS_OVERWRITE_ENDPOINT;
+
+ new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks);
+
+ expect(app.post).toHaveBeenCalledWith(
+ `/${CREDENTIALS_OVERWRITE_ENDPOINT}`,
+ rawBodyReader,
+ bodyParser,
+ expect.any(Function),
+ );
+ });
+
+ it('should not set up `/:endpoint` if overwrites endpoint is not set', async () => {
+ jest.spyOn(http, 'createServer').mockReturnValue(mock());
+
+ new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks);
+
+ expect(app.post).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('init', () => {
+ it('should call `worker.ready` external hook', async () => {
+ const server = mock();
+ jest.spyOn(http, 'createServer').mockReturnValue(server);
+
+ server.listen.mockImplementation((_port, callback: () => void) => {
+ callback();
+ return server;
+ });
+
+ const workerServer = new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks);
+ await workerServer.init();
+
+ expect(externalHooks.run).toHaveBeenCalledWith('worker.ready');
+ });
+ });
+});
diff --git a/packages/cli/src/scaling/worker-server.ts b/packages/cli/src/scaling/worker-server.ts
new file mode 100644
index 0000000000..2727fa4733
--- /dev/null
+++ b/packages/cli/src/scaling/worker-server.ts
@@ -0,0 +1,129 @@
+import { GlobalConfig } from '@n8n/config';
+import express from 'express';
+import { ensureError } from 'n8n-workflow';
+import { strict as assert } from 'node:assert';
+import http from 'node:http';
+import type { Server } from 'node:http';
+import { Service } from 'typedi';
+
+import config from '@/config';
+import { CredentialsOverwrites } from '@/credentials-overwrites';
+import * as Db from '@/db';
+import { CredentialsOverwritesAlreadySetError } from '@/errors/credentials-overwrites-already-set.error';
+import { NonJsonBodyError } from '@/errors/non-json-body.error';
+import { PortTakenError } from '@/errors/port-taken.error';
+import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error';
+import { ExternalHooks } from '@/external-hooks';
+import type { ICredentialsOverwrite } from '@/interfaces';
+import { Logger } from '@/logger';
+import { rawBodyReader, bodyParser } from '@/middlewares';
+import * as ResponseHelper from '@/response-helper';
+import { ScalingService } from '@/scaling/scaling.service';
+
+/**
+ * Responsible for handling HTTP requests sent to a worker.
+ */
+@Service()
+export class WorkerServer {
+ private readonly port: number;
+
+ private readonly server: Server;
+
+ /**
+ * @doc https://docs.n8n.io/embed/configuration/#credential-overwrites
+ */
+ private overwritesLoaded = false;
+
+ constructor(
+ private readonly globalConfig: GlobalConfig,
+ private readonly logger: Logger,
+ private readonly scalingService: ScalingService,
+ private readonly credentialsOverwrites: CredentialsOverwrites,
+ private readonly externalHooks: ExternalHooks,
+ ) {
+ assert(config.getEnv('generic.instanceType') === 'worker');
+
+ const app = express();
+
+ app.disable('x-powered-by');
+
+ this.server = http.createServer(app);
+
+ this.port = this.globalConfig.queue.health.port;
+
+ const overwritesEndpoint = this.globalConfig.credentials.overwrite.endpoint;
+
+ this.server.on('error', (error: NodeJS.ErrnoException) => {
+ if (error.code === 'EADDRINUSE') throw new PortTakenError(this.port);
+ });
+
+ if (this.globalConfig.queue.health.active) {
+ app.get('/healthz', async (req, res) => await this.healthcheck(req, res));
+ }
+
+ if (overwritesEndpoint !== '') {
+ app.post(`/${overwritesEndpoint}`, rawBodyReader, bodyParser, (req, res) =>
+ this.handleOverwrites(req, res),
+ );
+ }
+ }
+
+ async init() {
+ await new Promise((resolve) => this.server.listen(this.port, resolve));
+
+ await this.externalHooks.run('worker.ready');
+
+ this.logger.info(`\nn8n worker server listening on port ${this.port}`);
+ }
+
+ private async healthcheck(_req: express.Request, res: express.Response) {
+ this.logger.debug('[WorkerServer] Health check started');
+
+ try {
+ await Db.getConnection().query('SELECT 1');
+ } catch (value) {
+ this.logger.error('[WorkerServer] No database connection', ensureError(value));
+
+ return ResponseHelper.sendErrorResponse(
+ res,
+ new ServiceUnavailableError('No database connection'),
+ );
+ }
+
+ try {
+ await this.scalingService.pingQueue();
+ } catch (value) {
+ this.logger.error('[WorkerServer] No Redis connection', ensureError(value));
+
+ return ResponseHelper.sendErrorResponse(
+ res,
+ new ServiceUnavailableError('No Redis connection'),
+ );
+ }
+
+ this.logger.debug('[WorkerServer] Health check succeeded');
+
+ ResponseHelper.sendSuccessResponse(res, { status: 'ok' }, true, 200);
+ }
+
+ private handleOverwrites(
+ req: express.Request<{}, {}, ICredentialsOverwrite>,
+ res: express.Response,
+ ) {
+ if (this.overwritesLoaded) {
+ ResponseHelper.sendErrorResponse(res, new CredentialsOverwritesAlreadySetError());
+ return;
+ }
+
+ if (req.contentType !== 'application/json') {
+ ResponseHelper.sendErrorResponse(res, new NonJsonBodyError());
+ return;
+ }
+
+ this.credentialsOverwrites.setData(req.body);
+
+ this.overwritesLoaded = true;
+
+ ResponseHelper.sendSuccessResponse(res, { success: true }, true, 200);
+ }
+}
From 7f4ef315075f76814898ec3d29bf51e8b53f693d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?=
Date: Tue, 17 Sep 2024 10:38:47 +0200
Subject: [PATCH 15/42] refactor(core): Have `WorkerServer` use
`InstanceSettings` (#10840)
---
.../scaling/__tests__/worker-server.test.ts | 36 +++++++++++++------
packages/cli/src/scaling/worker-server.ts | 5 +--
2 files changed, 28 insertions(+), 13 deletions(-)
diff --git a/packages/cli/src/scaling/__tests__/worker-server.test.ts b/packages/cli/src/scaling/__tests__/worker-server.test.ts
index 7628420975..753a1e3e18 100644
--- a/packages/cli/src/scaling/__tests__/worker-server.test.ts
+++ b/packages/cli/src/scaling/__tests__/worker-server.test.ts
@@ -1,10 +1,10 @@
import type { GlobalConfig } from '@n8n/config';
import type express from 'express';
import { mock } from 'jest-mock-extended';
+import type { InstanceSettings } from 'n8n-core';
import { AssertionError } from 'node:assert';
import * as http from 'node:http';
-import config from '@/config';
import { PortTakenError } from '@/errors/port-taken.error';
import type { ExternalHooks } from '@/external-hooks';
import { bodyParser, rawBodyReader } from '@/middlewares';
@@ -27,9 +27,9 @@ describe('WorkerServer', () => {
let globalConfig: GlobalConfig;
const externalHooks = mock();
+ const instanceSettings = mock({ instanceType: 'worker' });
beforeEach(() => {
- config.set('generic.instanceType', 'worker');
globalConfig = mock({
queue: {
health: { active: true, port: 5678 },
@@ -43,10 +43,16 @@ describe('WorkerServer', () => {
describe('constructor', () => {
it('should throw if non-worker instance type', () => {
- config.set('generic.instanceType', 'webhook');
-
expect(
- () => new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks),
+ () =>
+ new WorkerServer(
+ globalConfig,
+ mock(),
+ mock(),
+ mock(),
+ externalHooks,
+ mock({ instanceType: 'webhook' }),
+ ),
).toThrowError(AssertionError);
});
@@ -61,14 +67,15 @@ describe('WorkerServer', () => {
});
expect(
- () => new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks),
+ () =>
+ new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks, instanceSettings),
).toThrowError(PortTakenError);
});
it('should set up `/healthz` if health check is enabled', async () => {
jest.spyOn(http, 'createServer').mockReturnValue(mock());
- new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks);
+ new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks, instanceSettings);
expect(app.get).toHaveBeenCalledWith('/healthz', expect.any(Function));
});
@@ -78,7 +85,7 @@ describe('WorkerServer', () => {
jest.spyOn(http, 'createServer').mockReturnValue(mock());
- new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks);
+ new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks, instanceSettings);
expect(app.get).not.toHaveBeenCalled();
});
@@ -89,7 +96,7 @@ describe('WorkerServer', () => {
const CREDENTIALS_OVERWRITE_ENDPOINT = 'credentials/overwrites';
globalConfig.credentials.overwrite.endpoint = CREDENTIALS_OVERWRITE_ENDPOINT;
- new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks);
+ new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks, instanceSettings);
expect(app.post).toHaveBeenCalledWith(
`/${CREDENTIALS_OVERWRITE_ENDPOINT}`,
@@ -102,7 +109,7 @@ describe('WorkerServer', () => {
it('should not set up `/:endpoint` if overwrites endpoint is not set', async () => {
jest.spyOn(http, 'createServer').mockReturnValue(mock());
- new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks);
+ new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks, instanceSettings);
expect(app.post).not.toHaveBeenCalled();
});
@@ -118,7 +125,14 @@ describe('WorkerServer', () => {
return server;
});
- const workerServer = new WorkerServer(globalConfig, mock(), mock(), mock(), externalHooks);
+ const workerServer = new WorkerServer(
+ globalConfig,
+ mock(),
+ mock(),
+ mock(),
+ externalHooks,
+ instanceSettings,
+ );
await workerServer.init();
expect(externalHooks.run).toHaveBeenCalledWith('worker.ready');
diff --git a/packages/cli/src/scaling/worker-server.ts b/packages/cli/src/scaling/worker-server.ts
index 2727fa4733..cc8d463951 100644
--- a/packages/cli/src/scaling/worker-server.ts
+++ b/packages/cli/src/scaling/worker-server.ts
@@ -1,12 +1,12 @@
import { GlobalConfig } from '@n8n/config';
import express from 'express';
+import { InstanceSettings } from 'n8n-core';
import { ensureError } from 'n8n-workflow';
import { strict as assert } from 'node:assert';
import http from 'node:http';
import type { Server } from 'node:http';
import { Service } from 'typedi';
-import config from '@/config';
import { CredentialsOverwrites } from '@/credentials-overwrites';
import * as Db from '@/db';
import { CredentialsOverwritesAlreadySetError } from '@/errors/credentials-overwrites-already-set.error';
@@ -40,8 +40,9 @@ export class WorkerServer {
private readonly scalingService: ScalingService,
private readonly credentialsOverwrites: CredentialsOverwrites,
private readonly externalHooks: ExternalHooks,
+ private readonly instanceSettings: InstanceSettings,
) {
- assert(config.getEnv('generic.instanceType') === 'worker');
+ assert(this.instanceSettings.instanceType === 'worker');
const app = express();
From e0c0ddee59e889f50dd5033d0a933bad60fb7e3a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ra=C3=BAl=20G=C3=B3mez=20Morales?=
Date: Tue, 17 Sep 2024 11:22:49 +0200
Subject: [PATCH 16/42] fix(editor): Address edge toolbar rendering glitches
(#10839)
---
.../src/components/canvas/Canvas.vue | 22 +----
.../elements/edges/CanvasArrowHeadMarker.vue | 29 ++++++
.../canvas/elements/edges/CanvasEdge.spec.ts | 11 ++-
.../canvas/elements/edges/CanvasEdge.vue | 94 ++++++++++---------
.../elements/edges/CanvasEdgeToolbar.vue | 4 +
5 files changed, 94 insertions(+), 66 deletions(-)
create mode 100644 packages/editor-ui/src/components/canvas/elements/edges/CanvasArrowHeadMarker.vue
diff --git a/packages/editor-ui/src/components/canvas/Canvas.vue b/packages/editor-ui/src/components/canvas/Canvas.vue
index e5a6a74add..e8e58e4cd1 100644
--- a/packages/editor-ui/src/components/canvas/Canvas.vue
+++ b/packages/editor-ui/src/components/canvas/Canvas.vue
@@ -7,7 +7,6 @@ import type {
ConnectStartEvent,
} from '@/types';
import type {
- EdgeMouseEvent,
Connection,
XYPosition,
ViewportTransform,
@@ -31,6 +30,7 @@ import { isPresent } from '@/utils/typesUtils';
import { GRID_SIZE } from '@/utils/nodeViewUtils';
import { CanvasKey } from '@/constants';
import { onKeyDown, onKeyUp } from '@vueuse/core';
+import CanvasArrowHeadMarker from './elements/edges/CanvasArrowHeadMarker.vue';
const $style = useCssModule();
@@ -261,19 +261,7 @@ function onClickConnectionAdd(connection: Connection) {
emit('click:connection:add', connection);
}
-/**
- * Connection hover
- */
-
-const hoveredEdges = ref>({});
-
-function onMouseEnterEdge(event: EdgeMouseEvent) {
- hoveredEdges.value[event.edge.id] = true;
-}
-
-function onMouseLeaveEdge(event: EdgeMouseEvent) {
- hoveredEdges.value[event.edge.id] = false;
-}
+const arrowHeadMarkerId = ref('custom-arrow-head');
/**
* Executions
@@ -511,8 +499,6 @@ provide(CanvasKey, {
:selection-key-code="selectionKeyCode"
:pan-activation-key-code="panningKeyCode"
data-test-id="canvas"
- @edge-mouse-enter="onMouseEnterEdge"
- @edge-mouse-leave="onMouseLeaveEdge"
@connect-start="onConnectStart"
@connect="onConnect"
@connect-end="onConnectEnd"
@@ -543,8 +529,8 @@ provide(CanvasKey, {
@@ -554,6 +540,8 @@ provide(CanvasKey, {
+
+
diff --git a/packages/editor-ui/src/components/canvas/elements/edges/CanvasArrowHeadMarker.vue b/packages/editor-ui/src/components/canvas/elements/edges/CanvasArrowHeadMarker.vue
new file mode 100644
index 0000000000..26810eb26a
--- /dev/null
+++ b/packages/editor-ui/src/components/canvas/elements/edges/CanvasArrowHeadMarker.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdge.spec.ts b/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdge.spec.ts
index 1e251abc87..93ca4f13fd 100644
--- a/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdge.spec.ts
+++ b/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdge.spec.ts
@@ -1,10 +1,10 @@
-import { fireEvent } from '@testing-library/vue';
import CanvasEdge, { type CanvasEdgeProps } from './CanvasEdge.vue';
import { createComponentRenderer } from '@/__tests__/render';
import { createTestingPinia } from '@pinia/testing';
import { setActivePinia } from 'pinia';
import { Position } from '@vue-flow/core';
import { NodeConnectionType } from 'n8n-workflow';
+import userEvent from '@testing-library/user-event';
const DEFAULT_PROPS = {
sourceX: 0,
@@ -31,18 +31,21 @@ beforeEach(() => {
describe('CanvasEdge', () => {
it('should emit delete event when toolbar delete is clicked', async () => {
const { emitted, getByTestId } = renderComponent();
+ await userEvent.hover(getByTestId('edge-label-wrapper'));
const deleteButton = getByTestId('delete-connection-button');
- await fireEvent.click(deleteButton);
+ await userEvent.click(deleteButton);
expect(emitted()).toHaveProperty('delete');
});
it('should emit add event when toolbar add is clicked', async () => {
const { emitted, getByTestId } = renderComponent();
+ await userEvent.hover(getByTestId('edge-label-wrapper'));
+
const addButton = getByTestId('add-connection-button');
- await fireEvent.click(addButton);
+ await userEvent.click(addButton);
expect(emitted()).toHaveProperty('add');
});
@@ -54,6 +57,8 @@ describe('CanvasEdge', () => {
},
});
+ await userEvent.hover(getByTestId('edge-label-wrapper'));
+
expect(() => getByTestId('add-connection-button')).toThrow();
expect(() => getByTestId('delete-connection-button')).toThrow();
});
diff --git a/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdge.vue b/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdge.vue
index 78af35b1cd..cb384489a9 100644
--- a/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdge.vue
+++ b/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdge.vue
@@ -3,9 +3,9 @@
import type { CanvasConnectionData } from '@/types';
import { isValidNodeConnectionType } from '@/utils/typeGuards';
import type { Connection, EdgeProps } from '@vue-flow/core';
-import { BaseEdge, EdgeLabelRenderer } from '@vue-flow/core';
+import { useVueFlow, BaseEdge, EdgeLabelRenderer } from '@vue-flow/core';
import { NodeConnectionType } from 'n8n-workflow';
-import { computed, useCssModule } from 'vue';
+import { computed, useCssModule, ref } from 'vue';
import CanvasEdgeToolbar from './CanvasEdgeToolbar.vue';
import { getCustomPath } from './utils/edgePath';
@@ -21,6 +21,19 @@ export type CanvasEdgeProps = EdgeProps & {
const props = defineProps();
+const { onEdgeMouseEnter, onEdgeMouseLeave } = useVueFlow();
+
+const isHovered = ref(false);
+
+onEdgeMouseEnter(({ edge }) => {
+ if (edge.id !== props.id) return;
+ isHovered.value = true;
+});
+onEdgeMouseLeave(({ edge }) => {
+ if (edge.id !== props.id) return;
+ isHovered.value = false;
+});
+
const $style = useCssModule();
const connectionType = computed(() =>
@@ -29,7 +42,7 @@ const connectionType = computed(() =>
: NodeConnectionType.Main,
);
-const isFocused = computed(() => props.selected || props.hovered);
+const renderToolbar = computed(() => (props.selected || isHovered.value) && !props.readOnly);
const status = computed(() => props.data.status);
const statusColor = computed(() => {
@@ -49,22 +62,10 @@ const statusColor = computed(() => {
const edgeStyle = computed(() => ({
...props.style,
strokeWidth: 2,
- stroke: statusColor.value,
+ stroke: isHovered.value ? 'var(--color-primary)' : statusColor.value,
}));
-const edgeLabel = computed(() => {
- if (isFocused.value && !props.readOnly) {
- return '';
- }
-
- return props.label;
-});
-
-const edgeLabelStyle = computed(() => ({
- fill: statusColor.value,
- transform: 'translateY(calc(var(--spacing-xs) * -1))',
- fontSize: 'var(--font-size-xs)',
-}));
+const edgeLabelStyle = computed(() => ({ color: statusColor.value }));
const edgeToolbarStyle = computed(() => {
const [, labelX, labelY] = path.value;
@@ -73,13 +74,6 @@ const edgeToolbarStyle = computed(() => {
};
});
-const edgeToolbarClasses = computed(() => ({
- [$style.edgeToolbar]: true,
- [$style.edgeToolbarVisible]: isFocused.value,
- nodrag: true,
- nopan: true,
-}));
-
const path = computed(() => getCustomPath(props));
const connection = computed(() => ({
@@ -105,39 +99,47 @@ function onDelete() {
:style="edgeStyle"
:path="path[0]"
:marker-end="markerEnd"
- :label="edgeLabel"
- :label-x="path[1]"
- :label-y="path[2]"
- :label-style="edgeLabelStyle"
- :label-show-bg="false"
+ :interaction-width="40"
/>
-
-
+
+ :class="$style.edgeLabelWrapper"
+ @mouseenter="isHovered = true"
+ @mouseleave="isHovered = false"
+ >
+
+ {{ label }}
+
diff --git a/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdgeToolbar.vue b/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdgeToolbar.vue
index fab32bf008..cf487042d5 100644
--- a/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdgeToolbar.vue
+++ b/packages/editor-ui/src/components/canvas/elements/edges/CanvasEdgeToolbar.vue
@@ -70,4 +70,8 @@ function onDelete() {
--button-background-color: var(--color-background-base);
--button-hover-background-color: var(--color-background-light);
}
+
+.canvas-edge-toolbar-button {
+ border-width: 2px;
+}
From 5a2c7e00a0ca1a151a7fec56da5f99b086c25b1f Mon Sep 17 00:00:00 2001
From: CodeShakingSheep <19874562+CodeShakingSheep@users.noreply.github.com>
Date: Tue, 17 Sep 2024 04:42:05 -0500
Subject: [PATCH 17/42] feat(Invoice Ninja Node): Add actions for bank
transactions (#10389)
---
.../BankTransactionDescription.ts | 213 ++++++++++++++++++
.../InvoiceNinja/BankTransactionInterface.ts | 9 +
.../nodes/InvoiceNinja/InvoiceNinja.node.ts | 179 ++++++++++++++-
3 files changed, 398 insertions(+), 3 deletions(-)
create mode 100644 packages/nodes-base/nodes/InvoiceNinja/BankTransactionDescription.ts
create mode 100644 packages/nodes-base/nodes/InvoiceNinja/BankTransactionInterface.ts
diff --git a/packages/nodes-base/nodes/InvoiceNinja/BankTransactionDescription.ts b/packages/nodes-base/nodes/InvoiceNinja/BankTransactionDescription.ts
new file mode 100644
index 0000000000..b72d88db48
--- /dev/null
+++ b/packages/nodes-base/nodes/InvoiceNinja/BankTransactionDescription.ts
@@ -0,0 +1,213 @@
+import type { INodeProperties } from 'n8n-workflow';
+
+export const bankTransactionOperations: INodeProperties[] = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ noDataExpression: true,
+ displayOptions: {
+ show: {
+ resource: ['bank_transaction'],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a new bank transaction',
+ action: 'Create a bank transaction',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete a bank transaction',
+ action: 'Delete a bank transaction',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Get data of a bank transaction',
+ action: 'Get a bank transaction',
+ },
+ {
+ name: 'Get Many',
+ value: 'getAll',
+ description: 'Get data of many bank transactions',
+ action: 'Get many bank transactions',
+ },
+ {
+ name: 'Match Payment',
+ value: 'matchPayment',
+ description: 'Match payment to a bank transaction',
+ action: 'Match payment to a bank transaction',
+ },
+ ],
+ default: 'create',
+ },
+];
+
+export const bankTransactionFields: INodeProperties[] = [
+ /* -------------------------------------------------------------------------- */
+ /* bankTransaction:create */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ operation: ['create'],
+ resource: ['bank_transaction'],
+ },
+ },
+ options: [
+ {
+ displayName: 'Amount',
+ name: 'amount',
+ type: 'number',
+ default: 0,
+ },
+ {
+ displayName: 'Bank Integration Name or ID',
+ name: 'bankIntegrationId',
+ type: 'options',
+ description:
+ 'Choose from the list, or specify an ID using an
expression ',
+ typeOptions: {
+ loadOptionsMethod: 'getBankIntegrations',
+ },
+ default: '',
+ },
+ {
+ displayName: 'Base Type',
+ name: 'baseType',
+ type: 'options',
+ options: [
+ {
+ name: 'Deposit',
+ value: 'CREDIT',
+ },
+ {
+ name: 'Withdrawal',
+ value: 'DEBIT',
+ },
+ ],
+ default: '',
+ },
+ {
+ displayName: 'Date',
+ name: 'date',
+ type: 'dateTime',
+ default: '',
+ },
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+ /* -------------------------------------------------------------------------- */
+ /* bankTransaction:delete */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Bank Transaction ID',
+ name: 'bankTransactionId',
+ type: 'string',
+ default: '',
+ required: true,
+ displayOptions: {
+ show: {
+ resource: ['bank_transaction'],
+ operation: ['delete'],
+ },
+ },
+ },
+ /* -------------------------------------------------------------------------- */
+ /* bankTransaction:get */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Bank Transaction ID',
+ name: 'bankTransactionId',
+ type: 'string',
+ default: '',
+ required: true,
+ displayOptions: {
+ show: {
+ resource: ['bank_transaction'],
+ operation: ['get'],
+ },
+ },
+ },
+ /* -------------------------------------------------------------------------- */
+ /* bankTransaction:getAll */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ resource: ['bank_transaction'],
+ operation: ['getAll'],
+ },
+ },
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ displayOptions: {
+ show: {
+ resource: ['bank_transaction'],
+ operation: ['getAll'],
+ returnAll: [false],
+ },
+ },
+ typeOptions: {
+ minValue: 1,
+ maxValue: 60,
+ },
+ default: 50,
+ description: 'Max number of results to return',
+ },
+ /* -------------------------------------------------------------------------- */
+ /* bankTransaction:matchPayment */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Bank Transaction ID',
+ name: 'bankTransactionId',
+ type: 'string',
+ default: '',
+ required: true,
+ displayOptions: {
+ show: {
+ resource: ['bank_transaction'],
+ operation: ['matchPayment'],
+ },
+ },
+ },
+ {
+ displayName: 'Payment Name or ID',
+ name: 'paymentId',
+ type: 'options',
+ description:
+ 'Choose from the list, or specify an ID using an
expression ',
+ typeOptions: {
+ loadOptionsMethod: 'getPayments',
+ },
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['bank_transaction'],
+ operation: ['matchPayment'],
+ },
+ },
+ },
+];
diff --git a/packages/nodes-base/nodes/InvoiceNinja/BankTransactionInterface.ts b/packages/nodes-base/nodes/InvoiceNinja/BankTransactionInterface.ts
new file mode 100644
index 0000000000..735f339ab8
--- /dev/null
+++ b/packages/nodes-base/nodes/InvoiceNinja/BankTransactionInterface.ts
@@ -0,0 +1,9 @@
+export interface IBankTransaction {
+ amount?: number;
+ bank_integration_id?: number;
+ base_type?: string;
+ date?: string;
+ description?: string;
+ id?: string;
+ paymentId?: string;
+}
diff --git a/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts
index a23a7ff8c5..fd43eb8e86 100644
--- a/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts
+++ b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts
@@ -36,6 +36,10 @@ import { quoteFields, quoteOperations } from './QuoteDescription';
import type { IQuote } from './QuoteInterface';
import { isoCountryCodes } from '@utils/ISOCountryCodes';
+import { bankTransactionFields, bankTransactionOperations } from './BankTransactionDescription';
+
+import type { IBankTransaction } from './BankTransactionInterface';
+
export class InvoiceNinja implements INodeType {
description: INodeTypeDescription = {
displayName: 'Invoice Ninja',
@@ -107,6 +111,15 @@ export class InvoiceNinja implements INodeType {
type: 'options',
noDataExpression: true,
options: [
+ {
+ name: 'Bank Transaction',
+ value: 'bank_transaction',
+ displayOptions: {
+ show: {
+ apiVersion: ['v5'],
+ },
+ },
+ },
{
name: 'Client',
value: 'client',
@@ -146,6 +159,8 @@ export class InvoiceNinja implements INodeType {
...expenseFields,
...quoteOperations,
...quoteFields,
+ ...bankTransactionOperations,
+ ...bankTransactionFields,
],
};
@@ -255,6 +270,58 @@ export class InvoiceNinja implements INodeType {
}
return returnData;
},
+ // Get all the available bank integrations to display them to user so that they can
+ // select them easily
+ async getBankIntegrations(this: ILoadOptionsFunctions): Promise
{
+ const returnData: INodePropertyOptions[] = [];
+ let banks = await invoiceNinjaApiRequestAllItems.call(
+ this,
+ 'data',
+ 'GET',
+ '/bank_integrations',
+ );
+ banks = banks.filter((e) => !e.is_deleted);
+ for (const bank of banks) {
+ const providerName = bank.provider_name as string;
+ const accountName = bank.bank_account_name as string;
+ const bankId = bank.id as string;
+ returnData.push({
+ name:
+ providerName != accountName
+ ? `${providerName} - ${accountName}`
+ : accountName || providerName,
+ value: bankId,
+ });
+ }
+ return returnData;
+ },
+ // Get all the available users to display them to user so that they can
+ // select them easily
+ async getPayments(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const qs: IDataObject = {};
+ // Only select payments that can be matched to transactions
+ qs.match_transactions = true;
+ const payments = await invoiceNinjaApiRequestAllItems.call(
+ this,
+ 'data',
+ 'GET',
+ '/payments',
+ {},
+ qs,
+ );
+ for (const payment of payments) {
+ const paymentName = [payment.number, payment.date, payment.amount]
+ .filter((e) => e)
+ .join(' - ');
+ const paymentId = payment.id as string;
+ returnData.push({
+ name: paymentName,
+ value: paymentId,
+ });
+ }
+ return returnData;
+ },
},
};
@@ -858,6 +925,106 @@ export class InvoiceNinja implements INodeType {
responseData = responseData.data;
}
}
+ if (resource === 'bank_transaction') {
+ const resourceEndpoint = '/bank_transactions';
+ if (operation === 'create') {
+ const additionalFields = this.getNodeParameter('additionalFields', i);
+ const body: IBankTransaction = {};
+ if (additionalFields.amount) {
+ body.amount = additionalFields.amount as number;
+ }
+ if (additionalFields.baseType) {
+ body.base_type = additionalFields.baseType as string;
+ }
+ if (additionalFields.bankIntegrationId) {
+ body.bank_integration_id = additionalFields.bankIntegrationId as number;
+ }
+ if (additionalFields.client) {
+ body.date = additionalFields.date as string;
+ }
+ if (additionalFields.email) {
+ body.description = additionalFields.description as string;
+ }
+ responseData = await invoiceNinjaApiRequest.call(
+ this,
+ 'POST',
+ resourceEndpoint,
+ body as IDataObject,
+ );
+ responseData = responseData.data;
+ }
+ if (operation === 'get') {
+ const bankTransactionId = this.getNodeParameter('bankTransactionId', i) as string;
+ const options = this.getNodeParameter('options', i);
+ if (options.include) {
+ qs.include = options.include as string;
+ }
+ responseData = await invoiceNinjaApiRequest.call(
+ this,
+ 'GET',
+ `${resourceEndpoint}/${bankTransactionId}`,
+ {},
+ qs,
+ );
+ responseData = responseData.data;
+ }
+ if (operation === 'getAll') {
+ const returnAll = this.getNodeParameter('returnAll', 0);
+ const options = this.getNodeParameter('options', i);
+ if (options.include) {
+ qs.include = options.include as string;
+ }
+ if (options.invoiceNumber) {
+ qs.invoice_number = options.invoiceNumber as string;
+ }
+ if (returnAll) {
+ responseData = await invoiceNinjaApiRequestAllItems.call(
+ this,
+ 'data',
+ 'GET',
+ resourceEndpoint,
+ {},
+ qs,
+ );
+ } else {
+ qs.per_page = this.getNodeParameter('limit', 0);
+ responseData = await invoiceNinjaApiRequest.call(
+ this,
+ 'GET',
+ resourceEndpoint,
+ {},
+ qs,
+ );
+ responseData = responseData.data;
+ }
+ }
+ if (operation === 'delete') {
+ const bankTransactionId = this.getNodeParameter('bankTransactionId', i) as string;
+ responseData = await invoiceNinjaApiRequest.call(
+ this,
+ 'DELETE',
+ `${resourceEndpoint}/${bankTransactionId}`,
+ );
+ responseData = responseData.data;
+ }
+ if (operation === 'matchPayment') {
+ const bankTransactionId = this.getNodeParameter('bankTransactionId', i) as string;
+ const paymentId = this.getNodeParameter('paymentId', i) as string;
+ const body: IBankTransaction = {};
+ if (bankTransactionId) {
+ body.id = bankTransactionId as string;
+ }
+ if (paymentId) {
+ body.paymentId = paymentId as string;
+ }
+ responseData = await invoiceNinjaApiRequest.call(
+ this,
+ 'POST',
+ `${resourceEndpoint}/match`,
+ body as IDataObject,
+ );
+ }
+ }
if (resource === 'quote') {
const resourceEndpoint = apiVersion === 'v4' ? '/invoices' : '/quotes';
if (operation === 'create') {
@@ -983,7 +1150,7 @@ export class InvoiceNinja implements INodeType {
responseData = await invoiceNinjaApiRequest.call(
this,
'GET',
- `/quotes/${quoteId}/email`,
+ `${resourceEndpoint}/${quoteId}/email`,
);
}
}
@@ -1016,13 +1183,19 @@ export class InvoiceNinja implements INodeType {
this,
'data',
'GET',
- '/quotes',
+ resourceEndpoint,
{},
qs,
);
} else {
qs.per_page = this.getNodeParameter('limit', 0);
- responseData = await invoiceNinjaApiRequest.call(this, 'GET', '/quotes', {}, qs);
+ responseData = await invoiceNinjaApiRequest.call(
+ this,
+ 'GET',
+ resourceEndpoint,
+ {},
+ qs,
+ );
responseData = responseData.data;
}
}
From acb4194fa1a1d0497dd1f48045f069e1db28c432 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?=
Date: Tue, 17 Sep 2024 12:02:17 +0200
Subject: [PATCH 18/42] fix(core): Prevent shutdown error in regular mode
(#10844)
---
packages/cli/src/commands/worker.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/cli/src/commands/worker.ts b/packages/cli/src/commands/worker.ts
index 89e131bc0d..b1cba17fd4 100644
--- a/packages/cli/src/commands/worker.ts
+++ b/packages/cli/src/commands/worker.ts
@@ -9,7 +9,6 @@ import { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus'
import { LogStreamingEventRelay } from '@/events/log-streaming-event-relay';
import { JobProcessor } from '@/scaling/job-processor';
import type { ScalingService } from '@/scaling/scaling.service';
-import { WorkerServer } from '@/scaling/worker-server';
import { OrchestrationHandlerWorkerService } from '@/services/orchestration/worker/orchestration.handler.worker.service';
import { OrchestrationWorkerService } from '@/services/orchestration/worker/orchestration.worker.service';
import type { RedisServicePubSubSubscriber } from '@/services/redis/redis-service-pub-sub-subscriber';
@@ -167,6 +166,7 @@ export class Worker extends BaseCommand {
this.globalConfig.queue.health.active ||
this.globalConfig.credentials.overwrite.endpoint !== ''
) {
+ const { WorkerServer } = await import('@/scaling/worker-server');
await Container.get(WorkerServer).init();
}
From 9c95db8282c9f3cef5568aa9793ca977d4d8a347 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ra=C3=BAl=20G=C3=B3mez=20Morales?=
Date: Tue, 17 Sep 2024 12:12:39 +0200
Subject: [PATCH 19/42] fix(editor): Minimap Show nodes outside viewport
(#10843)
---
packages/editor-ui/src/components/canvas/Canvas.vue | 1 -
1 file changed, 1 deletion(-)
diff --git a/packages/editor-ui/src/components/canvas/Canvas.vue b/packages/editor-ui/src/components/canvas/Canvas.vue
index e8e58e4cd1..8efb8fcea5 100644
--- a/packages/editor-ui/src/components/canvas/Canvas.vue
+++ b/packages/editor-ui/src/components/canvas/Canvas.vue
@@ -555,7 +555,6 @@ provide(CanvasKey, {
pannable
zoomable
:node-class-name="minimapNodeClassnameFn"
- mask-color="var(--color-background-base)"
:node-border-radius="16"
@mouseenter="onMinimapMouseEnter"
@mouseleave="onMinimapMouseLeave"
From d81e72b6c78cc430ab43b3d5bc5e65b67c480c67 Mon Sep 17 00:00:00 2001
From: Tomi Turtiainen <10324676+tomi@users.noreply.github.com>
Date: Tue, 17 Sep 2024 14:04:36 +0300
Subject: [PATCH 20/42] fix(Invoice Ninja Node): Fix lint error (no-changelog)
(#10848)
---
.../nodes/InvoiceNinja/BankTransactionDescription.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/nodes-base/nodes/InvoiceNinja/BankTransactionDescription.ts b/packages/nodes-base/nodes/InvoiceNinja/BankTransactionDescription.ts
index b72d88db48..abf39ab493 100644
--- a/packages/nodes-base/nodes/InvoiceNinja/BankTransactionDescription.ts
+++ b/packages/nodes-base/nodes/InvoiceNinja/BankTransactionDescription.ts
@@ -75,7 +75,7 @@ export const bankTransactionFields: INodeProperties[] = [
name: 'bankIntegrationId',
type: 'options',
description:
- 'Choose from the list, or specify an ID using an expression ',
+ 'Choose from the list, or specify an ID using an expression ',
typeOptions: {
loadOptionsMethod: 'getBankIntegrations',
},
@@ -198,7 +198,7 @@ export const bankTransactionFields: INodeProperties[] = [
name: 'paymentId',
type: 'options',
description:
- 'Choose from the list, or specify an ID using an expression ',
+ 'Choose from the list, or specify an ID using an expression ',
typeOptions: {
loadOptionsMethod: 'getPayments',
},
From a63a9b53f06d3a00e8e76c0ab9f2571604c01922 Mon Sep 17 00:00:00 2001
From: Csaba Tuncsik
Date: Tue, 17 Sep 2024 13:39:13 +0200
Subject: [PATCH 21/42] fix(editor): Allow custom git repo urls in source
control settings (#10849)
---
.../src/views/{__tests__ => }/SettingsSourceControl.test.ts | 2 ++
packages/editor-ui/src/views/SettingsSourceControl.vue | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
rename packages/editor-ui/src/views/{__tests__ => }/SettingsSourceControl.test.ts (98%)
diff --git a/packages/editor-ui/src/views/__tests__/SettingsSourceControl.test.ts b/packages/editor-ui/src/views/SettingsSourceControl.test.ts
similarity index 98%
rename from packages/editor-ui/src/views/__tests__/SettingsSourceControl.test.ts
rename to packages/editor-ui/src/views/SettingsSourceControl.test.ts
index aa0a100e4e..3cc3072e86 100644
--- a/packages/editor-ui/src/views/__tests__/SettingsSourceControl.test.ts
+++ b/packages/editor-ui/src/views/SettingsSourceControl.test.ts
@@ -170,6 +170,8 @@ describe('SettingsSourceControl', () => {
['git@192.168.1.101:2222:user/repo', true],
['git@ssh.dev.azure.com:v3/User/repo/directory', true],
['ssh://git@mydomain.example:2224/gitolite-admin', true],
+ ['gituser@192.168.1.1:ABC/Repo4.git', true],
+ ['root@192.168.1.1/repo.git', true],
['http://github.com/user/repository', false],
['https://github.com/user/repository', false],
])('%s', async (url: string, isValid: boolean) => {
diff --git a/packages/editor-ui/src/views/SettingsSourceControl.vue b/packages/editor-ui/src/views/SettingsSourceControl.vue
index c8c9263b6f..23505092da 100644
--- a/packages/editor-ui/src/views/SettingsSourceControl.vue
+++ b/packages/editor-ui/src/views/SettingsSourceControl.vue
@@ -131,7 +131,7 @@ const repoUrlValidationRules: Array = [
name: 'MATCH_REGEX',
config: {
regex:
- /^(ssh:\/\/)?git@(?:\[[0-9a-fA-F:]+\]|(?:[a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+)(?::[0-9]+)*:(?:v[0-9]+\/)?[a-zA-Z0-9_.\-\/]+(\.git)?(?:\/[a-zA-Z0-9_.\-\/]+)*$/,
+ /^(?:git@|ssh:\/\/git@|[\w-]+@)(?:[\w.-]+|\[[0-9a-fA-F:]+])(?::\d+)?[:\/][\w\-~]+(?:\/[\w\-~]+)*(?:\.git)?(?:\/.*)?$/,
message: locale.baseText('settings.sourceControl.repoUrlInvalid'),
},
},
From 57836cc17a57c790d2ffb2463abb16a03321eb59 Mon Sep 17 00:00:00 2001
From: Csaba Tuncsik
Date: Tue, 17 Sep 2024 14:00:22 +0200
Subject: [PATCH 22/42] feat(editor): Add truncate directive (#10842)
---
.../design-system/src/directives/index.ts | 1 +
.../src/directives/n8n-truncate.test.ts | 76 +++++++++++++++++++
.../src/directives/n8n-truncate.ts | 26 +++++++
packages/design-system/src/main.ts | 1 +
packages/design-system/src/plugin.ts | 5 ++
.../design-system/src/utils/string.test.ts | 17 +++++
packages/design-system/src/utils/string.ts | 2 +
7 files changed, 128 insertions(+)
create mode 100644 packages/design-system/src/directives/index.ts
create mode 100644 packages/design-system/src/directives/n8n-truncate.test.ts
create mode 100644 packages/design-system/src/directives/n8n-truncate.ts
create mode 100644 packages/design-system/src/utils/string.test.ts
create mode 100644 packages/design-system/src/utils/string.ts
diff --git a/packages/design-system/src/directives/index.ts b/packages/design-system/src/directives/index.ts
new file mode 100644
index 0000000000..eb939f0531
--- /dev/null
+++ b/packages/design-system/src/directives/index.ts
@@ -0,0 +1 @@
+export { n8nTruncate } from './n8n-truncate';
diff --git a/packages/design-system/src/directives/n8n-truncate.test.ts b/packages/design-system/src/directives/n8n-truncate.test.ts
new file mode 100644
index 0000000000..ec8922d2bc
--- /dev/null
+++ b/packages/design-system/src/directives/n8n-truncate.test.ts
@@ -0,0 +1,76 @@
+import { render } from '@testing-library/vue';
+import { n8nTruncate } from './n8n-truncate';
+
+describe('Directive n8n-truncate', () => {
+ it('should truncate text to 30 chars by default', async () => {
+ const { html } = render(
+ {
+ props: {
+ text: {
+ type: String,
+ },
+ },
+ template: '{{text}}
',
+ },
+ {
+ props: {
+ text: 'This is a very long text that should be truncated',
+ },
+ global: {
+ directives: {
+ n8nTruncate,
+ },
+ },
+ },
+ );
+ expect(html()).toBe('This is a very long text that...
');
+ });
+
+ it('should truncate text to 30 chars in case of wrong argument', async () => {
+ const { html } = render(
+ {
+ props: {
+ text: {
+ type: String,
+ },
+ },
+ template: '{{text}}
',
+ },
+ {
+ props: {
+ text: 'This is a very long text that should be truncated',
+ },
+ global: {
+ directives: {
+ n8nTruncate,
+ },
+ },
+ },
+ );
+ expect(html()).toBe('This is a very long text that...
');
+ });
+
+ it('should truncate text to given length', async () => {
+ const { html } = render(
+ {
+ props: {
+ text: {
+ type: String,
+ },
+ },
+ template: '{{text}}
',
+ },
+ {
+ props: {
+ text: 'This is a very long text that should be truncated',
+ },
+ global: {
+ directives: {
+ n8nTruncate,
+ },
+ },
+ },
+ );
+ expect(html()).toBe('This is a very long text...
');
+ });
+});
diff --git a/packages/design-system/src/directives/n8n-truncate.ts b/packages/design-system/src/directives/n8n-truncate.ts
new file mode 100644
index 0000000000..8d454654c0
--- /dev/null
+++ b/packages/design-system/src/directives/n8n-truncate.ts
@@ -0,0 +1,26 @@
+import type { DirectiveBinding, ObjectDirective } from 'vue';
+import { truncate } from '../utils/string';
+
+/**
+ * Custom directive `n8nTruncate` to truncate text content of an HTML element.
+ *
+ * Usage:
+ * In your Vue template, use the directive `v-n8n-truncate` with an argument to specify the length to truncate to.
+ *
+ * Example:
+ * Some long text that will be truncated
+ *
+ * This will truncate the text content of the paragraph to 10 characters.
+ *
+ * Hint: Do not use it on components
+ * https://vuejs.org/guide/reusability/custom-directives#usage-on-components
+ */
+
+export const n8nTruncate: ObjectDirective = {
+ mounted(el: HTMLElement, binding: DirectiveBinding) {
+ el.textContent = truncate(el.textContent ?? '', Number(binding.arg) || undefined);
+ },
+ updated(el: HTMLElement, binding: DirectiveBinding) {
+ el.textContent = truncate(el.textContent ?? '', Number(binding.arg) || undefined);
+ },
+};
diff --git a/packages/design-system/src/main.ts b/packages/design-system/src/main.ts
index 54934d6f1c..dddb89f888 100644
--- a/packages/design-system/src/main.ts
+++ b/packages/design-system/src/main.ts
@@ -5,4 +5,5 @@ export * from './components';
export * from './plugin';
export * from './types';
export * from './utils';
+export * from './directives';
export { locale };
diff --git a/packages/design-system/src/plugin.ts b/packages/design-system/src/plugin.ts
index 493fde38ec..c6d3d2a6c9 100644
--- a/packages/design-system/src/plugin.ts
+++ b/packages/design-system/src/plugin.ts
@@ -1,5 +1,6 @@
import type { Component, Plugin } from 'vue';
import * as components from './components';
+import * as directives from './directives';
export interface N8nPluginOptions {}
@@ -8,5 +9,9 @@ export const N8nPlugin: Plugin = {
for (const [name, component] of Object.entries(components)) {
app.component(name, component as unknown as Component);
}
+
+ for (const [name, directive] of Object.entries(directives)) {
+ app.directive(name, directive);
+ }
},
};
diff --git a/packages/design-system/src/utils/string.test.ts b/packages/design-system/src/utils/string.test.ts
new file mode 100644
index 0000000000..6f65775f9a
--- /dev/null
+++ b/packages/design-system/src/utils/string.test.ts
@@ -0,0 +1,17 @@
+import { truncate } from './string';
+
+describe('Utils string', () => {
+ describe('truncate', () => {
+ it('should truncate text to 30 chars by default', () => {
+ expect(truncate('This is a very long text that should be truncated')).toBe(
+ 'This is a very long text that...',
+ );
+ });
+
+ it('should truncate text to given length', () => {
+ expect(truncate('This is a very long text that should be truncated', 25)).toBe(
+ 'This is a very long text...',
+ );
+ });
+ });
+});
diff --git a/packages/design-system/src/utils/string.ts b/packages/design-system/src/utils/string.ts
new file mode 100644
index 0000000000..9170b57c00
--- /dev/null
+++ b/packages/design-system/src/utils/string.ts
@@ -0,0 +1,2 @@
+export const truncate = (text: string, length = 30): string =>
+ text.length > length ? text.slice(0, length).trim() + '...' : text;
From 6a35812f9288c13d0a1887fcb7df5d2a16db88e2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?=
=?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?=
=?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?=
Date: Tue, 17 Sep 2024 14:10:22 +0200
Subject: [PATCH 23/42] ci: Setup biome and pre-commit hooks for formatting
(no-changelog) (#10795)
Co-authored-by: Tomi Turtiainen <10324676+tomi@users.noreply.github.com>
---
.github/workflows/ci-pull-requests.yml | 3 +
.prettierignore | 8 +
.vscode/extensions.json | 1 +
.vscode/settings.default.json | 16 ++
biome.jsonc | 48 ++++
cypress/biome.jsonc | 7 +
cypress/e2e/24-ndv-paired-item.cy.ts | 3 +-
cypress/e2e/6-code-node.cy.ts | 8 +-
cypress/package.json | 3 +-
lefthook.yml | 10 +
n8n.code-workspace | 6 +-
package.json | 4 +
packages/@n8n/api-types/package.json | 3 +-
packages/@n8n/benchmark/biome.jsonc | 7 +
packages/@n8n/benchmark/package.json | 2 +
packages/@n8n/chat/package.json | 3 +-
packages/@n8n/chat/src/css/markdown.scss | 70 ++---
packages/@n8n/client-oauth2/package.json | 3 +-
packages/@n8n/codemirror-lang/.eslintrc.cjs | 4 +-
packages/@n8n/codemirror-lang/package.json | 3 +-
packages/@n8n/config/package.json | 3 +-
packages/@n8n/imap/package.json | 3 +-
packages/@n8n/nodes-langchain/.prettierrc.js | 51 ----
.../nodes-langchain/.vscode/extensions.json | 6 +-
packages/@n8n/nodes-langchain/package.json | 3 +-
packages/@n8n/permissions/package.json | 3 +-
packages/@n8n_io/eslint-config/base.js | 28 --
packages/@n8n_io/eslint-config/package.json | 1 -
packages/@n8n_io/eslint-config/shared.js | 2 +-
packages/cli/package.json | 3 +-
packages/cli/src/config/types.ts | 11 +-
.../cli/src/controllers/e2e.controller.ts | 17 +-
.../src/databases/entities/auth-identity.ts | 5 +-
.../1674138566000-AddStatusToExecutions.ts | 4 +-
packages/cli/src/events/event-relay.ts | 8 +-
.../to-save-settings.ts | 4 +-
.../external-secrets-manager.ee.ts | 2 +-
packages/cli/src/license.ts | 2 +-
.../orchestration/main/multi-main-setup.ee.ts | 9 +-
packages/core/package.json | 3 +-
packages/core/src/WorkflowExecute.ts | 6 +-
packages/design-system/.storybook/preview.js | 2 +-
packages/design-system/biome.jsonc | 7 +
packages/design-system/chromatic.config.json | 4 +-
packages/design-system/package.json | 3 +-
.../design-system/src/css/_tokens.dark.scss | 2 +-
packages/design-system/src/css/_tokens.scss | 10 +-
packages/design-system/tsconfig.json | 7 +-
packages/design-system/vite.config.mts | 6 +-
packages/editor-ui/biome.jsonc | 8 +
packages/editor-ui/package.json | 3 +-
.../src/components/CodeNodeEditor/theme.ts | 2 +-
.../src/components/Node/NodeCreator/utils.ts | 2 +-
.../src/composables/useExpressionEditor.ts | 5 +-
.../plugins/connectors/N8nCustomConnector.ts | 6 +-
packages/editor-ui/src/plugins/i18n/index.ts | 2 +-
packages/editor-ui/src/router.ts | 2 +-
.../editor-ui/src/stores/credentials.store.ts | 2 +-
packages/editor-ui/src/stores/ndv.store.ts | 2 +-
.../src/stores/workflows.ee.store.ts | 2 +-
.../editor-ui/src/styles/plugins/index.scss | 4 +-
packages/editor-ui/src/types/externalHooks.ts | 6 +-
packages/editor-ui/vite.config.mts | 8 +-
packages/node-dev/package.json | 3 +-
packages/nodes-base/biome.jsonc | 7 +
.../nodes/Baserow/GenericFunctions.ts | 2 +-
.../nodes/Brevo/GenericFunctions.ts | 12 +-
.../nodes/Cortex/AnalyzerInterface.ts | 26 +-
.../nodes/Google/Chat/MessageInterface.ts | 6 +-
.../Google/Sheet/v2/helpers/GoogleSheet.ts | 3 +-
.../Google/Sheet/v2/methods/listSearch.ts | 1 -
.../KoBoToolbox/KoBoToolboxTrigger.node.ts | 7 +-
.../nodes/Notion/shared/GenericFunctions.ts | 15 +-
.../nodes/Postgres/v1/genericFunctions.ts | 12 +-
.../nodes/QuickBase/GenericFunctions.ts | 7 +-
.../nodes/Telegram/GenericFunctions.ts | 7 +-
.../TheHive/interfaces/ObservableInterface.ts | 26 +-
packages/nodes-base/package.json | 3 +-
packages/workflow/package.json | 3 +-
.../ExpressionExtension.test.ts | 6 +-
packages/workflow/test/RoutingNode.test.ts | 2 +-
.../WorkflowDataProxy/pindata_workflow.json | 199 ++++++-------
pnpm-lock.yaml | 270 +++++++++++++-----
scripts/format.mjs | 67 +++--
turbo.json | 1 +
85 files changed, 701 insertions(+), 465 deletions(-)
create mode 100644 biome.jsonc
create mode 100644 cypress/biome.jsonc
create mode 100644 lefthook.yml
create mode 100644 packages/@n8n/benchmark/biome.jsonc
delete mode 100644 packages/@n8n/nodes-langchain/.prettierrc.js
create mode 100644 packages/design-system/biome.jsonc
create mode 100644 packages/editor-ui/biome.jsonc
create mode 100644 packages/nodes-base/biome.jsonc
diff --git a/.github/workflows/ci-pull-requests.yml b/.github/workflows/ci-pull-requests.yml
index d9938983b1..48a34c5eab 100644
--- a/.github/workflows/ci-pull-requests.yml
+++ b/.github/workflows/ci-pull-requests.yml
@@ -30,6 +30,9 @@ jobs:
- name: Build
run: pnpm build
+ - name: Run formatcheck
+ run: pnpm format:check
+
- name: Run typecheck
run: pnpm typecheck
diff --git a/.prettierignore b/.prettierignore
index 4ac8f0dafb..2f5967e399 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -7,3 +7,11 @@ packages/nodes-base/nodes/**/test
packages/cli/templates/form-trigger.handlebars
cypress/fixtures
CHANGELOG.md
+.github/pull_request_template.md
+# Ignored for now
+**/*.md
+# Handled by biome
+**/*.ts
+**/*.js
+**/*.json
+**/*.jsonc
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 158d03fdc8..681de6c024 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -1,5 +1,6 @@
{
"recommendations": [
+ "biomejs.biome",
"streetsidesoftware.code-spell-checker",
"dangmai.workspace-default-settings",
"dbaeumer.vscode-eslint",
diff --git a/.vscode/settings.default.json b/.vscode/settings.default.json
index 87db2ca2ee..99c514f741 100644
--- a/.vscode/settings.default.json
+++ b/.vscode/settings.default.json
@@ -1,6 +1,22 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
+ "[javascript]": {
+ "editor.defaultFormatter": "biomejs.biome"
+ },
+ "[typescript]": {
+ "editor.defaultFormatter": "biomejs.biome"
+ },
+ "[json]": {
+ "editor.defaultFormatter": "biomejs.biome"
+ },
+ "[jsonc]": {
+ "editor.defaultFormatter": "biomejs.biome"
+ },
+ "editor.codeActionsOnSave": {
+ "quickfix.biome": "explicit",
+ "source.organizeImports.biome": "never"
+ },
"search.exclude": {
"node_modules": true,
"dist": true,
diff --git a/biome.jsonc b/biome.jsonc
new file mode 100644
index 0000000000..356c964cd9
--- /dev/null
+++ b/biome.jsonc
@@ -0,0 +1,48 @@
+{
+ "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
+ "vcs": {
+ "clientKind": "git",
+ "enabled": true,
+ "useIgnoreFile": true
+ },
+ "files": {
+ "ignore": [
+ "**/.turbo",
+ "**/coverage",
+ "**/dist",
+ "**/package.json",
+ "**/pnpm-lock.yaml",
+ "**/CHANGELOG.md"
+ ]
+ },
+ "formatter": {
+ "enabled": true,
+ "formatWithErrors": false,
+ "indentStyle": "tab",
+ "indentWidth": 2,
+ "lineEnding": "lf",
+ "lineWidth": 100,
+ "attributePosition": "auto",
+ "ignore": [
+ // Handled by prettier
+ "**/*.vue"
+ ]
+ },
+ "organizeImports": { "enabled": false },
+ "linter": {
+ "enabled": false
+ },
+ "javascript": {
+ "formatter": {
+ "jsxQuoteStyle": "double",
+ "quoteProperties": "asNeeded",
+ "trailingCommas": "all",
+ "semicolons": "always",
+ "arrowParentheses": "always",
+ "bracketSpacing": true,
+ "bracketSameLine": false,
+ "quoteStyle": "single",
+ "attributePosition": "auto"
+ }
+ }
+}
diff --git a/cypress/biome.jsonc b/cypress/biome.jsonc
new file mode 100644
index 0000000000..5a63363ac8
--- /dev/null
+++ b/cypress/biome.jsonc
@@ -0,0 +1,7 @@
+{
+ "$schema": "../node_modules/@biomejs/biome/configuration_schema.json",
+ "extends": ["../biome.jsonc"],
+ "formatter": {
+ "ignore": ["fixtures/**"]
+ }
+}
diff --git a/cypress/e2e/24-ndv-paired-item.cy.ts b/cypress/e2e/24-ndv-paired-item.cy.ts
index 8324144343..1261a0fcd1 100644
--- a/cypress/e2e/24-ndv-paired-item.cy.ts
+++ b/cypress/e2e/24-ndv-paired-item.cy.ts
@@ -227,7 +227,7 @@ describe('NDV', () => {
workflowPage.actions.zoomToFit();
- /* prettier-ignore */
+ // biome-ignore format:
const PINNED_DATA = [
{
"id": "abc",
@@ -263,7 +263,6 @@ describe('NDV', () => {
]
}
];
- /* prettier-ignore */
workflowPage.actions.openNode('Get thread details1');
ndv.actions.pastePinnedData(PINNED_DATA);
ndv.actions.close();
diff --git a/cypress/e2e/6-code-node.cy.ts b/cypress/e2e/6-code-node.cy.ts
index 43486590c7..d26e0ded5d 100644
--- a/cypress/e2e/6-code-node.cy.ts
+++ b/cypress/e2e/6-code-node.cy.ts
@@ -43,7 +43,9 @@ describe('Code node', () => {
const getParameter = () => ndv.getters.parameterInput('jsCode').should('be.visible');
const getEditor = () => getParameter().find('.cm-content').should('exist');
- getEditor().type('{selectall}').paste(`$input.itemMatching()
+ getEditor()
+ .type('{selectall}')
+ .paste(`$input.itemMatching()
$input.item
$('When clicking ‘Test workflow’').item
$input.first(1)
@@ -68,7 +70,9 @@ return
ndv.getters.parameterInput('mode').click();
ndv.actions.selectOptionInParameterDropdown('mode', 'Run Once for Each Item');
- getEditor().type('{selectall}').paste(`$input.itemMatching()
+ getEditor()
+ .type('{selectall}')
+ .paste(`$input.itemMatching()
$input.all()
$input.first()
$input.item()
diff --git a/cypress/package.json b/cypress/package.json
index 5084e0b10c..509abca04b 100644
--- a/cypress/package.json
+++ b/cypress/package.json
@@ -7,7 +7,8 @@
"test:e2e:ui": "scripts/run-e2e.js ui",
"test:e2e:dev": "scripts/run-e2e.js dev",
"test:e2e:all": "scripts/run-e2e.js all",
- "format": "prettier --write . --ignore-path ../.prettierignore",
+ "format": "biome format --write .",
+ "format:check": "biome ci .",
"lint": "eslint . --quiet",
"lintfix": "eslint . --fix",
"develop": "cd ..; pnpm dev",
diff --git a/lefthook.yml b/lefthook.yml
new file mode 100644
index 0000000000..bb58014db9
--- /dev/null
+++ b/lefthook.yml
@@ -0,0 +1,10 @@
+pre-commit:
+ commands:
+ biome_check:
+ glob: 'packages/**/*.{js,ts,json}'
+ run: ./node_modules/.bin/biome check --write --no-errors-on-unmatched --files-ignore-unknown=true --colors=off {staged_files}
+ stage_fixed: true
+ prettier_check:
+ glob: 'packages/**/*.{vue,yml,md}'
+ run: ./node_modules/.bin/prettier --write --ignore-unknown --no-error-on-unmatched-pattern {staged_files}
+ stage_fixed: true
diff --git a/n8n.code-workspace b/n8n.code-workspace
index 9d32d7aa04..8f4183e8f0 100644
--- a/n8n.code-workspace
+++ b/n8n.code-workspace
@@ -1,7 +1,7 @@
{
"folders": [
{
- "path": ".",
- },
- ],
+ "path": "."
+ }
+ ]
}
diff --git a/package.json b/package.json
index 93ec1b5369..733edd2e24 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
},
"packageManager": "pnpm@9.6.0",
"scripts": {
+ "prepare": "lefthook install",
"preinstall": "node scripts/block-npm-install.js",
"build": "turbo run build",
"build:backend": "turbo run build:backend",
@@ -19,6 +20,7 @@
"clean": "turbo run clean --parallel",
"reset": "node scripts/ensure-zx.mjs && zx scripts/reset.mjs",
"format": "turbo run format && node scripts/format.mjs",
+ "format:check": "turbo run format:check",
"lint": "turbo run lint",
"lintfix": "turbo run lintfix",
"lint:backend": "turbo run lint:backend",
@@ -38,6 +40,7 @@
"worker": "./packages/cli/bin/n8n worker"
},
"devDependencies": {
+ "@biomejs/biome": "^1.9.0",
"@n8n_io/eslint-config": "workspace:*",
"@types/jest": "^29.5.3",
"@types/supertest": "^6.0.2",
@@ -46,6 +49,7 @@
"jest-expect-message": "^1.1.3",
"jest-mock": "^29.6.2",
"jest-mock-extended": "^3.0.4",
+ "lefthook": "^1.7.15",
"nock": "^13.3.2",
"nodemon": "^3.0.1",
"p-limit": "^3.1.0",
diff --git a/packages/@n8n/api-types/package.json b/packages/@n8n/api-types/package.json
index 2e6d17a2a8..07466a7a71 100644
--- a/packages/@n8n/api-types/package.json
+++ b/packages/@n8n/api-types/package.json
@@ -6,7 +6,8 @@
"dev": "pnpm watch",
"typecheck": "tsc --noEmit",
"build": "tsc -p tsconfig.build.json",
- "format": "prettier --write . --ignore-path ../../../.prettierignore",
+ "format": "biome format --write .",
+ "format:check": "biome ci .",
"lint": "eslint .",
"lintfix": "eslint . --fix",
"watch": "tsc -p tsconfig.build.json --watch",
diff --git a/packages/@n8n/benchmark/biome.jsonc b/packages/@n8n/benchmark/biome.jsonc
new file mode 100644
index 0000000000..3db16975c6
--- /dev/null
+++ b/packages/@n8n/benchmark/biome.jsonc
@@ -0,0 +1,7 @@
+{
+ "$schema": "../node_modules/@biomejs/biome/configuration_schema.json",
+ "extends": ["../../../biome.jsonc"],
+ "files": {
+ "ignore": ["scripts/mock-api/**"]
+ }
+}
diff --git a/packages/@n8n/benchmark/package.json b/packages/@n8n/benchmark/package.json
index 67292b07c9..6294391c4c 100644
--- a/packages/@n8n/benchmark/package.json
+++ b/packages/@n8n/benchmark/package.json
@@ -5,6 +5,8 @@
"main": "dist/index",
"scripts": {
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
+ "format": "biome format --write .",
+ "format:check": "biome ci .",
"lint": "eslint .",
"lintfix": "eslint . --fix",
"start": "./bin/n8n-benchmark",
diff --git a/packages/@n8n/chat/package.json b/packages/@n8n/chat/package.json
index 8a3b5b343c..1d319a743c 100644
--- a/packages/@n8n/chat/package.json
+++ b/packages/@n8n/chat/package.json
@@ -12,7 +12,8 @@
"typecheck": "vue-tsc --noEmit",
"lint": "eslint . --ext .js,.ts,.vue --quiet",
"lintfix": "eslint . --ext .js,.ts,.vue --fix",
- "format": "prettier --write src/",
+ "format": "biome format --write src .storybook && prettier --write src/ --ignore-path ../../.prettierignore",
+ "format:check": "biome ci src .storybook && prettier --check src/ --ignore-path ../../.prettierignore",
"storybook": "storybook dev -p 6006 --no-open",
"build:storybook": "storybook build"
},
diff --git a/packages/@n8n/chat/src/css/markdown.scss b/packages/@n8n/chat/src/css/markdown.scss
index f2ea3ea226..6d219bbe02 100644
--- a/packages/@n8n/chat/src/css/markdown.scss
+++ b/packages/@n8n/chat/src/css/markdown.scss
@@ -2,7 +2,7 @@
// https://github.com/pxlrbt/markdown-css
.chat-message-markdown {
- /*
+ /*
universalize.css (v1.0.2) — by Alexander Sandberg (https://alexandersandberg.com)
------------------------------------------------------------------------------
@@ -45,8 +45,6 @@
vertical-align: inherit; /* 2 */
}
-
-
/*
Remove inconsistent and unnecessary margins
*/
@@ -63,7 +61,8 @@
button, /* (Safari) 3 lines */
input,
select,
- textarea { /* (Firefox, Safari) */
+ textarea {
+ /* (Firefox, Safari) */
margin: 0;
}
@@ -80,11 +79,13 @@
Add correct display
*/
main, /* (IE11) */
- details { /* (Edge 18-, IE) */
+ details {
+ /* (Edge 18-, IE) */
display: block;
}
- summary { /* (all) */
+ summary {
+ /* (all) */
display: list-item;
}
@@ -106,18 +107,19 @@
kbd,
samp {
font-family:
- /* macOS 10.10+ */ "Menlo",
- /* Windows 6+ */ "Consolas",
- /* Android 4+ */ "Roboto Mono",
- /* Ubuntu 10.10+ */ "Ubuntu Monospace",
- /* KDE Plasma 5+ */ "Noto Mono",
- /* KDE Plasma 4+ */ "Oxygen Mono",
- /* Linux/OpenOffice fallback */ "Liberation Mono",
+ /* macOS 10.10+ */
+ 'Menlo',
+ /* Windows 6+ */ 'Consolas',
+ /* Android 4+ */ 'Roboto Mono',
+ /* Ubuntu 10.10+ */ 'Ubuntu Monospace',
+ /* KDE Plasma 5+ */ 'Noto Mono',
+ /* KDE Plasma 4+ */ 'Oxygen Mono',
+ /* Linux/OpenOffice fallback */ 'Liberation Mono',
/* fallback */ monospace,
- /* macOS emoji */ "Apple Color Emoji",
- /* Windows emoji */ "Segoe UI Emoji",
- /* Windows emoji */ "Segoe UI Symbol",
- /* Linux emoji */ "Noto Color Emoji"; /* 1 */
+ /* macOS emoji */ 'Apple Color Emoji',
+ /* Windows emoji */ 'Segoe UI Emoji',
+ /* Windows emoji */ 'Segoe UI Symbol',
+ /* Linux emoji */ 'Noto Color Emoji'; /* 1 */
font-size: 1em; /* 2 */
}
@@ -130,7 +132,7 @@
cursor: help; /* 1 */
text-decoration: underline; /* 2 */
-webkit-text-decoration: underline dotted;
- text-decoration: underline dotted; /* 2 */
+ text-decoration: underline dotted; /* 2 */
}
/*
@@ -201,9 +203,9 @@
Correct inability to style buttons (iOS, Safari)
*/
button,
- [type="button"],
- [type="reset"],
- [type="submit"] {
+ [type='button'],
+ [type='reset'],
+ [type='submit'] {
-webkit-appearance: button;
}
@@ -249,7 +251,7 @@
1. Correct outline style (Safari)
2. Correct odd appearance (Chrome, Edge, Safari)
*/
- [type="search"] {
+ [type='search'] {
outline-offset: -2px; /* 1 */
-webkit-appearance: textfield; /* 2 */
}
@@ -311,7 +313,7 @@
/*
Change cursor on busy elements (all)
*/
- [aria-busy="true"] {
+ [aria-busy='true'] {
cursor: progress;
}
@@ -325,7 +327,7 @@
/*
Change cursor on disabled, non-editable, or inoperable elements (all)
*/
- [aria-disabled="true"],
+ [aria-disabled='true'],
[disabled] {
cursor: not-allowed;
}
@@ -333,12 +335,12 @@
/*
Change display on visually hidden accessible elements (all)
*/
- [aria-hidden="false"][hidden] {
+ [aria-hidden='false'][hidden] {
display: inline;
display: initial;
}
- [aria-hidden="false"][hidden]:not(:focus) {
+ [aria-hidden='false'][hidden]:not(:focus) {
clip: rect(0, 0, 0, 0);
position: absolute;
}
@@ -347,8 +349,8 @@
Print out URLs after links (all)
*/
@media print {
- a[href^="http"]::after {
- content: " (" attr(href) ")";
+ a[href^='http']::after {
+ content: ' (' attr(href) ')';
}
}
/* ----- Variables ----- */
@@ -484,8 +486,8 @@
*/
pre {
-moz-tab-size: 4;
- -o-tab-size: 4;
- tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
}
/*
@@ -563,7 +565,7 @@
padding: 0.1em 0.25em;
border-radius: 0.2rem;
-webkit-box-decoration-break: clone;
- box-decoration-break: clone;
+ box-decoration-break: clone;
}
kbd > kbd {
@@ -573,8 +575,8 @@
pre {
-moz-tab-size: 4;
- -o-tab-size: 4;
- tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
}
pre code {
@@ -586,7 +588,7 @@
/* ----- Forms ----- */
/* ----- Misc ----- */
- [tabindex="-1"]:focus {
+ [tabindex='-1']:focus {
outline: none;
}
diff --git a/packages/@n8n/client-oauth2/package.json b/packages/@n8n/client-oauth2/package.json
index 7ec91b9839..1bd8cc3c03 100644
--- a/packages/@n8n/client-oauth2/package.json
+++ b/packages/@n8n/client-oauth2/package.json
@@ -6,7 +6,8 @@
"dev": "pnpm watch",
"typecheck": "tsc --noEmit",
"build": "tsc -p tsconfig.build.json",
- "format": "prettier --write . --ignore-path ../../../.prettierignore",
+ "format": "biome format --write src test",
+ "format:check": "biome ci src test",
"lint": "eslint . --quiet",
"lintfix": "eslint . --fix",
"watch": "tsc -p tsconfig.build.json --watch",
diff --git a/packages/@n8n/codemirror-lang/.eslintrc.cjs b/packages/@n8n/codemirror-lang/.eslintrc.cjs
index 25eba96d79..d07f6ff6fa 100644
--- a/packages/@n8n/codemirror-lang/.eslintrc.cjs
+++ b/packages/@n8n/codemirror-lang/.eslintrc.cjs
@@ -8,7 +8,5 @@ module.exports = {
...sharedOptions(__dirname),
- ignorePatterns: [
- 'src/expressions/grammar*.ts'
- ]
+ ignorePatterns: ['src/expressions/grammar*.ts'],
};
diff --git a/packages/@n8n/codemirror-lang/package.json b/packages/@n8n/codemirror-lang/package.json
index a8450b419d..31a8b1a379 100644
--- a/packages/@n8n/codemirror-lang/package.json
+++ b/packages/@n8n/codemirror-lang/package.json
@@ -24,7 +24,8 @@
"test": "jest",
"lint": "eslint . --ext .ts --quiet",
"lintfix": "eslint . --ext .ts --fix",
- "format": "prettier --write --ignore-path ../../.prettierignore src test"
+ "format": "biome format --write src test",
+ "format:check": "biome ci src test"
},
"peerDependencies": {
"@codemirror/language": "*",
diff --git a/packages/@n8n/config/package.json b/packages/@n8n/config/package.json
index 875d6ca797..aec7071c08 100644
--- a/packages/@n8n/config/package.json
+++ b/packages/@n8n/config/package.json
@@ -6,7 +6,8 @@
"dev": "pnpm watch",
"typecheck": "tsc --noEmit",
"build": "tsc -p tsconfig.build.json",
- "format": "prettier --write . --ignore-path ../../../.prettierignore",
+ "format": "biome format --write src test",
+ "format:check": "biome ci src test",
"lint": "eslint .",
"lintfix": "eslint . --fix",
"watch": "tsc -p tsconfig.build.json --watch",
diff --git a/packages/@n8n/imap/package.json b/packages/@n8n/imap/package.json
index 77b6254330..bfb2259202 100644
--- a/packages/@n8n/imap/package.json
+++ b/packages/@n8n/imap/package.json
@@ -6,7 +6,8 @@
"dev": "pnpm watch",
"typecheck": "tsc --noEmit",
"build": "tsc -p tsconfig.build.json",
- "format": "prettier --write . --ignore-path ../../../.prettierignore",
+ "format": "biome format --write src test",
+ "format:check": "biome ci src test",
"lint": "eslint . --quiet",
"lintfix": "eslint . --fix",
"watch": "tsc -p tsconfig.build.json --watch",
diff --git a/packages/@n8n/nodes-langchain/.prettierrc.js b/packages/@n8n/nodes-langchain/.prettierrc.js
deleted file mode 100644
index ebf28d8091..0000000000
--- a/packages/@n8n/nodes-langchain/.prettierrc.js
+++ /dev/null
@@ -1,51 +0,0 @@
-module.exports = {
- /**
- * https://prettier.io/docs/en/options.html#semicolons
- */
- semi: true,
-
- /**
- * https://prettier.io/docs/en/options.html#trailing-commas
- */
- trailingComma: 'all',
-
- /**
- * https://prettier.io/docs/en/options.html#bracket-spacing
- */
- bracketSpacing: true,
-
- /**
- * https://prettier.io/docs/en/options.html#tabs
- */
- useTabs: true,
-
- /**
- * https://prettier.io/docs/en/options.html#tab-width
- */
- tabWidth: 2,
-
- /**
- * https://prettier.io/docs/en/options.html#arrow-function-parentheses
- */
- arrowParens: 'always',
-
- /**
- * https://prettier.io/docs/en/options.html#quotes
- */
- singleQuote: true,
-
- /**
- * https://prettier.io/docs/en/options.html#quote-props
- */
- quoteProps: 'as-needed',
-
- /**
- * https://prettier.io/docs/en/options.html#end-of-line
- */
- endOfLine: 'lf',
-
- /**
- * https://prettier.io/docs/en/options.html#print-width
- */
- printWidth: 100,
-};
diff --git a/packages/@n8n/nodes-langchain/.vscode/extensions.json b/packages/@n8n/nodes-langchain/.vscode/extensions.json
index 306a6af39a..a15593feb1 100644
--- a/packages/@n8n/nodes-langchain/.vscode/extensions.json
+++ b/packages/@n8n/nodes-langchain/.vscode/extensions.json
@@ -1,7 +1,3 @@
{
- "recommendations": [
- "dbaeumer.vscode-eslint",
- "EditorConfig.EditorConfig",
- "esbenp.prettier-vscode",
- ]
+ "recommendations": ["dbaeumer.vscode-eslint", "EditorConfig.EditorConfig", "biomejs.biome"]
}
diff --git a/packages/@n8n/nodes-langchain/package.json b/packages/@n8n/nodes-langchain/package.json
index 56f675f09f..65adaad31a 100644
--- a/packages/@n8n/nodes-langchain/package.json
+++ b/packages/@n8n/nodes-langchain/package.json
@@ -9,7 +9,8 @@
"typecheck": "tsc --noEmit",
"build": "tsc -p tsconfig.build.json && pnpm n8n-copy-icons && pnpm build:metadata",
"build:metadata": "pnpm n8n-generate-known && pnpm n8n-generate-ui-types",
- "format": "prettier nodes credentials --write",
+ "format": "biome format --write .",
+ "format:check": "biome ci .",
"lint": "eslint nodes credentials --quiet",
"lintfix": "eslint nodes credentials --fix",
"watch": "tsc-watch -p tsconfig.build.json --onCompilationComplete \"tsc-alias -p tsconfig.build.json\" --onSuccess \"pnpm n8n-generate-ui-types\"",
diff --git a/packages/@n8n/permissions/package.json b/packages/@n8n/permissions/package.json
index 8f369a8a23..bbb046f111 100644
--- a/packages/@n8n/permissions/package.json
+++ b/packages/@n8n/permissions/package.json
@@ -6,7 +6,8 @@
"dev": "pnpm watch",
"typecheck": "tsc --noEmit",
"build": "tsc -p tsconfig.build.json",
- "format": "prettier --write . --ignore-path ../../../.prettierignore",
+ "format": "biome format --write .",
+ "format:check": "biome ci .",
"lint": "eslint . --quiet",
"lintfix": "eslint . --fix",
"watch": "tsc -p tsconfig.build.json --watch",
diff --git a/packages/@n8n_io/eslint-config/base.js b/packages/@n8n_io/eslint-config/base.js
index 682c5d68b5..1ec0410a88 100644
--- a/packages/@n8n_io/eslint-config/base.js
+++ b/packages/@n8n_io/eslint-config/base.js
@@ -22,12 +22,6 @@ const config = (module.exports = {
*/
'@typescript-eslint',
- /**
- * Plugin to report formatting violations as lint violations
- * https://github.com/prettier/eslint-plugin-prettier
- */
- 'eslint-plugin-prettier',
-
/*
* Plugin to allow specifying local ESLint rules.
* https://github.com/ivov/eslint-plugin-n8n-local-rules
@@ -76,28 +70,6 @@ const config = (module.exports = {
],
rules: {
- // ******************************************************************
- // required by prettier plugin
- // ******************************************************************
-
- // The following rule enables eslint-plugin-prettier
- // See: https://github.com/prettier/eslint-plugin-prettier#recommended-configuration
-
- 'prettier/prettier': ['error', { endOfLine: 'auto' }],
-
- // The following two rules must be disabled when using eslint-plugin-prettier:
- // See: https://github.com/prettier/eslint-plugin-prettier#arrow-body-style-and-prefer-arrow-callback-issue
-
- /**
- * https://eslint.org/docs/rules/arrow-body-style
- */
- 'arrow-body-style': 'off',
-
- /**
- * https://eslint.org/docs/rules/prefer-arrow-callback
- */
- 'prefer-arrow-callback': 'off',
-
// ******************************************************************
// additions to base ruleset
// ******************************************************************
diff --git a/packages/@n8n_io/eslint-config/package.json b/packages/@n8n_io/eslint-config/package.json
index 6f776d0e64..e7b2845a24 100644
--- a/packages/@n8n_io/eslint-config/package.json
+++ b/packages/@n8n_io/eslint-config/package.json
@@ -15,7 +15,6 @@
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-lodash": "^7.4.0",
"eslint-plugin-n8n-local-rules": "^1.0.0",
- "eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-unicorn": "^51.0.1",
"eslint-plugin-unused-imports": "^3.1.0",
"eslint-plugin-vue": "^9.23.0",
diff --git a/packages/@n8n_io/eslint-config/shared.js b/packages/@n8n_io/eslint-config/shared.js
index 0332606f0c..f77a70df2f 100644
--- a/packages/@n8n_io/eslint-config/shared.js
+++ b/packages/@n8n_io/eslint-config/shared.js
@@ -13,7 +13,7 @@ module.exports = (tsconfigRootDir, mode) => {
vue: 'vue-eslint-parser',
template: 'vue-eslint-parser',
},
- }
+ }
: {};
const settings = {
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 3b2d9ceffc..1b33555a96 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -17,7 +17,8 @@
"dev": "concurrently -k -n \"TypeScript,Node\" -c \"yellow.bold,cyan.bold\" \"npm run watch\" \"nodemon\"",
"dev:worker": "concurrently -k -n \"TypeScript,Node\" -c \"yellow.bold,cyan.bold\" \"npm run watch\" \"nodemon worker\"",
"dev:webhook": "concurrently -k -n \"TypeScript,Node\" -c \"yellow.bold,cyan.bold\" \"npm run watch\" \"nodemon webhook\"",
- "format": "prettier --write . --ignore-path ../../.prettierignore",
+ "format": "biome format --write .",
+ "format:check": "biome ci .",
"lint": "eslint . --quiet",
"lintfix": "eslint . --fix",
"start": "run-script-os",
diff --git a/packages/cli/src/config/types.ts b/packages/cli/src/config/types.ts
index b1a4b22b98..0d3c5db2cb 100644
--- a/packages/cli/src/config/types.ts
+++ b/packages/cli/src/config/types.ts
@@ -84,12 +84,11 @@ type ExceptionPaths = {
// string literals map
// -----------------------------------
-type GetPathSegmentsWithUnions =
- T extends ReadonlyArray
- ? [C]
- : {
- [K in ValidKeys]: [K, ...GetPathSegmentsWithUnions];
- }[ValidKeys];
+type GetPathSegmentsWithUnions = T extends ReadonlyArray
+ ? [C]
+ : {
+ [K in ValidKeys]: [K, ...GetPathSegmentsWithUnions];
+ }[ValidKeys];
type ToPathUnionPair = T extends [...infer Path, infer Union]
? Path extends string[]
diff --git a/packages/cli/src/controllers/e2e.controller.ts b/packages/cli/src/controllers/e2e.controller.ts
index 2e4d39acc0..5137d5b4af 100644
--- a/packages/cli/src/controllers/e2e.controller.ts
+++ b/packages/cli/src/controllers/e2e.controller.ts
@@ -12,6 +12,7 @@ import { UserRepository } from '@/databases/repositories/user.repository';
import { Patch, Post, RestController } from '@/decorators';
import { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus';
import type { BooleanLicenseFeature, NumericLicenseFeature } from '@/interfaces';
+import type { FeatureReturnType } from '@/license';
import { License } from '@/license';
import { Logger } from '@/logger';
import { MfaService } from '@/mfa/mfa.service';
@@ -115,10 +116,18 @@ export class E2EController {
) {
license.isFeatureEnabled = (feature: BooleanLicenseFeature) =>
this.enabledFeatures[feature] ?? false;
- // @ts-expect-error Overriding method
- // eslint-disable-next-line @typescript-eslint/unbound-method
- license.getFeatureValue = (feature: NumericLicenseFeature) =>
- this.numericFeatures[feature] ?? UNLIMITED_LICENSE_QUOTA;
+
+ // Ugly hack to satisfy biome parser
+ const getFeatureValue = (
+ feature: T,
+ ): FeatureReturnType[T] => {
+ if (feature in this.numericFeatures) {
+ return this.numericFeatures[feature as NumericLicenseFeature] as FeatureReturnType[T];
+ } else {
+ return UNLIMITED_LICENSE_QUOTA as FeatureReturnType[T];
+ }
+ };
+ license.getFeatureValue = getFeatureValue;
license.getPlanName = () => 'Enterprise';
}
diff --git a/packages/cli/src/databases/entities/auth-identity.ts b/packages/cli/src/databases/entities/auth-identity.ts
index f54b1b6ef4..88d382bb3a 100644
--- a/packages/cli/src/databases/entities/auth-identity.ts
+++ b/packages/cli/src/databases/entities/auth-identity.ts
@@ -11,7 +11,10 @@ export class AuthIdentity extends WithTimestamps {
@Column()
userId: string;
- @ManyToOne(() => User, (user) => user.authIdentities)
+ @ManyToOne(
+ () => User,
+ (user) => user.authIdentities,
+ )
user: User;
@PrimaryColumn()
diff --git a/packages/cli/src/databases/migrations/mysqldb/1674138566000-AddStatusToExecutions.ts b/packages/cli/src/databases/migrations/mysqldb/1674138566000-AddStatusToExecutions.ts
index b41948eb70..b8d86c63ca 100644
--- a/packages/cli/src/databases/migrations/mysqldb/1674138566000-AddStatusToExecutions.ts
+++ b/packages/cli/src/databases/migrations/mysqldb/1674138566000-AddStatusToExecutions.ts
@@ -8,6 +8,8 @@ export class AddStatusToExecutions1674138566000 implements ReversibleMigration {
}
async down({ queryRunner, tablePrefix }: MigrationContext) {
- await queryRunner.query(`ALTER TABLE \`${tablePrefix}execution_entity\` DROP COLUMN \`status\``);
+ await queryRunner.query(
+ `ALTER TABLE \`${tablePrefix}execution_entity\` DROP COLUMN \`status\``,
+ );
}
}
diff --git a/packages/cli/src/events/event-relay.ts b/packages/cli/src/events/event-relay.ts
index dde983884f..3202b69c15 100644
--- a/packages/cli/src/events/event-relay.ts
+++ b/packages/cli/src/events/event-relay.ts
@@ -8,9 +8,11 @@ import { EventService } from './event.service';
export class EventRelay {
constructor(readonly eventService: EventService) {}
- protected setupListeners(map: {
- [EventName in EventNames]?: (event: RelayEventMap[EventName]) => void | Promise;
- }) {
+ protected setupListeners(
+ map: {
+ [EventName in EventNames]?: (event: RelayEventMap[EventName]) => void | Promise;
+ },
+ ) {
for (const [eventName, handler] of Object.entries(map) as Array<
[EventNames, (event: RelayEventMap[EventNames]) => void | Promise]
>) {
diff --git a/packages/cli/src/execution-lifecycle-hooks/to-save-settings.ts b/packages/cli/src/execution-lifecycle-hooks/to-save-settings.ts
index 71d012a1d6..7a25adaeba 100644
--- a/packages/cli/src/execution-lifecycle-hooks/to-save-settings.ts
+++ b/packages/cli/src/execution-lifecycle-hooks/to-save-settings.ts
@@ -28,10 +28,10 @@ export function toSaveSettings(workflowSettings: IWorkflowSettings = {}) {
manual:
workflowSettings === undefined || workflowSettings.saveManualExecutions === 'DEFAULT'
? DEFAULTS.MANUAL
- : workflowSettings.saveManualExecutions ?? DEFAULTS.MANUAL,
+ : (workflowSettings.saveManualExecutions ?? DEFAULTS.MANUAL),
progress:
workflowSettings === undefined || workflowSettings.saveExecutionProgress === 'DEFAULT'
? DEFAULTS.PROGRESS
- : workflowSettings.saveExecutionProgress ?? DEFAULTS.PROGRESS,
+ : (workflowSettings.saveExecutionProgress ?? DEFAULTS.PROGRESS),
};
}
diff --git a/packages/cli/src/external-secrets/external-secrets-manager.ee.ts b/packages/cli/src/external-secrets/external-secrets-manager.ee.ts
index 5ac8a5a1bf..dd33132d37 100644
--- a/packages/cli/src/external-secrets/external-secrets-manager.ee.ts
+++ b/packages/cli/src/external-secrets/external-secrets-manager.ee.ts
@@ -292,7 +292,7 @@ export class ExternalSecretsManager {
}
settings[provider] = {
connected,
- connectedAt: connected ? new Date() : settings[provider]?.connectedAt ?? null,
+ connectedAt: connected ? new Date() : (settings[provider]?.connectedAt ?? null),
settings: settings[provider]?.settings ?? {},
};
diff --git a/packages/cli/src/license.ts b/packages/cli/src/license.ts
index 7f6ef42f3c..fde17a8fcc 100644
--- a/packages/cli/src/license.ts
+++ b/packages/cli/src/license.ts
@@ -21,7 +21,7 @@ import type { BooleanLicenseFeature, NumericLicenseFeature } from './interfaces'
import type { RedisServicePubSubPublisher } from './services/redis/redis-service-pub-sub-publisher';
import { RedisService } from './services/redis.service';
-type FeatureReturnType = Partial<
+export type FeatureReturnType = Partial<
{
planName: string;
} & { [K in NumericLicenseFeature]: number } & { [K in BooleanLicenseFeature]: boolean }
diff --git a/packages/cli/src/services/orchestration/main/multi-main-setup.ee.ts b/packages/cli/src/services/orchestration/main/multi-main-setup.ee.ts
index bdc3ab5316..aa0b02ffc2 100644
--- a/packages/cli/src/services/orchestration/main/multi-main-setup.ee.ts
+++ b/packages/cli/src/services/orchestration/main/multi-main-setup.ee.ts
@@ -42,12 +42,9 @@ export class MultiMainSetup extends TypedEmitter {
await this.tryBecomeLeader(); // prevent initial wait
- this.leaderCheckInterval = setInterval(
- async () => {
- await this.checkLeader();
- },
- config.getEnv('multiMainSetup.interval') * TIME.SECOND,
- );
+ this.leaderCheckInterval = setInterval(async () => {
+ await this.checkLeader();
+ }, config.getEnv('multiMainSetup.interval') * TIME.SECOND);
}
async shutdown() {
diff --git a/packages/core/package.json b/packages/core/package.json
index 8661527771..75189b51df 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -15,7 +15,8 @@
"typecheck": "tsc --noEmit",
"build": "tsc -p tsconfig.build.json",
"dev": "pnpm watch",
- "format": "prettier --write . --ignore-path ../../.prettierignore",
+ "format": "biome format --write .",
+ "format:check": "biome ci .",
"lint": "eslint . --quiet",
"lintfix": "eslint . --fix",
"watch": "tsc -p tsconfig.build.json --watch",
diff --git a/packages/core/src/WorkflowExecute.ts b/packages/core/src/WorkflowExecute.ts
index a7c282e434..fe4d60587b 100644
--- a/packages/core/src/WorkflowExecute.ts
+++ b/packages/core/src/WorkflowExecute.ts
@@ -1651,9 +1651,9 @@ export class WorkflowExecute {
// array as this shows that the parent nodes executed but they did not have any
// data to pass on.
const inputsWithData = this.runExecutionData
- .executionData!.waitingExecution[
- nodeName
- ][firstRunIndex].main.map((data, index) => (data === null ? null : index))
+ .executionData!.waitingExecution[nodeName][firstRunIndex].main.map((data, index) =>
+ data === null ? null : index,
+ )
.filter((data) => data !== null);
if (requiredInputs !== undefined) {
diff --git a/packages/design-system/.storybook/preview.js b/packages/design-system/.storybook/preview.js
index bb44f598ea..e39bc50436 100644
--- a/packages/design-system/.storybook/preview.js
+++ b/packages/design-system/.storybook/preview.js
@@ -8,7 +8,7 @@ import { library } from '@fortawesome/fontawesome-svg-core';
import { fas } from '@fortawesome/free-solid-svg-icons';
import ElementPlus from 'element-plus';
-import lang from 'element-plus/dist/locale/en.mjs'
+import lang from 'element-plus/dist/locale/en.mjs';
import { N8nPlugin } from '../src/plugin';
diff --git a/packages/design-system/biome.jsonc b/packages/design-system/biome.jsonc
new file mode 100644
index 0000000000..2c06b58894
--- /dev/null
+++ b/packages/design-system/biome.jsonc
@@ -0,0 +1,7 @@
+{
+ "$schema": "../../node_modules/@biomejs/biome/configuration_schema.json",
+ "extends": ["../../biome.jsonc"],
+ "formatter": {
+ "ignore": ["theme/**"]
+ }
+}
diff --git a/packages/design-system/chromatic.config.json b/packages/design-system/chromatic.config.json
index ce95f3e2fb..c37fc04267 100644
--- a/packages/design-system/chromatic.config.json
+++ b/packages/design-system/chromatic.config.json
@@ -1,4 +1,4 @@
{
- "projectId": "Project:65f085d72c13e4e1154414db",
- "buildScriptName": "build:storybook"
+ "projectId": "Project:65f085d72c13e4e1154414db",
+ "buildScriptName": "build:storybook"
}
diff --git a/packages/design-system/package.json b/packages/design-system/package.json
index 631bbd5dfc..d2cbb0678b 100644
--- a/packages/design-system/package.json
+++ b/packages/design-system/package.json
@@ -13,7 +13,8 @@
"build:storybook": "storybook build",
"storybook": "storybook dev -p 6006",
"chromatic": "chromatic",
- "format": "prettier --write . --ignore-path ../../.prettierignore",
+ "format": "biome format --write . && prettier --write . --ignore-path ../../.prettierignore",
+ "format:check": "biome ci . && prettier --check . --ignore-path ../../.prettierignore",
"lint": "eslint src --ext .js,.ts,.vue --quiet",
"lintfix": "eslint src --ext .js,.ts,.vue --fix"
},
diff --git a/packages/design-system/src/css/_tokens.dark.scss b/packages/design-system/src/css/_tokens.dark.scss
index 398a00e538..8377f524c6 100644
--- a/packages/design-system/src/css/_tokens.dark.scss
+++ b/packages/design-system/src/css/_tokens.dark.scss
@@ -37,7 +37,7 @@
--color-assistant-highlight-1: #8c90f2;
--color-assistant-highlight-2: #a977f0;
--color-assistant-highlight-3: #f0778b;
- --color-askAssistant-button-background: #2E2E2E;
+ --color-askAssistant-button-background: #2e2e2e;
--color-askAssistant-button-background-hover: #383839;
--color-askAssistant-button-background-active: #414244;
--color-assistant-inner-highlight-hover: var(--color-askAssistant-button-background-hover);
diff --git a/packages/design-system/src/css/_tokens.scss b/packages/design-system/src/css/_tokens.scss
index 3176b4e398..1690926232 100644
--- a/packages/design-system/src/css/_tokens.scss
+++ b/packages/design-system/src/css/_tokens.scss
@@ -80,11 +80,11 @@
--color-canvas-read-only-line: var(--prim-gray-30);
--color-canvas-selected: var(--prim-gray-70);
--color-canvas-selected-transparent: hsla(var(--prim-gray-h), 47%, 30%, 0.1);
- --color-canvas-label-background: hsla(
- var(--color-canvas-background-h),
- var(--color-canvas-background-s),
- var(--color-canvas-background-l),
- 0.85
+ --color-canvas-label-background: hsla(
+ var(--color-canvas-background-h),
+ var(--color-canvas-background-s),
+ var(--color-canvas-background-l),
+ 0.85
);
// Nodes
diff --git a/packages/design-system/tsconfig.json b/packages/design-system/tsconfig.json
index fd2232a2be..1b96b238c1 100644
--- a/packages/design-system/tsconfig.json
+++ b/packages/design-system/tsconfig.json
@@ -11,7 +11,12 @@
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"types": ["vitest/globals"],
- "typeRoots": ["./node_modules/@testing-library", "./node_modules/@types", "../../node_modules", "../../node_modules/@types"],
+ "typeRoots": [
+ "./node_modules/@testing-library",
+ "./node_modules/@types",
+ "../../node_modules",
+ "../../node_modules/@types"
+ ],
"paths": {
"n8n-design-system/*": ["./src/*"]
},
diff --git a/packages/design-system/vite.config.mts b/packages/design-system/vite.config.mts
index 59bab8aa74..783218b793 100644
--- a/packages/design-system/vite.config.mts
+++ b/packages/design-system/vite.config.mts
@@ -44,9 +44,9 @@ export default mergeConfig(
dts: false,
resolvers: [
iconsResolver({
- prefix: 'icon'
- })
- ]
+ prefix: 'icon',
+ }),
+ ],
}),
],
resolve: {
diff --git a/packages/editor-ui/biome.jsonc b/packages/editor-ui/biome.jsonc
new file mode 100644
index 0000000000..a8cf126857
--- /dev/null
+++ b/packages/editor-ui/biome.jsonc
@@ -0,0 +1,8 @@
+{
+ "$schema": "../../node_modules/@biomejs/biome/configuration_schema.json",
+ "extends": ["../../biome.jsonc"]
+ // Although nothing is extended here, it is required so biome use the
+ // editor-ui gitignore file:
+ // > For now, Biome only takes the ignore file in the working directory into account.
+ // https://biomejs.dev/guides/integrate-in-vcs/#use-the-ignore-file
+}
diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json
index 68c984fe5b..693529136d 100644
--- a/packages/editor-ui/package.json
+++ b/packages/editor-ui/package.json
@@ -10,7 +10,8 @@
"dev": "pnpm serve",
"lint": "eslint src --ext .js,.ts,.vue --quiet",
"lintfix": "eslint src --ext .js,.ts,.vue --fix",
- "format": "prettier --write . --ignore-path ../../.prettierignore",
+ "format": "biome format --write . && prettier --write . --ignore-path ../../.prettierignore",
+ "format:check": "biome ci . && prettier --check . --ignore-path ../../.prettierignore",
"serve": "cross-env VUE_APP_URL_BASE_API=http://localhost:5678/ vite --host 0.0.0.0 --port 8080 dev",
"test": "vitest run",
"test:dev": "vitest"
diff --git a/packages/editor-ui/src/components/CodeNodeEditor/theme.ts b/packages/editor-ui/src/components/CodeNodeEditor/theme.ts
index 0b6d923a97..e8e10aaad8 100644
--- a/packages/editor-ui/src/components/CodeNodeEditor/theme.ts
+++ b/packages/editor-ui/src/components/CodeNodeEditor/theme.ts
@@ -95,7 +95,7 @@ export const codeNodeEditorTheme = ({
: { minHeight: rows && rows !== -1 ? `${Number(rows + 1) * 1.3}em` : 'auto' }),
},
'.cm-gutter,.cm-content': {
- minHeight: rows && rows !== -1 ? 'auto' : minHeight ?? 'calc(35vh - var(--spacing-2xl))',
+ minHeight: rows && rows !== -1 ? 'auto' : (minHeight ?? 'calc(35vh - var(--spacing-2xl))'),
},
'.cm-diagnosticAction': {
backgroundColor: BASE_STYLING.diagnosticButton.backgroundColor,
diff --git a/packages/editor-ui/src/components/Node/NodeCreator/utils.ts b/packages/editor-ui/src/components/Node/NodeCreator/utils.ts
index 7bacf6b701..f8c1d1d3d2 100644
--- a/packages/editor-ui/src/components/Node/NodeCreator/utils.ts
+++ b/packages/editor-ui/src/components/Node/NodeCreator/utils.ts
@@ -142,7 +142,7 @@ export function groupItemsInSections(
title: section.title,
children: sortAlphabetically
? sortNodeCreateElements(children[section.key] ?? [])
- : children[section.key] ?? [],
+ : (children[section.key] ?? []),
}),
);
diff --git a/packages/editor-ui/src/composables/useExpressionEditor.ts b/packages/editor-ui/src/composables/useExpressionEditor.ts
index 416080a8ad..9b52589a7a 100644
--- a/packages/editor-ui/src/composables/useExpressionEditor.ts
+++ b/packages/editor-ui/src/composables/useExpressionEditor.ts
@@ -405,7 +405,8 @@ export const useExpressionEditor = ({
if (pos === 'lastExpression') {
const END_OF_EXPRESSION = ' }}';
const endOfLastExpression = readEditorValue().lastIndexOf(END_OF_EXPRESSION);
- pos = endOfLastExpression !== -1 ? endOfLastExpression : editor.value?.state.doc.length ?? 0;
+ pos =
+ endOfLastExpression !== -1 ? endOfLastExpression : (editor.value?.state.doc.length ?? 0);
} else if (pos === 'end') {
pos = editor.value?.state.doc.length ?? 0;
}
@@ -414,7 +415,7 @@ export const useExpressionEditor = ({
function select(anchor: number, head: number | 'end' = 'end'): void {
editor.value?.dispatch({
- selection: { anchor, head: head === 'end' ? editor.value?.state.doc.length ?? 0 : head },
+ selection: { anchor, head: head === 'end' ? (editor.value?.state.doc.length ?? 0) : head },
});
}
diff --git a/packages/editor-ui/src/plugins/connectors/N8nCustomConnector.ts b/packages/editor-ui/src/plugins/connectors/N8nCustomConnector.ts
index 07bd3609d0..6b41ab77a9 100644
--- a/packages/editor-ui/src/plugins/connectors/N8nCustomConnector.ts
+++ b/packages/editor-ui/src/plugins/connectors/N8nCustomConnector.ts
@@ -433,12 +433,14 @@ export class N8nConnector extends AbstractConnector {
const sourceStubWithOffset =
sourceStub +
(this.getEndpointOffset && params.sourceEndpoint
- ? this.getEndpointOffset(params.sourceEndpoint) ?? 0
+ ? (this.getEndpointOffset(params.sourceEndpoint) ?? 0)
: 0);
const targetStubWithOffset =
targetStub +
- (this.getEndpointOffset && targetEndpoint ? this.getEndpointOffset(targetEndpoint) ?? 0 : 0);
+ (this.getEndpointOffset && targetEndpoint
+ ? (this.getEndpointOffset(targetEndpoint) ?? 0)
+ : 0);
// same as paintinfo generated by jsplumb AbstractConnector type
const result = {
diff --git a/packages/editor-ui/src/plugins/i18n/index.ts b/packages/editor-ui/src/plugins/i18n/index.ts
index d461349f56..6c7372dce2 100644
--- a/packages/editor-ui/src/plugins/i18n/index.ts
+++ b/packages/editor-ui/src/plugins/i18n/index.ts
@@ -76,7 +76,7 @@ export class I18nClass {
* Render a string of dynamic text, i.e. a string with a constructed path to the localized value.
*/
private dynamicRender({ key, fallback }: { key: string; fallback?: string }) {
- return this.i18n.te(key) ? this.i18n.t(key).toString() : fallback ?? '';
+ return this.i18n.te(key) ? this.i18n.t(key).toString() : (fallback ?? '');
}
displayTimer(msPassed: number, showMs = false): string {
diff --git a/packages/editor-ui/src/router.ts b/packages/editor-ui/src/router.ts
index 6c6a68b22d..c304d6bd75 100644
--- a/packages/editor-ui/src/router.ts
+++ b/packages/editor-ui/src/router.ts
@@ -739,7 +739,7 @@ function withCanvasReadOnlyMeta(route: RouteRecordRaw) {
}
const router = createRouter({
- history: createWebHistory(import.meta.env.DEV ? '/' : window.BASE_PATH ?? '/'),
+ history: createWebHistory(import.meta.env.DEV ? '/' : (window.BASE_PATH ?? '/')),
scrollBehavior(to: RouteLocationNormalized, _, savedPosition) {
// saved position == null means the page is NOT visited from history (back button)
if (savedPosition === null && to.name === VIEWS.TEMPLATES && to.meta?.setScrollPosition) {
diff --git a/packages/editor-ui/src/stores/credentials.store.ts b/packages/editor-ui/src/stores/credentials.store.ts
index 663303572b..ec71aa5b29 100644
--- a/packages/editor-ui/src/stores/credentials.store.ts
+++ b/packages/editor-ui/src/stores/credentials.store.ts
@@ -189,7 +189,7 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, () => {
? email
? `${name} (${email})`
: name
- : email ?? i18n.baseText('credentialEdit.credentialSharing.info.sharee.fallback');
+ : (email ?? i18n.baseText('credentialEdit.credentialSharing.info.sharee.fallback'));
};
});
diff --git a/packages/editor-ui/src/stores/ndv.store.ts b/packages/editor-ui/src/stores/ndv.store.ts
index 1e7d5da363..e3be5db5fe 100644
--- a/packages/editor-ui/src/stores/ndv.store.ts
+++ b/packages/editor-ui/src/stores/ndv.store.ts
@@ -91,7 +91,7 @@ export const useNDVStore = defineStore(STORES.NDV, {
ndvInputDataWithPinnedData(): INodeExecutionData[] {
const data = this.ndvInputData;
return this.ndvInputNodeName
- ? useWorkflowsStore().pinDataByNodeName(this.ndvInputNodeName) ?? data
+ ? (useWorkflowsStore().pinDataByNodeName(this.ndvInputNodeName) ?? data)
: data;
},
hasInputData(): boolean {
diff --git a/packages/editor-ui/src/stores/workflows.ee.store.ts b/packages/editor-ui/src/stores/workflows.ee.store.ts
index 18bb81cdf2..de3589f9fe 100644
--- a/packages/editor-ui/src/stores/workflows.ee.store.ts
+++ b/packages/editor-ui/src/stores/workflows.ee.store.ts
@@ -21,7 +21,7 @@ export const useWorkflowsEEStore = defineStore(STORES.WORKFLOWS_EE, {
const workflow = useWorkflowsStore().getWorkflowById(workflowId);
const { name, email } = splitName(workflow?.homeProject?.name ?? '');
- return name ? (email ? `${name} (${email})` : name) : email ?? fallback;
+ return name ? (email ? `${name} (${email})` : name) : (email ?? fallback);
};
},
},
diff --git a/packages/editor-ui/src/styles/plugins/index.scss b/packages/editor-ui/src/styles/plugins/index.scss
index c5d099d5d1..9914829eea 100644
--- a/packages/editor-ui/src/styles/plugins/index.scss
+++ b/packages/editor-ui/src/styles/plugins/index.scss
@@ -1,2 +1,2 @@
-@import "codemirror";
-@import "vueflow";
+@import 'codemirror';
+@import 'vueflow';
diff --git a/packages/editor-ui/src/types/externalHooks.ts b/packages/editor-ui/src/types/externalHooks.ts
index 005762e759..d2c8b01821 100644
--- a/packages/editor-ui/src/types/externalHooks.ts
+++ b/packages/editor-ui/src/types/externalHooks.ts
@@ -302,8 +302,10 @@ export type ExternalHooksKey = {
[K in keyof ExternalHooks]: `${K}.${Extract}`;
}[keyof ExternalHooks];
-type ExtractHookMethodArray =
- ExternalHooks[P][S] extends Array ? U : never;
+type ExtractHookMethodArray<
+ P extends keyof ExternalHooks,
+ S extends keyof ExternalHooks[P],
+> = ExternalHooks[P][S] extends Array ? U : never;
type ExtractHookMethodFunction = T extends ExternalHooksMethod ? T : never;
diff --git a/packages/editor-ui/vite.config.mts b/packages/editor-ui/vite.config.mts
index 9fc8b15718..f6824d31a5 100644
--- a/packages/editor-ui/vite.config.mts
+++ b/packages/editor-ui/vite.config.mts
@@ -6,7 +6,7 @@ import { sentryVitePlugin } from '@sentry/vite-plugin';
import packageJSON from './package.json';
import { vitestConfig } from '../design-system/vite.config.mts';
import icons from 'unplugin-icons/vite';
-import iconsResolver from 'unplugin-icons/resolver'
+import iconsResolver from 'unplugin-icons/resolver';
import components from 'unplugin-vue-components/vite';
const vendorChunks = ['vue', 'vue-router'];
@@ -80,9 +80,9 @@ const plugins = [
dts: './src/components.d.ts',
resolvers: [
iconsResolver({
- prefix: 'icon'
- })
- ]
+ prefix: 'icon',
+ }),
+ ],
}),
vue(),
];
diff --git a/packages/node-dev/package.json b/packages/node-dev/package.json
index 0b538e613a..4bcfd04d6c 100644
--- a/packages/node-dev/package.json
+++ b/packages/node-dev/package.json
@@ -13,7 +13,8 @@
"dev": "pnpm watch",
"build": "tsc --noEmit",
"build-node-dev": "tsc",
- "format": "prettier --write . --ignore-path ../../.prettierignore",
+ "format": "biome format --write .",
+ "format:check": "biome ci .",
"lint": "eslint . --quiet",
"lintfix": "eslint . --fix",
"prepack": "echo \"Building project...\" && rm -rf dist && tsc -b",
diff --git a/packages/nodes-base/biome.jsonc b/packages/nodes-base/biome.jsonc
new file mode 100644
index 0000000000..6f4325bafd
--- /dev/null
+++ b/packages/nodes-base/biome.jsonc
@@ -0,0 +1,7 @@
+{
+ "$schema": "../../node_modules/@biomejs/biome/configuration_schema.json",
+ "extends": ["../../biome.jsonc"],
+ "formatter": {
+ "ignore": ["nodes/**/test/*.json"]
+ }
+}
diff --git a/packages/nodes-base/nodes/Baserow/GenericFunctions.ts b/packages/nodes-base/nodes/Baserow/GenericFunctions.ts
index d08d0c2f5e..ba33755e2f 100644
--- a/packages/nodes-base/nodes/Baserow/GenericFunctions.ts
+++ b/packages/nodes-base/nodes/Baserow/GenericFunctions.ts
@@ -169,7 +169,7 @@ export class TableFieldMapper {
}
setField(field: string) {
- return this.mapIds ? field : this.nameToIdMapping[field] ?? field;
+ return this.mapIds ? field : (this.nameToIdMapping[field] ?? field);
}
idsToNames(obj: Record) {
diff --git a/packages/nodes-base/nodes/Brevo/GenericFunctions.ts b/packages/nodes-base/nodes/Brevo/GenericFunctions.ts
index d43f3e8bfd..4b5da056c1 100644
--- a/packages/nodes-base/nodes/Brevo/GenericFunctions.ts
+++ b/packages/nodes-base/nodes/Brevo/GenericFunctions.ts
@@ -19,15 +19,15 @@ export namespace BrevoNode {
type ValidatedEmail = ToEmail | SenderEmail | CCEmail | BBCEmail;
const enum OVERRIDE_MAP_VALUES {
- 'CATEGORY' = 'category',
- 'NORMAL' = 'boolean',
- 'TRANSACTIONAL' = 'id',
+ CATEGORY = 'category',
+ NORMAL = 'boolean',
+ TRANSACTIONAL = 'id',
}
const enum OVERRIDE_MAP_TYPE {
- 'CATEGORY' = 'category',
- 'NORMAL' = 'normal',
- 'TRANSACTIONAL' = 'transactional',
+ CATEGORY = 'category',
+ NORMAL = 'normal',
+ TRANSACTIONAL = 'transactional',
}
export const INTERCEPTORS = new Map void>([
diff --git a/packages/nodes-base/nodes/Cortex/AnalyzerInterface.ts b/packages/nodes-base/nodes/Cortex/AnalyzerInterface.ts
index 5f9f8fe827..a20948d01b 100644
--- a/packages/nodes-base/nodes/Cortex/AnalyzerInterface.ts
+++ b/packages/nodes-base/nodes/Cortex/AnalyzerInterface.ts
@@ -16,19 +16,19 @@ export const enum TLP {
}
export const enum ObservableDataType {
- 'domain' = 'domain',
- 'file' = 'file',
- 'filename' = 'filename',
- 'fqdn' = 'fqdn',
- 'hash' = 'hash',
- 'ip' = 'ip',
- 'mail' = 'mail',
- 'mail_subject' = 'mail_subject',
- 'other' = 'other',
- 'regexp' = 'regexp',
- 'registry' = 'registry',
- 'uri_path' = 'uri_path',
- 'url' = 'url',
+ domain = 'domain',
+ file = 'file',
+ filename = 'filename',
+ fqdn = 'fqdn',
+ hash = 'hash',
+ ip = 'ip',
+ mail = 'mail',
+ mail_subject = 'mail_subject',
+ other = 'other',
+ regexp = 'regexp',
+ registry = 'registry',
+ uri_path = 'uri_path',
+ url = 'url',
'user-agent' = 'user-agent',
}
export interface IJob {
diff --git a/packages/nodes-base/nodes/Google/Chat/MessageInterface.ts b/packages/nodes-base/nodes/Google/Chat/MessageInterface.ts
index a03ab9f130..b2193f9820 100644
--- a/packages/nodes-base/nodes/Google/Chat/MessageInterface.ts
+++ b/packages/nodes-base/nodes/Google/Chat/MessageInterface.ts
@@ -32,9 +32,9 @@ export interface IUser {
isAnonymous?: boolean;
}
const enum Type {
- 'TYPE_UNSPECIFIED',
- 'HUMAN',
- 'BOT',
+ TYPE_UNSPECIFIED,
+ HUMAN,
+ BOT,
}
// // TODO: define other interfaces
diff --git a/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts b/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts
index fa96649464..c5f8a8dc79 100644
--- a/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts
+++ b/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts
@@ -485,8 +485,7 @@ export class GoogleSheet {
columnValuesList?: string[];
}) {
const decodedRange = this.getDecodedSheetRange(range);
- // prettier-ignore
- const keyRowRange = `${decodedRange.name}!${decodedRange.start?.column || ''}${keyRowIndex + 1}:${decodedRange.end?.column || ''}${keyRowIndex + 1}`;
+ const keyRowRange = `${decodedRange.name}!${decodedRange.start?.column || ''}${keyRowIndex + 1}:${decodedRange.end?.column || ''}${keyRowIndex + 1}`;
const sheetDatakeyRow = columnNamesList || (await this.getData(keyRowRange, valueRenderMode));
diff --git a/packages/nodes-base/nodes/Google/Sheet/v2/methods/listSearch.ts b/packages/nodes-base/nodes/Google/Sheet/v2/methods/listSearch.ts
index 36f18521e4..6c0c8b74ca 100644
--- a/packages/nodes-base/nodes/Google/Sheet/v2/methods/listSearch.ts
+++ b/packages/nodes-base/nodes/Google/Sheet/v2/methods/listSearch.ts
@@ -84,7 +84,6 @@ export async function sheetsSearch(
returnData.push({
name: sheet.properties!.title as string,
value: (sheet.properties!.sheetId as number) || 'gid=0',
- //prettier-ignore
url: `https://docs.google.com/spreadsheets/d/${spreadsheetId}/edit#gid=${sheet.properties!.sheetId}`,
});
}
diff --git a/packages/nodes-base/nodes/KoBoToolbox/KoBoToolboxTrigger.node.ts b/packages/nodes-base/nodes/KoBoToolbox/KoBoToolboxTrigger.node.ts
index 436f9d86a7..503c100741 100644
--- a/packages/nodes-base/nodes/KoBoToolbox/KoBoToolboxTrigger.node.ts
+++ b/packages/nodes-base/nodes/KoBoToolbox/KoBoToolboxTrigger.node.ts
@@ -144,9 +144,12 @@ export class KoBoToolboxTrigger implements INodeType {
const req = this.getRequestObject();
const formatOptions = this.getNodeParameter('formatOptions') as IDataObject;
- // prettier-ignore
const responseData = formatOptions.reformat
- ? formatSubmission(req.body as IDataObject, parseStringList(formatOptions.selectMask as string), parseStringList(formatOptions.numberMask as string))
+ ? formatSubmission(
+ req.body as IDataObject,
+ parseStringList(formatOptions.selectMask as string),
+ parseStringList(formatOptions.numberMask as string),
+ )
: req.body;
if (formatOptions.download) {
diff --git a/packages/nodes-base/nodes/Notion/shared/GenericFunctions.ts b/packages/nodes-base/nodes/Notion/shared/GenericFunctions.ts
index af27d2c4cb..1b6ba6563d 100644
--- a/packages/nodes-base/nodes/Notion/shared/GenericFunctions.ts
+++ b/packages/nodes-base/nodes/Notion/shared/GenericFunctions.ts
@@ -27,7 +27,6 @@ import { filters } from './descriptions/Filters';
function uuidValidateWithoutDashes(this: IExecuteFunctions, value: string) {
if (uuidValidate(value)) return true;
if (value.length == 32) {
- //prettier-ignore
const strWithDashes = `${value.slice(0, 8)}-${value.slice(8, 12)}-${value.slice(12, 16)}-${value.slice(16, 20)}-${value.slice(20)}`;
if (uuidValidate(strWithDashes)) return true;
}
@@ -309,7 +308,6 @@ export function formatBlocks(blocks: IDataObject[]) {
[block.type as string]: {
...(block.type === 'to_do' ? { checked: block.checked } : {}),
...(block.type === 'image' ? { type: 'external', external: { url: block.url } } : {}),
- // prettier-ignore,
...(!['image'].includes(block.type as string) ? getTextBlocks(block) : {}),
},
});
@@ -867,9 +865,12 @@ export type FileRecord = {
};
};
};
-// prettier-ignore
-export async function downloadFiles(this: IExecuteFunctions | IPollFunctions, records: FileRecord[], pairedItem?: IPairedItemData[]): Promise {
+export async function downloadFiles(
+ this: IExecuteFunctions | IPollFunctions,
+ records: FileRecord[],
+ pairedItem?: IPairedItemData[],
+): Promise {
const elements: INodeExecutionData[] = [];
for (const record of records) {
const element: INodeExecutionData = { json: {}, binary: {} };
@@ -887,10 +888,12 @@ export async function downloadFiles(this: IExecuteFunctions | IPollFunctions, re
'',
{},
{},
- file?.file?.url as string || file?.external?.url as string,
+ (file?.file?.url as string) || (file?.external?.url as string),
{ json: false, encoding: null },
);
- element.binary![`${key}_${index}`] = await this.helpers.prepareBinaryData(data as Buffer);
+ element.binary![`${key}_${index}`] = await this.helpers.prepareBinaryData(
+ data as Buffer,
+ );
}
}
}
diff --git a/packages/nodes-base/nodes/Postgres/v1/genericFunctions.ts b/packages/nodes-base/nodes/Postgres/v1/genericFunctions.ts
index 9420c427a1..834779d1a0 100644
--- a/packages/nodes-base/nodes/Postgres/v1/genericFunctions.ts
+++ b/packages/nodes-base/nodes/Postgres/v1/genericFunctions.ts
@@ -536,8 +536,10 @@ export async function pgUpdate(
} else {
const where =
' WHERE ' +
- // eslint-disable-next-line n8n-local-rules/no-interpolation-in-regular-string
- updateKeys.map((entry) => pgp.as.name(entry.name) + ' = ${' + entry.prop + '}').join(' AND ');
+ updateKeys
+ // eslint-disable-next-line n8n-local-rules/no-interpolation-in-regular-string
+ .map((entry) => pgp.as.name(entry.name) + ' = ${' + entry.prop + '}')
+ .join(' AND ');
if (mode === 'transaction') {
return await db.tx(async (t) => {
const result: IDataObject[] = [];
@@ -664,8 +666,10 @@ export async function pgUpdateV2(
} else {
const where =
' WHERE ' +
- // eslint-disable-next-line n8n-local-rules/no-interpolation-in-regular-string
- updateKeys.map((entry) => pgp.as.name(entry.name) + ' = ${' + entry.prop + '}').join(' AND ');
+ updateKeys
+ // eslint-disable-next-line n8n-local-rules/no-interpolation-in-regular-string
+ .map((entry) => pgp.as.name(entry.name) + ' = ${' + entry.prop + '}')
+ .join(' AND ');
if (mode === 'transaction') {
return await db.tx(async (t) => {
const result: IDataObject[] = [];
diff --git a/packages/nodes-base/nodes/QuickBase/GenericFunctions.ts b/packages/nodes-base/nodes/QuickBase/GenericFunctions.ts
index 73d8232aa6..0f2cbee6db 100644
--- a/packages/nodes-base/nodes/QuickBase/GenericFunctions.ts
+++ b/packages/nodes-base/nodes/QuickBase/GenericFunctions.ts
@@ -62,9 +62,10 @@ export async function quickbaseApiRequest(
}
}
-//@ts-ignore
-// prettier-ignore
-export async function getFieldsObject(this: IHookFunctions | ILoadOptionsFunctions | IExecuteFunctions, tableId: string): any {
+export async function getFieldsObject(
+ this: IHookFunctions | ILoadOptionsFunctions | IExecuteFunctions,
+ tableId: string,
+): Promise {
const fieldsLabelKey: { [key: string]: number } = {};
const fieldsIdKey: { [key: number]: string } = {};
const data = await quickbaseApiRequest.call(this, 'GET', '/fields', {}, { tableId });
diff --git a/packages/nodes-base/nodes/Telegram/GenericFunctions.ts b/packages/nodes-base/nodes/Telegram/GenericFunctions.ts
index cbffe21d5f..309b23c176 100644
--- a/packages/nodes-base/nodes/Telegram/GenericFunctions.ts
+++ b/packages/nodes-base/nodes/Telegram/GenericFunctions.ts
@@ -158,9 +158,12 @@ export function addAdditionalFields(
}
sendRows.push(sendButtonData);
}
+
// @ts-ignore
- // prettier-ignore
- ((body.reply_markup as ITelegramInlineReply | ITelegramReplyKeyboard)[setParameterName] as ITelegramKeyboardButton[][]).push(sendRows);
+ const array = (body.reply_markup as ITelegramInlineReply | ITelegramReplyKeyboard)[
+ setParameterName
+ ] as ITelegramKeyboardButton[][];
+ array.push(sendRows);
}
}
} else if (replyMarkupOption === 'forceReply') {
diff --git a/packages/nodes-base/nodes/TheHive/interfaces/ObservableInterface.ts b/packages/nodes-base/nodes/TheHive/interfaces/ObservableInterface.ts
index cb35df2189..da792ba07a 100644
--- a/packages/nodes-base/nodes/TheHive/interfaces/ObservableInterface.ts
+++ b/packages/nodes-base/nodes/TheHive/interfaces/ObservableInterface.ts
@@ -5,19 +5,19 @@ export const enum ObservableStatus {
DELETED = 'Deleted',
}
export const enum ObservableDataType {
- 'domain' = 'domain',
- 'file' = 'file',
- 'filename' = 'filename',
- 'fqdn' = 'fqdn',
- 'hash' = 'hash',
- 'ip' = 'ip',
- 'mail' = 'mail',
- 'mail_subject' = 'mail_subject',
- 'other' = 'other',
- 'regexp' = 'regexp',
- 'registry' = 'registry',
- 'uri_path' = 'uri_path',
- 'url' = 'url',
+ domain = 'domain',
+ file = 'file',
+ filename = 'filename',
+ fqdn = 'fqdn',
+ hash = 'hash',
+ ip = 'ip',
+ mail = 'mail',
+ mail_subject = 'mail_subject',
+ other = 'other',
+ regexp = 'regexp',
+ registry = 'registry',
+ uri_path = 'uri_path',
+ url = 'url',
'user-agent' = 'user-agent',
}
diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json
index d1a16ef2c6..053eb4abeb 100644
--- a/packages/nodes-base/package.json
+++ b/packages/nodes-base/package.json
@@ -9,7 +9,8 @@
"typecheck": "tsc --noEmit",
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && pnpm n8n-copy-icons && pnpm n8n-generate-translations && pnpm build:metadata",
"build:metadata": "pnpm n8n-generate-known && pnpm n8n-generate-ui-types",
- "format": "prettier --write . --ignore-path ../../.prettierignore",
+ "format": "biome format --write .",
+ "format:check": "biome ci .",
"lint": "eslint . --quiet && node ./scripts/validate-load-options-methods.js",
"lintfix": "eslint . --fix",
"watch": "tsc-watch -p tsconfig.build.json --onCompilationComplete \"tsc-alias -p tsconfig.build.json\" --onSuccess \"pnpm n8n-generate-ui-types\"",
diff --git a/packages/workflow/package.json b/packages/workflow/package.json
index 723994f917..be6eae2ba6 100644
--- a/packages/workflow/package.json
+++ b/packages/workflow/package.json
@@ -18,7 +18,8 @@
"dev": "pnpm watch",
"typecheck": "tsc --noEmit",
"build": "tsc -p tsconfig.build.json",
- "format": "prettier --write . --ignore-path ../../.prettierignore",
+ "format": "biome format --write .",
+ "format:check": "biome ci .",
"lint": "eslint . --quiet",
"lintfix": "eslint . --fix",
"watch": "tsc -p tsconfig.build.json --watch",
diff --git a/packages/workflow/test/ExpressionExtensions/ExpressionExtension.test.ts b/packages/workflow/test/ExpressionExtensions/ExpressionExtension.test.ts
index b35cd2c728..15980c67e2 100644
--- a/packages/workflow/test/ExpressionExtensions/ExpressionExtension.test.ts
+++ b/packages/workflow/test/ExpressionExtensions/ExpressionExtension.test.ts
@@ -157,13 +157,11 @@ describe('tmpl Expression Parser', () => {
});
test('Multiple optional chains in an expression', () => {
- expect(extendTransform('$json.test?.test2($json.test?.test2)')?.code)
- .toBe(`window.chainCancelToken2 = ((window.chainValue2 = $json.test) ?? undefined) === undefined, window.chainCancelToken2 === true ? undefined : window.chainValue2.test2(
+ expect(extendTransform('$json.test?.test2($json.test?.test2)')?.code).toBe(`window.chainCancelToken2 = ((window.chainValue2 = $json.test) ?? undefined) === undefined, window.chainCancelToken2 === true ? undefined : window.chainValue2.test2(
(window.chainCancelToken1 = ((window.chainValue1 = $json.test) ?? undefined) === undefined, window.chainCancelToken1 === true ? undefined : window.chainValue1.test2)
);`);
- expect(extendTransform('$json.test?.test2($json.test.sum?.())')?.code)
- .toBe(`window.chainCancelToken2 = ((window.chainValue2 = $json.test) ?? undefined) === undefined, window.chainCancelToken2 === true ? undefined : window.chainValue2.test2(
+ expect(extendTransform('$json.test?.test2($json.test.sum?.())')?.code).toBe(`window.chainCancelToken2 = ((window.chainValue2 = $json.test) ?? undefined) === undefined, window.chainCancelToken2 === true ? undefined : window.chainValue2.test2(
(window.chainCancelToken1 = ((window.chainValue1 = extendOptional($json.test, "sum")) ?? undefined) === undefined, window.chainCancelToken1 === true ? undefined : window.chainValue1())
);`);
});
diff --git a/packages/workflow/test/RoutingNode.test.ts b/packages/workflow/test/RoutingNode.test.ts
index e2638d8c90..16f9735d37 100644
--- a/packages/workflow/test/RoutingNode.test.ts
+++ b/packages/workflow/test/RoutingNode.test.ts
@@ -1959,7 +1959,7 @@ describe('RoutingNode', () => {
executeSingleFunctions.getNodeParameter = (parameterName: string) =>
parameterName in testData.input.node.parameters
? testData.input.node.parameters[parameterName]
- : getNodeParameter(parameterName) ?? {};
+ : (getNodeParameter(parameterName) ?? {});
const result = await routingNode.runNode(
inputData,
diff --git a/packages/workflow/test/fixtures/WorkflowDataProxy/pindata_workflow.json b/packages/workflow/test/fixtures/WorkflowDataProxy/pindata_workflow.json
index 543bdb814b..0941baaeb0 100644
--- a/packages/workflow/test/fixtures/WorkflowDataProxy/pindata_workflow.json
+++ b/packages/workflow/test/fixtures/WorkflowDataProxy/pindata_workflow.json
@@ -1,106 +1,97 @@
{
- "meta": {
- "instanceId": "a786b722078489c1fa382391a9f3476c2784761624deb2dfb4634827256d51a0"
- },
- "nodes": [
- {
- "parameters": {
- "assignments": {
- "assignments": [
- {
- "id": "3058c300-b377-41b7-9c90-a01372f9b581",
- "name": "firstName",
- "value": "Joe",
- "type": "string"
- },
- {
- "id": "bb871662-c23c-4234-ac0c-b78c279bbf34",
- "name": "lastName",
- "value": "Smith",
- "type": "string"
- }
- ]
- },
- "options": {}
- },
- "id": "baee2bf4-5083-4cbe-8e51-4eddcf859ef5",
- "name": "PinnedSet",
- "type": "n8n-nodes-base.set",
- "typeVersion": 3.3,
- "position": [
- 1120,
- 380
- ]
- },
- {
- "parameters": {
- "assignments": {
- "assignments": [
- {
- "id": "a482f1fd-4815-4da4-a733-7beafb43c500",
- "name": "test",
- "value": "={{ $('PinnedSet').all().json }}\n{{ $('PinnedSet').item.json.firstName }}\n{{ $('PinnedSet').first().json.firstName }}\n{{ $('PinnedSet').itemMatching(0).json.firstName }}\n{{ $('PinnedSet').itemMatching(1).json.firstName }}\n{{ $('PinnedSet').last().json.firstName }}\n{{ $('PinnedSet').all()[0].json.firstName }}\n{{ $('PinnedSet').all()[1].json.firstName }}\n\n{{ $input.first().json.firstName }}\n{{ $input.last().json.firstName }}\n{{ $input.item.json.firstName }}\n\n{{ $json.firstName }}\n{{ $data.firstName }}\n\n{{ $items()[0].json.firstName }}",
- "type": "string"
- }
- ]
- },
- "options": {}
- },
- "id": "2a543169-e2c1-4764-ac63-09534310b2b9",
- "name": "NotPinnedSet1",
- "type": "n8n-nodes-base.set",
- "typeVersion": 3.3,
- "position": [
- 1360,
- 380
- ]
- },
- {
- "parameters": {},
- "id": "f36672e5-8c87-480e-a5b8-de9da6b63192",
- "name": "Start",
- "type": "n8n-nodes-base.manualTrigger",
- "position": [
- 920,
- 380
- ],
- "typeVersion": 1
- }
- ],
- "connections": {
- "PinnedSet": {
- "main": [
- [
- {
- "node": "NotPinnedSet1",
- "type": "main",
- "index": 0
- }
- ]
- ]
- },
- "Start": {
- "main": [
- [
- {
- "node": "PinnedSet",
- "type": "main",
- "index": 0
- }
- ]
- ]
- }
- },
- "pinData": {
- "PinnedSet": [
- {
- "firstName": "Joe",
- "lastName": "Smith"
- },
- {
- "firstName": "Joan",
- "lastName": "Summers"
- }
- ]
- }
+ "meta": {
+ "instanceId": "a786b722078489c1fa382391a9f3476c2784761624deb2dfb4634827256d51a0"
+ },
+ "nodes": [
+ {
+ "parameters": {
+ "assignments": {
+ "assignments": [
+ {
+ "id": "3058c300-b377-41b7-9c90-a01372f9b581",
+ "name": "firstName",
+ "value": "Joe",
+ "type": "string"
+ },
+ {
+ "id": "bb871662-c23c-4234-ac0c-b78c279bbf34",
+ "name": "lastName",
+ "value": "Smith",
+ "type": "string"
+ }
+ ]
+ },
+ "options": {}
+ },
+ "id": "baee2bf4-5083-4cbe-8e51-4eddcf859ef5",
+ "name": "PinnedSet",
+ "type": "n8n-nodes-base.set",
+ "typeVersion": 3.3,
+ "position": [1120, 380]
+ },
+ {
+ "parameters": {
+ "assignments": {
+ "assignments": [
+ {
+ "id": "a482f1fd-4815-4da4-a733-7beafb43c500",
+ "name": "test",
+ "value": "={{ $('PinnedSet').all().json }}\n{{ $('PinnedSet').item.json.firstName }}\n{{ $('PinnedSet').first().json.firstName }}\n{{ $('PinnedSet').itemMatching(0).json.firstName }}\n{{ $('PinnedSet').itemMatching(1).json.firstName }}\n{{ $('PinnedSet').last().json.firstName }}\n{{ $('PinnedSet').all()[0].json.firstName }}\n{{ $('PinnedSet').all()[1].json.firstName }}\n\n{{ $input.first().json.firstName }}\n{{ $input.last().json.firstName }}\n{{ $input.item.json.firstName }}\n\n{{ $json.firstName }}\n{{ $data.firstName }}\n\n{{ $items()[0].json.firstName }}",
+ "type": "string"
+ }
+ ]
+ },
+ "options": {}
+ },
+ "id": "2a543169-e2c1-4764-ac63-09534310b2b9",
+ "name": "NotPinnedSet1",
+ "type": "n8n-nodes-base.set",
+ "typeVersion": 3.3,
+ "position": [1360, 380]
+ },
+ {
+ "parameters": {},
+ "id": "f36672e5-8c87-480e-a5b8-de9da6b63192",
+ "name": "Start",
+ "type": "n8n-nodes-base.manualTrigger",
+ "position": [920, 380],
+ "typeVersion": 1
+ }
+ ],
+ "connections": {
+ "PinnedSet": {
+ "main": [
+ [
+ {
+ "node": "NotPinnedSet1",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Start": {
+ "main": [
+ [
+ {
+ "node": "PinnedSet",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ }
+ },
+ "pinData": {
+ "PinnedSet": [
+ {
+ "firstName": "Joe",
+ "lastName": "Smith"
+ },
+ {
+ "firstName": "Joan",
+ "lastName": "Summers"
+ }
+ ]
+ }
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 131970bbf7..2c1cfd13ba 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -123,6 +123,9 @@ importers:
.:
devDependencies:
+ '@biomejs/biome':
+ specifier: ^1.9.0
+ version: 1.9.0
'@n8n_io/eslint-config':
specifier: workspace:*
version: link:packages/@n8n_io/eslint-config
@@ -147,6 +150,9 @@ importers:
jest-mock-extended:
specifier: ^3.0.4
version: 3.0.4(jest@29.6.2(@types/node@18.16.16)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.6.2)))(typescript@5.6.2)
+ lefthook:
+ specifier: ^1.7.15
+ version: 1.7.15
nock:
specifier: ^13.3.2
version: 13.3.2
@@ -641,9 +647,6 @@ importers:
eslint-plugin-n8n-local-rules:
specifier: ^1.0.0
version: 1.0.0
- eslint-plugin-prettier:
- specifier: ^5.1.3
- version: 5.1.3(@types/eslint@8.56.5)(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.2.5)
eslint-plugin-unicorn:
specifier: ^51.0.1
version: 51.0.1(eslint@8.57.0)
@@ -1853,7 +1856,7 @@ importers:
devDependencies:
'@langchain/core':
specifier: 'catalog:'
- version: 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0)
+ version: 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0(zod@3.23.8)))(openai@4.58.0(zod@3.23.8))
'@types/deep-equal':
specifier: ^1.0.1
version: 1.0.1
@@ -3137,6 +3140,59 @@ packages:
'@bcoe/v8-coverage@0.2.3':
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
+ '@biomejs/biome@1.9.0':
+ resolution: {integrity: sha512-NlWh2F1wbxB3O/wE+aohGL0BziTS6e+6+dyFvpdeqLsbQZY7EsiklFb9W5Xs41U4vEmY7ANgdNp+oVDij6sQdA==}
+ engines: {node: '>=14.21.3'}
+ hasBin: true
+
+ '@biomejs/cli-darwin-arm64@1.9.0':
+ resolution: {integrity: sha512-2w9v/NRtYSmodx5QWQ49OGcyGKSECdWKbzc7n532Iq5sBhkKk996fd19icT6BuL54f01KFKRCRibAW+A2rg1Kw==}
+ engines: {node: '>=14.21.3'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@biomejs/cli-darwin-x64@1.9.0':
+ resolution: {integrity: sha512-fBVt8jJQi0zX0SJ1C+tdzUbRpuX/07sgtBXEhunWRkPjdi6W/2S1sYHQ1wKn4OKiRAKfHM2Cf2FNO7hQvY61dA==}
+ engines: {node: '>=14.21.3'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@biomejs/cli-linux-arm64-musl@1.9.0':
+ resolution: {integrity: sha512-Jy84mZ4vcppdmWMgQWOCfd8qIVC/vHmlaS5gy7GXkdWlBKSQ56YxEXTU58MHTbZ16LwJQpK2IulqRCC/rqWLBA==}
+ engines: {node: '>=14.21.3'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@biomejs/cli-linux-arm64@1.9.0':
+ resolution: {integrity: sha512-l8U2lcqsl9yKPP5WUdIrKH//C1pWyM2cSUfcTBn6GSvXmsSjBNEdGSdM4Wfne777Oe/9ONaD1Ga53U2HksHHLw==}
+ engines: {node: '>=14.21.3'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@biomejs/cli-linux-x64-musl@1.9.0':
+ resolution: {integrity: sha512-N3enoFoIrkB6qJWyYfTiYmFdB1R/Mrij1dd1xBHqxxCKZY9GRkEswRX3F1Uqzo5T+9Iu8nAQobDqI/ygicYy/Q==}
+ engines: {node: '>=14.21.3'}
+ cpu: [x64]
+ os: [linux]
+
+ '@biomejs/cli-linux-x64@1.9.0':
+ resolution: {integrity: sha512-8jAzjrrJTj510pwq4aVs7ZKkOvEy1D+nzl9DKvrPh4TOyUw5Ie+0EDwXGE2RAkCKHkGNOQBZ78WtIdsATgz5sA==}
+ engines: {node: '>=14.21.3'}
+ cpu: [x64]
+ os: [linux]
+
+ '@biomejs/cli-win32-arm64@1.9.0':
+ resolution: {integrity: sha512-AIjwJTGfdWGMRluSQ9pDB29nzce077dfHh0/HMqzztKzgD3spyuo2R9VoaFpbR0hLHPWEH6g6OxxDO7hfkXNkQ==}
+ engines: {node: '>=14.21.3'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@biomejs/cli-win32-x64@1.9.0':
+ resolution: {integrity: sha512-4/4wTjNSoyNkm1SzcUaStDx46baX1VJRXtUoeEHjX9LfedR5N3qwZz5KfrRUnCd2fl5bmXK1CwMqKBkoF6zEiA==}
+ engines: {node: '>=14.21.3'}
+ cpu: [x64]
+ os: [win32]
+
'@chromatic-com/storybook@1.5.0':
resolution: {integrity: sha512-LkLKv7SWu/6kGep1ft2HA1T/cm14wU0zoW71gE4cZRcgUoRQJtyhITFTLHrjqAxz6bVqNgqzQtd5oBZ2nK3L3g==}
engines: {node: '>=16.0.0', yarn: '>=1.22.18'}
@@ -9956,6 +10012,60 @@ packages:
leac@0.6.0:
resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==}
+ lefthook-darwin-arm64@1.7.15:
+ resolution: {integrity: sha512-o8JgCnLM7UgF9g0MwarHJFoj6aVSSkUInHpsQZegV1c7CVQY/LIXgSeAWRb9XBvuUjByJ/HiHFMp9/hAALTwxQ==}
+ cpu: [arm64]
+ os: [darwin]
+
+ lefthook-darwin-x64@1.7.15:
+ resolution: {integrity: sha512-nrdoex0icmXYl7AAvG7WtfEzjQtX/kWvM88jPu/gijH6VhAjp110Y8VScB7rWOcApb5kNNeqd1nKMAFgZ8KhAg==}
+ cpu: [x64]
+ os: [darwin]
+
+ lefthook-freebsd-arm64@1.7.15:
+ resolution: {integrity: sha512-zl1TPynklJZZ/YsWb2H0gfErQbey318i2W85wIKGEk8kC2UzOgYTuPffnfi7kANei9ntZnhvGgilc6tqYOPuGQ==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ lefthook-freebsd-x64@1.7.15:
+ resolution: {integrity: sha512-/gKPwhWhZ3Q/efGs73/qw6nyR4WCT74oSTgn8wabAJO5+T/2FXTKzo7IiKkQmScmY5OcnD/0teJbJZ17VWSSOg==}
+ cpu: [x64]
+ os: [freebsd]
+
+ lefthook-linux-arm64@1.7.15:
+ resolution: {integrity: sha512-re2f8WmYw19n8ojit0rnKbERAzD1/iCpU/Y8anXOjD/ROw/cpSfO88uKQrUCNY9Rp4XLtkkp9oDJs3Eg7JS7vA==}
+ cpu: [arm64]
+ os: [linux]
+
+ lefthook-linux-x64@1.7.15:
+ resolution: {integrity: sha512-Kj6ieTlhFGlbPDyVFeOb296MS9x/Jj5y/xTPLBM+EKbdsTJSvUYu5FdtEWfhpLyWmPfkZtULHcTQE1hoo9Q4Cg==}
+ cpu: [x64]
+ os: [linux]
+
+ lefthook-openbsd-arm64@1.7.15:
+ resolution: {integrity: sha512-85amE23mJ4BC9OThLkt+QCeVwue2Cr0ezN9LSwP0h8+royyj7YRcSu0VM/Et3B9LO50T2bpdI9norOqTcPZ9yA==}
+ cpu: [arm64]
+ os: [openbsd]
+
+ lefthook-openbsd-x64@1.7.15:
+ resolution: {integrity: sha512-vXx/PpcalFgdvqkoHLI4KTGZp0ti+VCCL7RqDTA6n+GZpxPTWEXSOz3GuwNALX93Dn6MOYQYktKtXADwy24fcA==}
+ cpu: [x64]
+ os: [openbsd]
+
+ lefthook-windows-arm64@1.7.15:
+ resolution: {integrity: sha512-jPKdQOLWQLRPO3VfI3ptpmdQBBsXTwaCLKXMo/gcSyU0xE/ltPD4QqvHzTAFJo00VcKRHjv9QeH69qhENjJtjw==}
+ cpu: [arm64]
+ os: [win32]
+
+ lefthook-windows-x64@1.7.15:
+ resolution: {integrity: sha512-tlkSU669+b64AsqytGy1W3au7h8kFjt5ejLhTkErJpylTqThZIHm/GI0wUmpX+ud8kekM+9j407dweAHYOQ1XA==}
+ cpu: [x64]
+ os: [win32]
+
+ lefthook@1.7.15:
+ resolution: {integrity: sha512-HW2mYkhg0a3RH2t57+ZJwacJiSIuDEhsXQAaCw6iGeN7zowdUV7g5QtnRdFdPkaK2eaNFpG6Rp0GsTrl/v0gNg==}
+ hasBin: true
+
leven@3.1.0:
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
engines: {node: '>=6'}
@@ -16142,6 +16252,41 @@ snapshots:
'@bcoe/v8-coverage@0.2.3': {}
+ '@biomejs/biome@1.9.0':
+ optionalDependencies:
+ '@biomejs/cli-darwin-arm64': 1.9.0
+ '@biomejs/cli-darwin-x64': 1.9.0
+ '@biomejs/cli-linux-arm64': 1.9.0
+ '@biomejs/cli-linux-arm64-musl': 1.9.0
+ '@biomejs/cli-linux-x64': 1.9.0
+ '@biomejs/cli-linux-x64-musl': 1.9.0
+ '@biomejs/cli-win32-arm64': 1.9.0
+ '@biomejs/cli-win32-x64': 1.9.0
+
+ '@biomejs/cli-darwin-arm64@1.9.0':
+ optional: true
+
+ '@biomejs/cli-darwin-x64@1.9.0':
+ optional: true
+
+ '@biomejs/cli-linux-arm64-musl@1.9.0':
+ optional: true
+
+ '@biomejs/cli-linux-arm64@1.9.0':
+ optional: true
+
+ '@biomejs/cli-linux-x64-musl@1.9.0':
+ optional: true
+
+ '@biomejs/cli-linux-x64@1.9.0':
+ optional: true
+
+ '@biomejs/cli-win32-arm64@1.9.0':
+ optional: true
+
+ '@biomejs/cli-win32-x64@1.9.0':
+ optional: true
+
'@chromatic-com/storybook@1.5.0(react@18.2.0)':
dependencies:
chromatic: 11.4.1
@@ -17149,20 +17294,7 @@ snapshots:
dependencies:
'@langchain/core': 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0(zod@3.23.8)))(openai@4.58.0(zod@3.23.8))
js-tiktoken: 1.0.12
- openai: 4.58.0(zod@3.23.8)
- zod: 3.23.8
- zod-to-json-schema: 3.23.2(zod@3.23.8)
- transitivePeerDependencies:
- - encoding
- - langchain
- - supports-color
- optional: true
-
- '@langchain/openai@0.2.10(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))':
- dependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0(zod@3.23.8))
- js-tiktoken: 1.0.12
- openai: 4.58.0(zod@3.23.8)
+ openai: 4.58.0(encoding@0.1.13)(zod@3.23.8)
zod: 3.23.8
zod-to-json-schema: 3.23.2(zod@3.23.8)
transitivePeerDependencies:
@@ -22425,7 +22557,7 @@ snapshots:
eslint-import-resolver-node@0.3.9:
dependencies:
- debug: 3.2.7(supports-color@5.5.0)
+ debug: 3.2.7(supports-color@8.1.1)
is-core-module: 2.13.1
resolve: 1.22.8
transitivePeerDependencies:
@@ -22450,7 +22582,7 @@ snapshots:
eslint-module-utils@2.8.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0):
dependencies:
- debug: 3.2.7(supports-color@5.5.0)
+ debug: 3.2.7(supports-color@8.1.1)
optionalDependencies:
'@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.6.2)
eslint: 8.57.0
@@ -22470,7 +22602,7 @@ snapshots:
array.prototype.findlastindex: 1.2.3
array.prototype.flat: 1.3.2
array.prototype.flatmap: 1.3.2
- debug: 3.2.7(supports-color@5.5.0)
+ debug: 3.2.7(supports-color@8.1.1)
doctrine: 2.1.0
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
@@ -23336,7 +23468,7 @@ snapshots:
array-parallel: 0.1.3
array-series: 0.1.5
cross-spawn: 4.0.2
- debug: 3.2.7(supports-color@5.5.0)
+ debug: 3.2.7(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
@@ -24816,34 +24948,7 @@ snapshots:
optionalDependencies:
'@langchain/core': 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0(zod@3.23.8)))(openai@4.58.0(zod@3.23.8))
langchain: 0.2.18(axios@1.7.4)(openai@4.58.0(zod@3.23.8))
- openai: 4.58.0(zod@3.23.8)
-
- langsmith@0.1.51(@langchain/core@0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0(zod@3.23.8)))(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0(zod@3.23.8)):
- dependencies:
- '@types/uuid': 10.0.0
- commander: 10.0.1
- p-queue: 6.6.2
- p-retry: 4.6.2
- semver: 7.6.0
- uuid: 10.0.0
- optionalDependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0(zod@3.23.8))
- langchain: 0.2.18(axios@1.7.4)(openai@4.58.0)
- openai: 4.58.0(zod@3.23.8)
- optional: true
-
- langsmith@0.1.51(@langchain/core@0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0))(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0):
- dependencies:
- '@types/uuid': 10.0.0
- commander: 10.0.1
- p-queue: 6.6.2
- p-retry: 4.6.2
- semver: 7.6.0
- uuid: 10.0.0
- optionalDependencies:
- '@langchain/core': 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0)
- langchain: 0.2.18(axios@1.7.4)(openai@4.58.0)
- openai: 4.58.0(zod@3.23.8)
+ openai: 4.58.0(encoding@0.1.13)(zod@3.23.8)
lazy-ass@1.6.0: {}
@@ -24867,6 +24972,49 @@ snapshots:
leac@0.6.0: {}
+ lefthook-darwin-arm64@1.7.15:
+ optional: true
+
+ lefthook-darwin-x64@1.7.15:
+ optional: true
+
+ lefthook-freebsd-arm64@1.7.15:
+ optional: true
+
+ lefthook-freebsd-x64@1.7.15:
+ optional: true
+
+ lefthook-linux-arm64@1.7.15:
+ optional: true
+
+ lefthook-linux-x64@1.7.15:
+ optional: true
+
+ lefthook-openbsd-arm64@1.7.15:
+ optional: true
+
+ lefthook-openbsd-x64@1.7.15:
+ optional: true
+
+ lefthook-windows-arm64@1.7.15:
+ optional: true
+
+ lefthook-windows-x64@1.7.15:
+ optional: true
+
+ lefthook@1.7.15:
+ optionalDependencies:
+ lefthook-darwin-arm64: 1.7.15
+ lefthook-darwin-x64: 1.7.15
+ lefthook-freebsd-arm64: 1.7.15
+ lefthook-freebsd-x64: 1.7.15
+ lefthook-linux-arm64: 1.7.15
+ lefthook-linux-x64: 1.7.15
+ lefthook-openbsd-arm64: 1.7.15
+ lefthook-openbsd-x64: 1.7.15
+ lefthook-windows-arm64: 1.7.15
+ lefthook-windows-x64: 1.7.15
+
leven@3.1.0: {}
levn@0.3.0:
@@ -26152,24 +26300,6 @@ snapshots:
- encoding
- supports-color
- openai@4.58.0(zod@3.23.8):
- dependencies:
- '@types/node': 18.16.16
- '@types/node-fetch': 2.6.4
- '@types/qs': 6.9.15
- abort-controller: 3.0.0
- agentkeepalive: 4.2.1
- form-data-encoder: 1.7.2
- formdata-node: 4.4.1
- node-fetch: 2.7.0(encoding@0.1.13)
- qs: 6.11.0
- optionalDependencies:
- zod: 3.23.8
- transitivePeerDependencies:
- - encoding
- - supports-color
- optional: true
-
openapi-sampler@1.4.0:
dependencies:
'@types/json-schema': 7.0.15
@@ -26374,7 +26504,7 @@ snapshots:
pdf-parse@1.1.1:
dependencies:
- debug: 3.2.7(supports-color@5.5.0)
+ debug: 3.2.7(supports-color@8.1.1)
node-ensure: 0.0.0
transitivePeerDependencies:
- supports-color
@@ -27272,7 +27402,7 @@ snapshots:
rhea@1.0.24:
dependencies:
- debug: 3.2.7(supports-color@5.5.0)
+ debug: 3.2.7(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
diff --git a/scripts/format.mjs b/scripts/format.mjs
index 11b267b477..69bbfce015 100644
--- a/scripts/format.mjs
+++ b/scripts/format.mjs
@@ -5,42 +5,61 @@ import path from 'path';
import { execSync } from 'child_process';
const prettier = path.resolve('node_modules', '.bin', 'prettier');
+const biome = path.resolve('node_modules', '.bin', 'biome');
-if (!fs.existsSync(prettier)) {
- throw new Error(
- [`Prettier not found at path: ${prettier}`, 'Please run `pnpm i` first'].join('\n'),
- );
-}
+[prettier, biome].forEach((bin) => {
+ if (!fs.existsSync(bin)) {
+ throw new Error(
+ [`${path.basename(bin)} not found at path: ${bin}`, 'Please run `pnpm i` first'].join('\n'),
+ );
+ }
+});
-const config = path.resolve('.prettierrc.js');
+const prettierConfig = path.resolve('.prettierrc.js');
+const biomeConfig = path.resolve('biome.jsonc');
const ignore = path.resolve('.prettierignore');
-const ROOT_DIRS_TO_SKIP = ['.git', 'node_modules', 'packages'];
-const EXTENSIONS_TO_FORMAT = ['.md', '.yml', '.js', '.json', '.ts'];
+const ROOT_DIRS_TO_SKIP = ['.git', 'node_modules', 'packages', '.turbo', 'cypress'];
+const EXTENSIONS_TO_FORMAT_WITH_PRETTIER = ['.yml'];
+const EXTENSIONS_TO_FORMAT_WITH_BIOME = ['.js', '.json', '.ts'];
const isDir = (path) => fs.lstatSync(path).isDirectory();
-const isTarget = (path) => EXTENSIONS_TO_FORMAT.some((ext) => path.endsWith(ext));
+const isPrettierTarget = (path) =>
+ EXTENSIONS_TO_FORMAT_WITH_PRETTIER.some((ext) => path.endsWith(ext));
+const isBiomeTarget = (path) => EXTENSIONS_TO_FORMAT_WITH_BIOME.some((ext) => path.endsWith(ext));
-const walk = (dir, test, found = []) => {
+const biomeTargets = [];
+const prettierTargets = [];
+
+const walk = (dir) => {
fs.readdirSync(dir).forEach((entry) => {
const entryPath = path.resolve(dir, entry);
- if (isDir(entryPath)) walk(entryPath, test, found);
- if (test(entryPath)) found.push(entryPath);
+ if (isDir(entryPath)) walk(entryPath);
+ if (isPrettierTarget(entryPath)) prettierTargets.push(entryPath);
+ if (isBiomeTarget(entryPath)) biomeTargets.push(entryPath);
});
-
- return found;
};
-const targets = fs
- .readdirSync('.')
- .reduce((acc, cur) => {
- if (ROOT_DIRS_TO_SKIP.includes(cur)) return acc;
- if (isDir(cur)) return [...acc, ...walk(cur, isTarget)];
- if (isTarget(cur)) return [...acc, cur];
+fs.readdirSync('.').forEach((cur) => {
+ if (ROOT_DIRS_TO_SKIP.includes(cur)) return;
+ if (isDir(cur)) walk(cur);
+ if (isPrettierTarget(cur)) prettierTargets.push(cur);
+ if (isBiomeTarget(cur)) biomeTargets.push(cur);
+});
- return acc;
- }, [])
- .join(' ');
+execSync(
+ [
+ prettier,
+ '--config',
+ prettierConfig,
+ '--ignore-path',
+ ignore,
+ '--write',
+ prettierTargets.join(' '),
+ ].join(' '),
+);
-execSync([prettier, '--config', config, '--ignore-path', ignore, '--write', targets].join(' '));
+execSync(
+ [biome, 'format', '--write', `--config-path=${biomeConfig}`, biomeTargets.join(' ')].join(' '),
+);
diff --git a/turbo.json b/turbo.json
index 55e67cae94..cd38cc5045 100644
--- a/turbo.json
+++ b/turbo.json
@@ -23,6 +23,7 @@
"dependsOn": ["^typecheck"]
},
"format": {},
+ "format:check": {},
"lint:backend": {
"dependsOn": [
"@n8n/api-types#lint",
From c55df63abc234ace6ac8e54ed094d10797671264 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?=
=?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?=
=?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?=
Date: Tue, 17 Sep 2024 15:09:35 +0200
Subject: [PATCH 24/42] fix(RSS Feed Trigger Node): Handle empty items
gracefully (#10855)
---
.../RssFeedRead/RssFeedReadTrigger.node.ts | 24 ++++---
.../RssFeedRead/test/RssFeedRead.test.ts | 64 +++++++++++++++++++
.../RssFeedRead/test/node/RssFeedRead.test.ts | 2 +-
3 files changed, 80 insertions(+), 10 deletions(-)
create mode 100644 packages/nodes-base/nodes/RssFeedRead/test/RssFeedRead.test.ts
diff --git a/packages/nodes-base/nodes/RssFeedRead/RssFeedReadTrigger.node.ts b/packages/nodes-base/nodes/RssFeedRead/RssFeedReadTrigger.node.ts
index b956a4e18d..a0607f3b86 100644
--- a/packages/nodes-base/nodes/RssFeedRead/RssFeedReadTrigger.node.ts
+++ b/packages/nodes-base/nodes/RssFeedRead/RssFeedReadTrigger.node.ts
@@ -9,6 +9,11 @@ import { NodeConnectionType, NodeOperationError } from 'n8n-workflow';
import Parser from 'rss-parser';
import moment from 'moment-timezone';
+interface PollData {
+ lastItemDate?: string;
+ lastTimeChecked?: string;
+}
+
export class RssFeedReadTrigger implements INodeType {
description: INodeTypeDescription = {
displayName: 'RSS Feed Trigger',
@@ -39,12 +44,12 @@ export class RssFeedReadTrigger implements INodeType {
};
async poll(this: IPollFunctions): Promise {
- const pollData = this.getWorkflowStaticData('node');
+ const pollData = this.getWorkflowStaticData('node') as PollData;
const feedUrl = this.getNodeParameter('feedUrl') as string;
- const now = moment().utc().format();
- const dateToCheck =
- (pollData.lastItemDate as string) || (pollData.lastTimeChecked as string) || now;
+ const dateToCheck = Date.parse(
+ pollData.lastItemDate ?? pollData.lastTimeChecked ?? moment().utc().format(),
+ );
if (!feedUrl) {
throw new NodeOperationError(this.getNode(), 'The parameter "URL" has to be set!');
@@ -73,14 +78,15 @@ export class RssFeedReadTrigger implements INodeType {
return [this.helpers.returnJsonArray(feed.items[0])];
}
feed.items.forEach((item) => {
- if (Date.parse(item.isoDate as string) > Date.parse(dateToCheck)) {
+ if (Date.parse(item.isoDate as string) > dateToCheck) {
returnData.push(item);
}
});
- const maxIsoDate = feed.items.reduce((a, b) =>
- new Date(a.isoDate as string) > new Date(b.isoDate as string) ? a : b,
- ).isoDate;
- pollData.lastItemDate = maxIsoDate;
+
+ if (feed.items.length) {
+ const maxIsoDate = Math.max(...feed.items.map(({ isoDate }) => Date.parse(isoDate!)));
+ pollData.lastItemDate = new Date(maxIsoDate).toISOString();
+ }
}
if (Array.isArray(returnData) && returnData.length !== 0) {
diff --git a/packages/nodes-base/nodes/RssFeedRead/test/RssFeedRead.test.ts b/packages/nodes-base/nodes/RssFeedRead/test/RssFeedRead.test.ts
new file mode 100644
index 0000000000..0f7b13fbe9
--- /dev/null
+++ b/packages/nodes-base/nodes/RssFeedRead/test/RssFeedRead.test.ts
@@ -0,0 +1,64 @@
+import { mock } from 'jest-mock-extended';
+import type { IPollFunctions } from 'n8n-workflow';
+import Parser from 'rss-parser';
+import { returnJsonArray } from 'n8n-core';
+import { RssFeedReadTrigger } from '../RssFeedReadTrigger.node';
+
+jest.mock('rss-parser');
+
+const now = new Date('2024-02-01T01:23:45.678Z');
+jest.useFakeTimers({ now });
+
+describe('RssFeedReadTrigger', () => {
+ describe('poll', () => {
+ const feedUrl = 'https://example.com/feed';
+ const lastItemDate = '2022-01-01T00:00:00.000Z';
+ const newItemDate = '2022-01-02T00:00:00.000Z';
+
+ const node = new RssFeedReadTrigger();
+ const pollFunctions = mock({
+ helpers: mock({ returnJsonArray }),
+ });
+
+ it('should throw an error if the feed URL is empty', async () => {
+ pollFunctions.getNodeParameter.mockReturnValue('');
+
+ await expect(node.poll.call(pollFunctions)).rejects.toThrowError();
+
+ expect(pollFunctions.getNodeParameter).toHaveBeenCalledWith('feedUrl');
+ expect(Parser.prototype.parseURL).not.toHaveBeenCalled();
+ });
+
+ it('should return new items from the feed', async () => {
+ const pollData = mock({ lastItemDate });
+ pollFunctions.getNodeParameter.mockReturnValue(feedUrl);
+ pollFunctions.getWorkflowStaticData.mockReturnValue(pollData);
+ (Parser.prototype.parseURL as jest.Mock).mockResolvedValue({
+ items: [{ isoDate: lastItemDate }, { isoDate: newItemDate }],
+ });
+
+ const result = await node.poll.call(pollFunctions);
+
+ expect(result).toEqual([[{ json: { isoDate: newItemDate } }]]);
+ expect(pollFunctions.getWorkflowStaticData).toHaveBeenCalledWith('node');
+ expect(pollFunctions.getNodeParameter).toHaveBeenCalledWith('feedUrl');
+ expect(Parser.prototype.parseURL).toHaveBeenCalledWith(feedUrl);
+ expect(pollData.lastItemDate).toEqual(newItemDate);
+ });
+
+ it('should return null if the feed is empty', async () => {
+ const pollData = mock({ lastItemDate });
+ pollFunctions.getNodeParameter.mockReturnValue(feedUrl);
+ pollFunctions.getWorkflowStaticData.mockReturnValue(pollData);
+ (Parser.prototype.parseURL as jest.Mock).mockResolvedValue({ items: [] });
+
+ const result = await node.poll.call(pollFunctions);
+
+ expect(result).toEqual(null);
+ expect(pollFunctions.getWorkflowStaticData).toHaveBeenCalledWith('node');
+ expect(pollFunctions.getNodeParameter).toHaveBeenCalledWith('feedUrl');
+ expect(Parser.prototype.parseURL).toHaveBeenCalledWith(feedUrl);
+ expect(pollData.lastItemDate).toEqual(lastItemDate);
+ });
+ });
+});
diff --git a/packages/nodes-base/nodes/RssFeedRead/test/node/RssFeedRead.test.ts b/packages/nodes-base/nodes/RssFeedRead/test/node/RssFeedRead.test.ts
index 62806e448c..7238dcfa00 100644
--- a/packages/nodes-base/nodes/RssFeedRead/test/node/RssFeedRead.test.ts
+++ b/packages/nodes-base/nodes/RssFeedRead/test/node/RssFeedRead.test.ts
@@ -4,7 +4,7 @@ import { setup, equalityTest, workflowToTests, getWorkflowFilenames } from '@tes
// eslint-disable-next-line n8n-local-rules/no-unneeded-backticks
const feed = ` http://example.com/RSS for Node Thu, 09 Feb 2023 13:40:32 GMT Thu, 09 Feb 2023 13:40:00 GMT 1 http://example.com/test/1675950000http://example.com/test/1675950000 Thu, 09 Feb 2023 13:40:00 GMT http://example.com/test/1675949940http://example.com/test/1675949940 Thu, 09 Feb 2023 13:39:00 GMT http://example.com/test/1675949880http://example.com/test/1675949880 Thu, 09 Feb 2023 13:38:00 GMT `;
-describe('Test HTTP Request Node', () => {
+describe('Test RSS Feed Trigger Node', () => {
const workflows = getWorkflowFilenames(__dirname);
const tests = workflowToTests(workflows);
From aa00d9c2ae4010824e098f64cca6bef930fd075a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?=
Date: Tue, 17 Sep 2024 15:45:42 +0200
Subject: [PATCH 25/42] refactor(core): Flatten Redis pubsub class hierarchy
(no-changelog) (#10616)
---
.../cli/src/__tests__/wait-tracker.test.ts | 2 +-
packages/cli/src/commands/start.ts | 3 +-
packages/cli/src/commands/worker.ts | 6 +-
packages/cli/src/license.ts | 13 +--
.../__tests__/publisher.service.test.ts | 75 ++++++++++++++++
.../__tests__/subscriber.service.test.ts | 60 +++++++++++++
.../src/scaling/pubsub/publisher.service.ts | 88 +++++++++++++++++++
.../cli/src/scaling/pubsub/pubsub.types.ts | 14 +++
.../src/scaling/pubsub/subscriber.service.ts | 60 +++++++++++++
.../__tests__/orchestration.service.test.ts | 47 +++-------
.../services/__tests__/redis.service.test.ts | 57 ------------
.../orchestration.handler.base.service.ts | 13 ---
.../cli/src/services/orchestration.service.ts | 37 ++++----
.../orchestration/main/multi-main-setup.ee.ts | 19 ++--
.../orchestration.handler.main.service.ts | 29 +++---
.../src/services/orchestration/main/types.ts | 4 +-
.../orchestration.handler.webhook.service.ts | 23 ++---
.../worker/handle-command-message-worker.ts | 12 +--
.../orchestration.handler.worker.service.ts | 15 ++--
.../services/orchestration/worker/types.ts | 4 +-
packages/cli/src/services/redis.service.ts | 25 ------
.../services/redis/redis-service-commands.ts | 2 +-
.../redis/redis-service-pub-sub-publisher.ts | 60 -------------
.../redis/redis-service-pub-sub-subscriber.ts | 59 -------------
24 files changed, 392 insertions(+), 335 deletions(-)
create mode 100644 packages/cli/src/scaling/__tests__/publisher.service.test.ts
create mode 100644 packages/cli/src/scaling/__tests__/subscriber.service.test.ts
create mode 100644 packages/cli/src/scaling/pubsub/publisher.service.ts
create mode 100644 packages/cli/src/scaling/pubsub/pubsub.types.ts
create mode 100644 packages/cli/src/scaling/pubsub/subscriber.service.ts
delete mode 100644 packages/cli/src/services/__tests__/redis.service.test.ts
delete mode 100644 packages/cli/src/services/redis.service.ts
delete mode 100644 packages/cli/src/services/redis/redis-service-pub-sub-publisher.ts
delete mode 100644 packages/cli/src/services/redis/redis-service-pub-sub-subscriber.ts
diff --git a/packages/cli/src/__tests__/wait-tracker.test.ts b/packages/cli/src/__tests__/wait-tracker.test.ts
index a3d5e87d4b..9ca3a66d33 100644
--- a/packages/cli/src/__tests__/wait-tracker.test.ts
+++ b/packages/cli/src/__tests__/wait-tracker.test.ts
@@ -11,7 +11,7 @@ jest.useFakeTimers();
describe('WaitTracker', () => {
const executionRepository = mock();
const multiMainSetup = mock();
- const orchestrationService = new OrchestrationService(mock(), mock(), mock(), multiMainSetup);
+ const orchestrationService = new OrchestrationService(mock(), mock(), multiMainSetup);
const execution = mock({
id: '123',
diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts
index 539934bd99..61212049d7 100644
--- a/packages/cli/src/commands/start.ts
+++ b/packages/cli/src/commands/start.ts
@@ -21,6 +21,7 @@ import { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus'
import { EventService } from '@/events/event.service';
import { ExecutionService } from '@/executions/execution.service';
import { License } from '@/license';
+import { Publisher } from '@/scaling/pubsub/publisher.service';
import { Server } from '@/server';
import { OrchestrationHandlerMainService } from '@/services/orchestration/main/orchestration.handler.main.service';
import { OrchestrationService } from '@/services/orchestration.service';
@@ -240,7 +241,7 @@ export class Start extends BaseCommand {
await Container.get(OrchestrationHandlerMainService).initWithOptions({
queueModeId: this.queueModeId,
- redisPublisher: Container.get(OrchestrationService).redisPublisher,
+ publisher: Container.get(Publisher),
});
if (!orchestrationService.isMultiMainSetupEnabled) return;
diff --git a/packages/cli/src/commands/worker.ts b/packages/cli/src/commands/worker.ts
index b1cba17fd4..4b719e8443 100644
--- a/packages/cli/src/commands/worker.ts
+++ b/packages/cli/src/commands/worker.ts
@@ -8,10 +8,10 @@ import { EventMessageGeneric } from '@/eventbus/event-message-classes/event-mess
import { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus';
import { LogStreamingEventRelay } from '@/events/log-streaming-event-relay';
import { JobProcessor } from '@/scaling/job-processor';
+import { Publisher } from '@/scaling/pubsub/publisher.service';
import type { ScalingService } from '@/scaling/scaling.service';
import { OrchestrationHandlerWorkerService } from '@/services/orchestration/worker/orchestration.handler.worker.service';
import { OrchestrationWorkerService } from '@/services/orchestration/worker/orchestration.worker.service';
-import type { RedisServicePubSubSubscriber } from '@/services/redis/redis-service-pub-sub-subscriber';
import { BaseCommand } from './base-command';
@@ -40,8 +40,6 @@ export class Worker extends BaseCommand {
jobProcessor: JobProcessor;
- redisSubscriber: RedisServicePubSubSubscriber;
-
override needsCommunityPackages = true;
/**
@@ -131,7 +129,7 @@ export class Worker extends BaseCommand {
await Container.get(OrchestrationWorkerService).init();
await Container.get(OrchestrationHandlerWorkerService).initWithOptions({
queueModeId: this.queueModeId,
- redisPublisher: Container.get(OrchestrationWorkerService).redisPublisher,
+ publisher: Container.get(Publisher),
getRunningJobIds: () => this.jobProcessor.getRunningJobIds(),
getRunningJobsSummary: () => this.jobProcessor.getRunningJobsSummary(),
});
diff --git a/packages/cli/src/license.ts b/packages/cli/src/license.ts
index fde17a8fcc..75a57efd2c 100644
--- a/packages/cli/src/license.ts
+++ b/packages/cli/src/license.ts
@@ -18,8 +18,6 @@ import {
UNLIMITED_LICENSE_QUOTA,
} from './constants';
import type { BooleanLicenseFeature, NumericLicenseFeature } from './interfaces';
-import type { RedisServicePubSubPublisher } from './services/redis/redis-service-pub-sub-publisher';
-import { RedisService } from './services/redis.service';
export type FeatureReturnType = Partial<
{
@@ -31,8 +29,6 @@ export type FeatureReturnType = Partial<
export class License {
private manager: LicenseManager | undefined;
- private redisPublisher: RedisServicePubSubPublisher;
-
private isShuttingDown = false;
constructor(
@@ -163,13 +159,8 @@ export class License {
}
if (config.getEnv('executions.mode') === 'queue') {
- if (!this.redisPublisher) {
- this.logger.debug('Initializing Redis publisher for License Service');
- this.redisPublisher = await Container.get(RedisService).getPubSubPublisher();
- }
- await this.redisPublisher.publishToCommandChannel({
- command: 'reloadLicense',
- });
+ const { Publisher } = await import('@/scaling/pubsub/publisher.service');
+ await Container.get(Publisher).publishCommand({ command: 'reloadLicense' });
}
const isS3Selected = config.getEnv('binaryDataManager.mode') === 's3';
diff --git a/packages/cli/src/scaling/__tests__/publisher.service.test.ts b/packages/cli/src/scaling/__tests__/publisher.service.test.ts
new file mode 100644
index 0000000000..06b7fe05b4
--- /dev/null
+++ b/packages/cli/src/scaling/__tests__/publisher.service.test.ts
@@ -0,0 +1,75 @@
+import type { Redis as SingleNodeClient } from 'ioredis';
+import { mock } from 'jest-mock-extended';
+
+import config from '@/config';
+import { generateNanoId } from '@/databases/utils/generators';
+import type { RedisClientService } from '@/services/redis/redis-client.service';
+import type {
+ RedisServiceCommandObject,
+ RedisServiceWorkerResponseObject,
+} from '@/services/redis/redis-service-commands';
+
+import { Publisher } from '../pubsub/publisher.service';
+
+describe('Publisher', () => {
+ let queueModeId: string;
+
+ beforeEach(() => {
+ config.set('executions.mode', 'queue');
+ queueModeId = generateNanoId();
+ config.set('redis.queueModeId', queueModeId);
+ });
+
+ const client = mock();
+ const redisClientService = mock({ createClient: () => client });
+
+ describe('constructor', () => {
+ it('should init Redis client in scaling mode', () => {
+ const publisher = new Publisher(mock(), redisClientService);
+
+ expect(publisher.getClient()).toEqual(client);
+ });
+
+ it('should not init Redis client in regular mode', () => {
+ config.set('executions.mode', 'regular');
+ const publisher = new Publisher(mock(), redisClientService);
+
+ expect(publisher.getClient()).toBeUndefined();
+ });
+ });
+
+ describe('shutdown', () => {
+ it('should disconnect Redis client', () => {
+ const publisher = new Publisher(mock(), redisClientService);
+ publisher.shutdown();
+ expect(client.disconnect).toHaveBeenCalled();
+ });
+ });
+
+ describe('publishCommand', () => {
+ it('should publish command into `n8n.commands` pubsub channel', async () => {
+ const publisher = new Publisher(mock(), redisClientService);
+ const msg = mock({ command: 'reloadLicense' });
+
+ await publisher.publishCommand(msg);
+
+ expect(client.publish).toHaveBeenCalledWith(
+ 'n8n.commands',
+ JSON.stringify({ ...msg, senderId: queueModeId }),
+ );
+ });
+ });
+
+ describe('publishWorkerResponse', () => {
+ it('should publish worker response into `n8n.worker-response` pubsub channel', async () => {
+ const publisher = new Publisher(mock(), redisClientService);
+ const msg = mock({
+ command: 'reloadExternalSecretsProviders',
+ });
+
+ await publisher.publishWorkerResponse(msg);
+
+ expect(client.publish).toHaveBeenCalledWith('n8n.worker-response', JSON.stringify(msg));
+ });
+ });
+});
diff --git a/packages/cli/src/scaling/__tests__/subscriber.service.test.ts b/packages/cli/src/scaling/__tests__/subscriber.service.test.ts
new file mode 100644
index 0000000000..96566b7152
--- /dev/null
+++ b/packages/cli/src/scaling/__tests__/subscriber.service.test.ts
@@ -0,0 +1,60 @@
+import type { Redis as SingleNodeClient } from 'ioredis';
+import { mock } from 'jest-mock-extended';
+
+import config from '@/config';
+import type { RedisClientService } from '@/services/redis/redis-client.service';
+
+import { Subscriber } from '../pubsub/subscriber.service';
+
+describe('Subscriber', () => {
+ beforeEach(() => {
+ config.set('executions.mode', 'queue');
+ });
+
+ const client = mock();
+ const redisClientService = mock({ createClient: () => client });
+
+ describe('constructor', () => {
+ it('should init Redis client in scaling mode', () => {
+ const subscriber = new Subscriber(mock(), redisClientService);
+
+ expect(subscriber.getClient()).toEqual(client);
+ });
+
+ it('should not init Redis client in regular mode', () => {
+ config.set('executions.mode', 'regular');
+ const subscriber = new Subscriber(mock(), redisClientService);
+
+ expect(subscriber.getClient()).toBeUndefined();
+ });
+ });
+
+ describe('shutdown', () => {
+ it('should disconnect Redis client', () => {
+ const subscriber = new Subscriber(mock(), redisClientService);
+ subscriber.shutdown();
+ expect(client.disconnect).toHaveBeenCalled();
+ });
+ });
+
+ describe('subscribe', () => {
+ it('should subscribe to pubsub channel', async () => {
+ const subscriber = new Subscriber(mock(), redisClientService);
+
+ await subscriber.subscribe('n8n.commands');
+
+ expect(client.subscribe).toHaveBeenCalledWith('n8n.commands', expect.any(Function));
+ });
+ });
+
+ describe('setHandler', () => {
+ it('should set handler function', () => {
+ const subscriber = new Subscriber(mock(), redisClientService);
+ const handlerFn = jest.fn();
+
+ subscriber.addMessageHandler(handlerFn);
+
+ expect(client.on).toHaveBeenCalledWith('message', handlerFn);
+ });
+ });
+});
diff --git a/packages/cli/src/scaling/pubsub/publisher.service.ts b/packages/cli/src/scaling/pubsub/publisher.service.ts
new file mode 100644
index 0000000000..fee4724d87
--- /dev/null
+++ b/packages/cli/src/scaling/pubsub/publisher.service.ts
@@ -0,0 +1,88 @@
+import type { Redis as SingleNodeClient, Cluster as MultiNodeClient } from 'ioredis';
+import { Service } from 'typedi';
+
+import config from '@/config';
+import { Logger } from '@/logger';
+import { RedisClientService } from '@/services/redis/redis-client.service';
+import type {
+ RedisServiceCommandObject,
+ RedisServiceWorkerResponseObject,
+} from '@/services/redis/redis-service-commands';
+
+/**
+ * Responsible for publishing messages into the pubsub channels used by scaling mode.
+ */
+@Service()
+export class Publisher {
+ private readonly client: SingleNodeClient | MultiNodeClient;
+
+ // #region Lifecycle
+
+ constructor(
+ private readonly logger: Logger,
+ private readonly redisClientService: RedisClientService,
+ ) {
+ // @TODO: Once this class is only ever initialized in scaling mode, throw in the next line instead.
+ if (config.getEnv('executions.mode') !== 'queue') return;
+
+ this.client = this.redisClientService.createClient({ type: 'publisher(n8n)' });
+
+ this.client.on('error', (error) => this.logger.error(error.message));
+ }
+
+ getClient() {
+ return this.client;
+ }
+
+ // @TODO: Use `@OnShutdown()` decorator
+ shutdown() {
+ this.client.disconnect();
+ }
+
+ // #endregion
+
+ // #region Publishing
+
+ /** Publish a command into the `n8n.commands` channel. */
+ async publishCommand(msg: Omit) {
+ await this.client.publish(
+ 'n8n.commands',
+ JSON.stringify({ ...msg, senderId: config.getEnv('redis.queueModeId') }),
+ );
+
+ this.logger.debug(`Published ${msg.command} to command channel`);
+ }
+
+ /** Publish a response for a command into the `n8n.worker-response` channel. */
+ async publishWorkerResponse(msg: RedisServiceWorkerResponseObject) {
+ await this.client.publish('n8n.worker-response', JSON.stringify(msg));
+
+ this.logger.debug(`Published response for ${msg.command} to worker response channel`);
+ }
+
+ // #endregion
+
+ // #region Utils for multi-main setup
+
+ // @TODO: The following methods are not pubsub-specific. Consider a dedicated client for multi-main setup.
+
+ async setIfNotExists(key: string, value: string) {
+ const success = await this.client.setnx(key, value);
+
+ return !!success;
+ }
+
+ async setExpiration(key: string, ttl: number) {
+ await this.client.expire(key, ttl);
+ }
+
+ async get(key: string) {
+ return await this.client.get(key);
+ }
+
+ async clear(key: string) {
+ await this.client?.del(key);
+ }
+
+ // #endregion
+}
diff --git a/packages/cli/src/scaling/pubsub/pubsub.types.ts b/packages/cli/src/scaling/pubsub/pubsub.types.ts
new file mode 100644
index 0000000000..b7f56904b6
--- /dev/null
+++ b/packages/cli/src/scaling/pubsub/pubsub.types.ts
@@ -0,0 +1,14 @@
+import type {
+ COMMAND_REDIS_CHANNEL,
+ WORKER_RESPONSE_REDIS_CHANNEL,
+} from '@/services/redis/redis-constants';
+
+/**
+ * Pubsub channel used by scaling mode:
+ *
+ * - `n8n.commands` for messages sent by a main process to command workers or other main processes
+ * - `n8n.worker-response` for messages sent by workers in response to commands from main processes
+ */
+export type ScalingPubSubChannel =
+ | typeof COMMAND_REDIS_CHANNEL
+ | typeof WORKER_RESPONSE_REDIS_CHANNEL;
diff --git a/packages/cli/src/scaling/pubsub/subscriber.service.ts b/packages/cli/src/scaling/pubsub/subscriber.service.ts
new file mode 100644
index 0000000000..5335c4b04e
--- /dev/null
+++ b/packages/cli/src/scaling/pubsub/subscriber.service.ts
@@ -0,0 +1,60 @@
+import type { Redis as SingleNodeClient, Cluster as MultiNodeClient } from 'ioredis';
+import { Service } from 'typedi';
+
+import config from '@/config';
+import { Logger } from '@/logger';
+import { RedisClientService } from '@/services/redis/redis-client.service';
+
+import type { ScalingPubSubChannel } from './pubsub.types';
+
+/**
+ * Responsible for subscribing to the pubsub channels used by scaling mode.
+ */
+@Service()
+export class Subscriber {
+ private readonly client: SingleNodeClient | MultiNodeClient;
+
+ // #region Lifecycle
+
+ constructor(
+ private readonly logger: Logger,
+ private readonly redisClientService: RedisClientService,
+ ) {
+ // @TODO: Once this class is only ever initialized in scaling mode, throw in the next line instead.
+ if (config.getEnv('executions.mode') !== 'queue') return;
+
+ this.client = this.redisClientService.createClient({ type: 'subscriber(n8n)' });
+
+ this.client.on('error', (error) => this.logger.error(error.message));
+ }
+
+ getClient() {
+ return this.client;
+ }
+
+ // @TODO: Use `@OnShutdown()` decorator
+ shutdown() {
+ this.client.disconnect();
+ }
+
+ // #endregion
+
+ // #region Subscribing
+
+ async subscribe(channel: ScalingPubSubChannel) {
+ await this.client.subscribe(channel, (error) => {
+ if (error) {
+ this.logger.error('Failed to subscribe to channel', { channel, cause: error });
+ return;
+ }
+
+ this.logger.debug('Subscribed to channel', { channel });
+ });
+ }
+
+ addMessageHandler(handlerFn: (channel: string, msg: string) => void) {
+ this.client.on('message', handlerFn);
+ }
+
+ // #endregion
+}
diff --git a/packages/cli/src/services/__tests__/orchestration.service.test.ts b/packages/cli/src/services/__tests__/orchestration.service.test.ts
index 493453d308..b5c6c6a80c 100644
--- a/packages/cli/src/services/__tests__/orchestration.service.test.ts
+++ b/packages/cli/src/services/__tests__/orchestration.service.test.ts
@@ -16,11 +16,13 @@ import { OrchestrationHandlerMainService } from '@/services/orchestration/main/o
import { OrchestrationService } from '@/services/orchestration.service';
import { RedisClientService } from '@/services/redis/redis-client.service';
import type { RedisServiceWorkerResponseObject } from '@/services/redis/redis-service-commands';
-import { RedisService } from '@/services/redis.service';
import { mockInstance } from '@test/mocking';
import type { MainResponseReceivedHandlerOptions } from '../orchestration/main/types';
+config.set('executions.mode', 'queue');
+config.set('generic.instanceType', 'main');
+
const instanceSettings = Container.get(InstanceSettings);
const redisClientService = mockInstance(RedisClientService);
const mockRedisClient = mock();
@@ -32,10 +34,6 @@ mockInstance(ActiveWorkflowManager);
let queueModeId: string;
-function setDefaultConfig() {
- config.set('executions.mode', 'queue');
-}
-
const workerRestartEventBusResponse: RedisServiceWorkerResponseObject = {
senderId: 'test',
workerId: 'test',
@@ -47,30 +45,10 @@ const workerRestartEventBusResponse: RedisServiceWorkerResponseObject = {
describe('Orchestration Service', () => {
mockInstance(Push);
- mockInstance(RedisService);
mockInstance(ExternalSecretsManager);
const eventBus = mockInstance(MessageEventBus);
beforeAll(async () => {
- jest.mock('@/services/redis/redis-service-pub-sub-publisher', () => {
- return jest.fn().mockImplementation(() => {
- return {
- init: jest.fn(),
- publishToEventLog: jest.fn(),
- publishToWorkerChannel: jest.fn(),
- destroy: jest.fn(),
- };
- });
- });
- jest.mock('@/services/redis/redis-service-pub-sub-subscriber', () => {
- return jest.fn().mockImplementation(() => {
- return {
- subscribeToCommandChannel: jest.fn(),
- destroy: jest.fn(),
- };
- });
- });
- setDefaultConfig();
queueModeId = config.get('redis.queueModeId');
// @ts-expect-error readonly property
@@ -82,16 +60,16 @@ describe('Orchestration Service', () => {
});
afterAll(async () => {
- jest.mock('@/services/redis/redis-service-pub-sub-publisher').restoreAllMocks();
- jest.mock('@/services/redis/redis-service-pub-sub-subscriber').restoreAllMocks();
await os.shutdown();
});
test('should initialize', async () => {
await os.init();
await handler.init();
- expect(os.redisPublisher).toBeDefined();
- expect(handler.redisSubscriber).toBeDefined();
+ // @ts-expect-error Private field
+ expect(os.publisher).toBeDefined();
+ // @ts-expect-error Private field
+ expect(handler.subscriber).toBeDefined();
expect(queueModeId).toBeDefined();
});
@@ -126,15 +104,16 @@ describe('Orchestration Service', () => {
});
test('should send command messages', async () => {
- setDefaultConfig();
- jest.spyOn(os.redisPublisher, 'publishToCommandChannel').mockImplementation(async () => {});
+ // @ts-expect-error Private field
+ jest.spyOn(os.publisher, 'publishCommand').mockImplementation(async () => {});
await os.getWorkerIds();
- expect(os.redisPublisher.publishToCommandChannel).toHaveBeenCalled();
- jest.spyOn(os.redisPublisher, 'publishToCommandChannel').mockRestore();
+ // @ts-expect-error Private field
+ expect(os.publisher.publishCommand).toHaveBeenCalled();
+ // @ts-expect-error Private field
+ jest.spyOn(os.publisher, 'publishCommand').mockRestore();
});
test('should prevent receiving commands too often', async () => {
- setDefaultConfig();
jest.spyOn(helpers, 'debounceMessageReceiver');
const res1 = await handleCommandMessageMain(
JSON.stringify({
diff --git a/packages/cli/src/services/__tests__/redis.service.test.ts b/packages/cli/src/services/__tests__/redis.service.test.ts
deleted file mode 100644
index 9990b58d6a..0000000000
--- a/packages/cli/src/services/__tests__/redis.service.test.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import Container from 'typedi';
-
-import config from '@/config';
-import { Logger } from '@/logger';
-import { RedisService } from '@/services/redis.service';
-import { mockInstance } from '@test/mocking';
-
-jest.mock('ioredis', () => {
- const Redis = require('ioredis-mock');
- if (typeof Redis === 'object') {
- // the first mock is an ioredis shim because ioredis-mock depends on it
- // https://github.com/stipsan/ioredis-mock/blob/master/src/index.js#L101-L111
- return {
- Command: { _transformer: { argument: {}, reply: {} } },
- };
- }
- // second mock for our code
- return function (...args: unknown[]) {
- return new Redis(args);
- };
-});
-
-mockInstance(Logger);
-const redisService = Container.get(RedisService);
-
-function setDefaultConfig() {
- config.set('executions.mode', 'queue');
-}
-
-const PUBSUB_CHANNEL = 'testchannel';
-
-describe('RedisService', () => {
- beforeAll(async () => {
- setDefaultConfig();
- });
-
- test('should create pubsub publisher and subscriber with handler', async () => {
- const pub = await redisService.getPubSubPublisher();
- const sub = await redisService.getPubSubSubscriber();
- expect(pub).toBeDefined();
- expect(sub).toBeDefined();
-
- const mockHandler = jest.fn();
- mockHandler.mockImplementation((_channel: string, _message: string) => {});
- sub.addMessageHandler(PUBSUB_CHANNEL, mockHandler);
- await sub.subscribe(PUBSUB_CHANNEL);
- await pub.publish(PUBSUB_CHANNEL, 'test');
- await new Promise((resolve) =>
- setTimeout(async () => {
- resolve(0);
- }, 50),
- );
- expect(mockHandler).toHaveBeenCalled();
- await sub.destroy();
- await pub.destroy();
- });
-});
diff --git a/packages/cli/src/services/orchestration.handler.base.service.ts b/packages/cli/src/services/orchestration.handler.base.service.ts
index d2f0a2de5d..e994ff6308 100644
--- a/packages/cli/src/services/orchestration.handler.base.service.ts
+++ b/packages/cli/src/services/orchestration.handler.base.service.ts
@@ -1,21 +1,9 @@
-import Container from 'typedi';
-
import type { MainResponseReceivedHandlerOptions } from './orchestration/main/types';
import type { WorkerCommandReceivedHandlerOptions } from './orchestration/worker/types';
-import type { RedisServicePubSubSubscriber } from './redis/redis-service-pub-sub-subscriber';
-import { RedisService } from './redis.service';
export abstract class OrchestrationHandlerService {
protected initialized = false;
- redisSubscriber: RedisServicePubSubSubscriber;
-
- readonly redisService: RedisService;
-
- constructor() {
- this.redisService = Container.get(RedisService);
- }
-
async init() {
await this.initSubscriber();
this.initialized = true;
@@ -29,7 +17,6 @@ export abstract class OrchestrationHandlerService {
}
async shutdown() {
- await this.redisSubscriber?.destroy();
this.initialized = false;
}
diff --git a/packages/cli/src/services/orchestration.service.ts b/packages/cli/src/services/orchestration.service.ts
index b95191efb8..8e4963070d 100644
--- a/packages/cli/src/services/orchestration.service.ts
+++ b/packages/cli/src/services/orchestration.service.ts
@@ -1,24 +1,27 @@
import { InstanceSettings } from 'n8n-core';
import type { WorkflowActivateMode } from 'n8n-workflow';
-import { Service } from 'typedi';
+import Container, { Service } from 'typedi';
import config from '@/config';
import { Logger } from '@/logger';
+import type { Publisher } from '@/scaling/pubsub/publisher.service';
+import type { Subscriber } from '@/scaling/pubsub/subscriber.service';
import { MultiMainSetup } from './orchestration/main/multi-main-setup.ee';
import type { RedisServiceBaseCommand, RedisServiceCommand } from './redis/redis-service-commands';
-import type { RedisServicePubSubPublisher } from './redis/redis-service-pub-sub-publisher';
-import { RedisService } from './redis.service';
@Service()
export class OrchestrationService {
constructor(
private readonly logger: Logger,
- protected readonly instanceSettings: InstanceSettings,
- private readonly redisService: RedisService,
+ readonly instanceSettings: InstanceSettings,
readonly multiMainSetup: MultiMainSetup,
) {}
+ private publisher: Publisher;
+
+ private subscriber: Subscriber;
+
protected isInitialized = false;
private isMultiMainSetupLicensed = false;
@@ -40,8 +43,6 @@ export class OrchestrationService {
return !this.isMultiMainSetupEnabled;
}
- redisPublisher: RedisServicePubSubPublisher;
-
get instanceId() {
return config.getEnv('redis.queueModeId');
}
@@ -63,7 +64,13 @@ export class OrchestrationService {
async init() {
if (this.isInitialized) return;
- if (config.get('executions.mode') === 'queue') await this.initPublisher();
+ if (config.get('executions.mode') === 'queue') {
+ const { Publisher } = await import('@/scaling/pubsub/publisher.service');
+ this.publisher = Container.get(Publisher);
+
+ const { Subscriber } = await import('@/scaling/pubsub/subscriber.service');
+ this.subscriber = Container.get(Subscriber);
+ }
if (this.isMultiMainSetupEnabled) {
await this.multiMainSetup.init();
@@ -74,12 +81,14 @@ export class OrchestrationService {
this.isInitialized = true;
}
+ // @TODO: Use `@OnShutdown()` decorator
async shutdown() {
if (!this.isInitialized) return;
if (this.isMultiMainSetupEnabled) await this.multiMainSetup.shutdown();
- await this.redisPublisher.destroy();
+ this.publisher.shutdown();
+ this.subscriber.shutdown();
this.isInitialized = false;
}
@@ -88,10 +97,6 @@ export class OrchestrationService {
// pubsub
// ----------------------------------
- protected async initPublisher() {
- this.redisPublisher = await this.redisService.getPubSubPublisher();
- }
-
async publish(command: RedisServiceCommand, data?: unknown) {
if (!this.sanityCheck()) return;
@@ -99,7 +104,7 @@ export class OrchestrationService {
this.logger.debug(`[Instance ID ${this.instanceId}] Publishing command "${command}"`, payload);
- await this.redisPublisher.publishToCommandChannel({ command, payload });
+ await this.publisher.publishCommand({ command, payload });
}
// ----------------------------------
@@ -113,7 +118,7 @@ export class OrchestrationService {
this.logger.debug(`Sending "${command}" to command channel`);
- await this.redisPublisher.publishToCommandChannel({
+ await this.publisher.publishCommand({
command,
targets: id ? [id] : undefined,
});
@@ -126,7 +131,7 @@ export class OrchestrationService {
this.logger.debug(`Sending "${command}" to command channel`);
- await this.redisPublisher.publishToCommandChannel({ command });
+ await this.publisher.publishCommand({ command });
}
// ----------------------------------
diff --git a/packages/cli/src/services/orchestration/main/multi-main-setup.ee.ts b/packages/cli/src/services/orchestration/main/multi-main-setup.ee.ts
index aa0b02ffc2..a03389ce15 100644
--- a/packages/cli/src/services/orchestration/main/multi-main-setup.ee.ts
+++ b/packages/cli/src/services/orchestration/main/multi-main-setup.ee.ts
@@ -5,8 +5,8 @@ import { Service } from 'typedi';
import config from '@/config';
import { TIME } from '@/constants';
import { Logger } from '@/logger';
+import { Publisher } from '@/scaling/pubsub/publisher.service';
import { RedisClientService } from '@/services/redis/redis-client.service';
-import { RedisServicePubSubPublisher } from '@/services/redis/redis-service-pub-sub-publisher';
import { TypedEmitter } from '@/typed-emitter';
type MultiMainEvents = {
@@ -19,7 +19,7 @@ export class MultiMainSetup extends TypedEmitter {
constructor(
private readonly logger: Logger,
private readonly instanceSettings: InstanceSettings,
- private readonly redisPublisher: RedisServicePubSubPublisher,
+ private readonly publisher: Publisher,
private readonly redisClientService: RedisClientService,
) {
super();
@@ -52,16 +52,16 @@ export class MultiMainSetup extends TypedEmitter {
const { isLeader } = this.instanceSettings;
- if (isLeader) await this.redisPublisher.clear(this.leaderKey);
+ if (isLeader) await this.publisher.clear(this.leaderKey);
}
private async checkLeader() {
- const leaderId = await this.redisPublisher.get(this.leaderKey);
+ const leaderId = await this.publisher.get(this.leaderKey);
if (leaderId === this.instanceId) {
this.logger.debug(`[Instance ID ${this.instanceId}] Leader is this instance`);
- await this.redisPublisher.setExpiration(this.leaderKey, this.leaderKeyTtl);
+ await this.publisher.setExpiration(this.leaderKey, this.leaderKeyTtl);
return;
}
@@ -98,17 +98,14 @@ export class MultiMainSetup extends TypedEmitter {
private async tryBecomeLeader() {
// this can only succeed if leadership is currently vacant
- const keySetSuccessfully = await this.redisPublisher.setIfNotExists(
- this.leaderKey,
- this.instanceId,
- );
+ const keySetSuccessfully = await this.publisher.setIfNotExists(this.leaderKey, this.instanceId);
if (keySetSuccessfully) {
this.logger.debug(`[Instance ID ${this.instanceId}] Leader is now this instance`);
this.instanceSettings.markAsLeader();
- await this.redisPublisher.setExpiration(this.leaderKey, this.leaderKeyTtl);
+ await this.publisher.setExpiration(this.leaderKey, this.leaderKeyTtl);
/**
* Gained leadership - start triggers, pollers, pruning, wait-tracking, license renewal, queue recovery
@@ -120,6 +117,6 @@ export class MultiMainSetup extends TypedEmitter {
}
async fetchLeaderKey() {
- return await this.redisPublisher.get(this.leaderKey);
+ return await this.publisher.get(this.leaderKey);
}
}
diff --git a/packages/cli/src/services/orchestration/main/orchestration.handler.main.service.ts b/packages/cli/src/services/orchestration/main/orchestration.handler.main.service.ts
index fc08146aa0..0aec5c0f08 100644
--- a/packages/cli/src/services/orchestration/main/orchestration.handler.main.service.ts
+++ b/packages/cli/src/services/orchestration/main/orchestration.handler.main.service.ts
@@ -1,5 +1,7 @@
import { Service } from 'typedi';
+import { Subscriber } from '@/scaling/pubsub/subscriber.service';
+
import { handleCommandMessageMain } from './handle-command-message-main';
import { handleWorkerResponseMessageMain } from './handle-worker-response-message-main';
import type { MainResponseReceivedHandlerOptions } from './types';
@@ -8,21 +10,20 @@ import { COMMAND_REDIS_CHANNEL, WORKER_RESPONSE_REDIS_CHANNEL } from '../../redi
@Service()
export class OrchestrationHandlerMainService extends OrchestrationHandlerService {
+ constructor(private readonly subscriber: Subscriber) {
+ super();
+ }
+
async initSubscriber(options: MainResponseReceivedHandlerOptions) {
- this.redisSubscriber = await this.redisService.getPubSubSubscriber();
+ await this.subscriber.subscribe('n8n.commands');
+ await this.subscriber.subscribe('n8n.worker-response');
- await this.redisSubscriber.subscribeToCommandChannel();
- await this.redisSubscriber.subscribeToWorkerResponseChannel();
-
- this.redisSubscriber.addMessageHandler(
- 'OrchestrationMessageReceiver',
- async (channel: string, messageString: string) => {
- if (channel === WORKER_RESPONSE_REDIS_CHANNEL) {
- await handleWorkerResponseMessageMain(messageString, options);
- } else if (channel === COMMAND_REDIS_CHANNEL) {
- await handleCommandMessageMain(messageString);
- }
- },
- );
+ this.subscriber.addMessageHandler(async (channel: string, messageString: string) => {
+ if (channel === WORKER_RESPONSE_REDIS_CHANNEL) {
+ await handleWorkerResponseMessageMain(messageString, options);
+ } else if (channel === COMMAND_REDIS_CHANNEL) {
+ await handleCommandMessageMain(messageString);
+ }
+ });
}
}
diff --git a/packages/cli/src/services/orchestration/main/types.ts b/packages/cli/src/services/orchestration/main/types.ts
index 57992f8221..7388a55032 100644
--- a/packages/cli/src/services/orchestration/main/types.ts
+++ b/packages/cli/src/services/orchestration/main/types.ts
@@ -1,6 +1,6 @@
-import type { RedisServicePubSubPublisher } from '@/services/redis/redis-service-pub-sub-publisher';
+import type { Publisher } from '@/scaling/pubsub/publisher.service';
export type MainResponseReceivedHandlerOptions = {
queueModeId: string;
- redisPublisher: RedisServicePubSubPublisher;
+ publisher: Publisher;
};
diff --git a/packages/cli/src/services/orchestration/webhook/orchestration.handler.webhook.service.ts b/packages/cli/src/services/orchestration/webhook/orchestration.handler.webhook.service.ts
index d1c778697d..5ecef9ade6 100644
--- a/packages/cli/src/services/orchestration/webhook/orchestration.handler.webhook.service.ts
+++ b/packages/cli/src/services/orchestration/webhook/orchestration.handler.webhook.service.ts
@@ -1,23 +1,24 @@
import { Service } from 'typedi';
+import { Subscriber } from '@/scaling/pubsub/subscriber.service';
+
import { handleCommandMessageWebhook } from './handle-command-message-webhook';
import { OrchestrationHandlerService } from '../../orchestration.handler.base.service';
import { COMMAND_REDIS_CHANNEL } from '../../redis/redis-constants';
@Service()
export class OrchestrationHandlerWebhookService extends OrchestrationHandlerService {
+ constructor(private readonly subscriber: Subscriber) {
+ super();
+ }
+
async initSubscriber() {
- this.redisSubscriber = await this.redisService.getPubSubSubscriber();
+ await this.subscriber.subscribe('n8n.commands');
- await this.redisSubscriber.subscribeToCommandChannel();
-
- this.redisSubscriber.addMessageHandler(
- 'OrchestrationMessageReceiver',
- async (channel: string, messageString: string) => {
- if (channel === COMMAND_REDIS_CHANNEL) {
- await handleCommandMessageWebhook(messageString);
- }
- },
- );
+ this.subscriber.addMessageHandler(async (channel: string, messageString: string) => {
+ if (channel === COMMAND_REDIS_CHANNEL) {
+ await handleCommandMessageWebhook(messageString);
+ }
+ });
}
}
diff --git a/packages/cli/src/services/orchestration/worker/handle-command-message-worker.ts b/packages/cli/src/services/orchestration/worker/handle-command-message-worker.ts
index 2bc46745c8..13cb8ceaff 100644
--- a/packages/cli/src/services/orchestration/worker/handle-command-message-worker.ts
+++ b/packages/cli/src/services/orchestration/worker/handle-command-message-worker.ts
@@ -39,7 +39,7 @@ export function getWorkerCommandReceivedHandler(options: WorkerCommandReceivedHa
switch (message.command) {
case 'getStatus':
if (!debounceMessageReceiver(message, 500)) return;
- await options.redisPublisher.publishToWorkerChannel({
+ await options.publisher.publishWorkerResponse({
workerId: options.queueModeId,
command: 'getStatus',
payload: {
@@ -66,7 +66,7 @@ export function getWorkerCommandReceivedHandler(options: WorkerCommandReceivedHa
break;
case 'getId':
if (!debounceMessageReceiver(message, 500)) return;
- await options.redisPublisher.publishToWorkerChannel({
+ await options.publisher.publishWorkerResponse({
workerId: options.queueModeId,
command: 'getId',
});
@@ -75,7 +75,7 @@ export function getWorkerCommandReceivedHandler(options: WorkerCommandReceivedHa
if (!debounceMessageReceiver(message, 500)) return;
try {
await Container.get(MessageEventBus).restart();
- await options.redisPublisher.publishToWorkerChannel({
+ await options.publisher.publishWorkerResponse({
workerId: options.queueModeId,
command: 'restartEventBus',
payload: {
@@ -83,7 +83,7 @@ export function getWorkerCommandReceivedHandler(options: WorkerCommandReceivedHa
},
});
} catch (error) {
- await options.redisPublisher.publishToWorkerChannel({
+ await options.publisher.publishWorkerResponse({
workerId: options.queueModeId,
command: 'restartEventBus',
payload: {
@@ -97,7 +97,7 @@ export function getWorkerCommandReceivedHandler(options: WorkerCommandReceivedHa
if (!debounceMessageReceiver(message, 500)) return;
try {
await Container.get(ExternalSecretsManager).reloadAllProviders();
- await options.redisPublisher.publishToWorkerChannel({
+ await options.publisher.publishWorkerResponse({
workerId: options.queueModeId,
command: 'reloadExternalSecretsProviders',
payload: {
@@ -105,7 +105,7 @@ export function getWorkerCommandReceivedHandler(options: WorkerCommandReceivedHa
},
});
} catch (error) {
- await options.redisPublisher.publishToWorkerChannel({
+ await options.publisher.publishWorkerResponse({
workerId: options.queueModeId,
command: 'reloadExternalSecretsProviders',
payload: {
diff --git a/packages/cli/src/services/orchestration/worker/orchestration.handler.worker.service.ts b/packages/cli/src/services/orchestration/worker/orchestration.handler.worker.service.ts
index 3e91ca1655..a1356df5de 100644
--- a/packages/cli/src/services/orchestration/worker/orchestration.handler.worker.service.ts
+++ b/packages/cli/src/services/orchestration/worker/orchestration.handler.worker.service.ts
@@ -1,18 +1,19 @@
import { Service } from 'typedi';
+import { Subscriber } from '@/scaling/pubsub/subscriber.service';
+
import { getWorkerCommandReceivedHandler } from './handle-command-message-worker';
import type { WorkerCommandReceivedHandlerOptions } from './types';
import { OrchestrationHandlerService } from '../../orchestration.handler.base.service';
@Service()
export class OrchestrationHandlerWorkerService extends OrchestrationHandlerService {
- async initSubscriber(options: WorkerCommandReceivedHandlerOptions) {
- this.redisSubscriber = await this.redisService.getPubSubSubscriber();
+ constructor(private readonly subscriber: Subscriber) {
+ super();
+ }
- await this.redisSubscriber.subscribeToCommandChannel();
- this.redisSubscriber.addMessageHandler(
- 'WorkerCommandReceivedHandler',
- getWorkerCommandReceivedHandler(options),
- );
+ async initSubscriber(options: WorkerCommandReceivedHandlerOptions) {
+ await this.subscriber.subscribe('n8n.commands');
+ this.subscriber.addMessageHandler(getWorkerCommandReceivedHandler(options));
}
}
diff --git a/packages/cli/src/services/orchestration/worker/types.ts b/packages/cli/src/services/orchestration/worker/types.ts
index 370f7a1ec3..df500ee3c1 100644
--- a/packages/cli/src/services/orchestration/worker/types.ts
+++ b/packages/cli/src/services/orchestration/worker/types.ts
@@ -1,11 +1,11 @@
import type { RunningJobSummary } from '@n8n/api-types';
import type { ExecutionStatus, WorkflowExecuteMode } from 'n8n-workflow';
-import type { RedisServicePubSubPublisher } from '../../redis/redis-service-pub-sub-publisher';
+import type { Publisher } from '@/scaling/pubsub/publisher.service';
export interface WorkerCommandReceivedHandlerOptions {
queueModeId: string;
- redisPublisher: RedisServicePubSubPublisher;
+ publisher: Publisher;
getRunningJobIds: () => Array;
getRunningJobsSummary: () => RunningJobSummary[];
}
diff --git a/packages/cli/src/services/redis.service.ts b/packages/cli/src/services/redis.service.ts
deleted file mode 100644
index 22692f3637..0000000000
--- a/packages/cli/src/services/redis.service.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Service } from 'typedi';
-
-import { RedisServicePubSubPublisher } from './redis/redis-service-pub-sub-publisher';
-import { RedisServicePubSubSubscriber } from './redis/redis-service-pub-sub-subscriber';
-
-/*
- * This is a convenience service that provides access to all the Redis clients.
- */
-@Service()
-export class RedisService {
- constructor(
- private redisServicePubSubSubscriber: RedisServicePubSubSubscriber,
- private redisServicePubSubPublisher: RedisServicePubSubPublisher,
- ) {}
-
- async getPubSubSubscriber() {
- await this.redisServicePubSubSubscriber.init();
- return this.redisServicePubSubSubscriber;
- }
-
- async getPubSubPublisher() {
- await this.redisServicePubSubPublisher.init();
- return this.redisServicePubSubPublisher;
- }
-}
diff --git a/packages/cli/src/services/redis/redis-service-commands.ts b/packages/cli/src/services/redis/redis-service-commands.ts
index 71ebd3dee5..e64d1e97fc 100644
--- a/packages/cli/src/services/redis/redis-service-commands.ts
+++ b/packages/cli/src/services/redis/redis-service-commands.ts
@@ -21,7 +21,7 @@ export type RedisServiceCommand =
| 'clear-test-webhooks'; // multi-main only
/**
- * An object to be sent via Redis pub/sub from the main process to the workers.
+ * An object to be sent via Redis pubsub from the main process to the workers.
* @field command: The command to be executed.
* @field targets: The targets to execute the command on. Leave empty to execute on all workers or specify worker ids.
* @field payload: Optional arguments to be sent with the command.
diff --git a/packages/cli/src/services/redis/redis-service-pub-sub-publisher.ts b/packages/cli/src/services/redis/redis-service-pub-sub-publisher.ts
deleted file mode 100644
index 367591b4cd..0000000000
--- a/packages/cli/src/services/redis/redis-service-pub-sub-publisher.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-import { Service } from 'typedi';
-
-import { COMMAND_REDIS_CHANNEL, WORKER_RESPONSE_REDIS_CHANNEL } from './redis-constants';
-import { RedisServiceBaseSender } from './redis-service-base-classes';
-import type {
- RedisServiceCommandObject,
- RedisServiceWorkerResponseObject,
-} from './redis-service-commands';
-
-@Service()
-export class RedisServicePubSubPublisher extends RedisServiceBaseSender {
- async init(): Promise {
- await super.init('publisher(n8n)');
- }
-
- async publish(channel: string, message: string): Promise {
- if (!this.redisClient) {
- await this.init();
- }
- await this.redisClient?.publish(channel, message);
- }
-
- async publishToCommandChannel(
- message: Omit,
- ): Promise {
- const messageWithSenderId = message as RedisServiceCommandObject;
- messageWithSenderId.senderId = this.senderId;
- await this.publish(COMMAND_REDIS_CHANNEL, JSON.stringify(messageWithSenderId));
- }
-
- async publishToWorkerChannel(message: RedisServiceWorkerResponseObject): Promise {
- await this.publish(WORKER_RESPONSE_REDIS_CHANNEL, JSON.stringify(message));
- }
-
- async setIfNotExists(key: string, value: string) {
- if (!this.redisClient) await this.init();
-
- const success = await this.redisClient?.setnx(key, value);
-
- return !!success;
- }
-
- async setExpiration(key: string, ttl: number) {
- if (!this.redisClient) await this.init();
-
- await this.redisClient?.expire(key, ttl);
- }
-
- async get(key: string) {
- if (!this.redisClient) await this.init();
-
- return await this.redisClient?.get(key);
- }
-
- async clear(key: string) {
- if (!this.redisClient) await this.init();
-
- await this.redisClient?.del(key);
- }
-}
diff --git a/packages/cli/src/services/redis/redis-service-pub-sub-subscriber.ts b/packages/cli/src/services/redis/redis-service-pub-sub-subscriber.ts
deleted file mode 100644
index 52bc0b225f..0000000000
--- a/packages/cli/src/services/redis/redis-service-pub-sub-subscriber.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import { Service } from 'typedi';
-
-import { COMMAND_REDIS_CHANNEL, WORKER_RESPONSE_REDIS_CHANNEL } from './redis-constants';
-import { RedisServiceBaseReceiver } from './redis-service-base-classes';
-
-@Service()
-export class RedisServicePubSubSubscriber extends RedisServiceBaseReceiver {
- async init(): Promise {
- await super.init('subscriber(n8n)');
-
- this.redisClient?.on('message', (channel: string, message: string) => {
- this.messageHandlers.forEach((handler: (channel: string, message: string) => void) =>
- handler(channel, message),
- );
- });
- }
-
- async subscribe(channel: string): Promise {
- if (!this.redisClient) {
- await this.init();
- }
- await this.redisClient?.subscribe(channel, (error, _count: number) => {
- if (error) {
- this.logger.error(`Error subscribing to channel ${channel}`);
- } else {
- this.logger.debug(`Subscribed Redis PubSub client to channel: ${channel}`);
- }
- });
- }
-
- async unsubscribe(channel: string): Promise {
- if (!this.redisClient) {
- return;
- }
- await this.redisClient?.unsubscribe(channel, (error, _count: number) => {
- if (error) {
- this.logger.error(`Error unsubscribing from channel ${channel}`);
- } else {
- this.logger.debug(`Unsubscribed Redis PubSub client from channel: ${channel}`);
- }
- });
- }
-
- async subscribeToCommandChannel(): Promise {
- await this.subscribe(COMMAND_REDIS_CHANNEL);
- }
-
- async subscribeToWorkerResponseChannel(): Promise {
- await this.subscribe(WORKER_RESPONSE_REDIS_CHANNEL);
- }
-
- async unSubscribeFromCommandChannel(): Promise {
- await this.unsubscribe(COMMAND_REDIS_CHANNEL);
- }
-
- async unSubscribeFromWorkerResponseChannel(): Promise {
- await this.unsubscribe(WORKER_RESPONSE_REDIS_CHANNEL);
- }
-}
From a3335e0ecd3796c874985d3c6fbbaabc35dc3490 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?=
=?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?=
=?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?=
Date: Tue, 17 Sep 2024 15:55:51 +0200
Subject: [PATCH 26/42] feat(core): Allow customizing max file size in
form-data payloads for webhooks (#10857)
---
packages/@n8n/config/src/configs/endpoints.config.ts | 4 ++++
packages/@n8n/config/test/config.test.ts | 1 +
packages/cli/src/webhooks/webhook-helpers.ts | 3 +++
3 files changed, 8 insertions(+)
diff --git a/packages/@n8n/config/src/configs/endpoints.config.ts b/packages/@n8n/config/src/configs/endpoints.config.ts
index 9905d9f48a..4ec58ccf0d 100644
--- a/packages/@n8n/config/src/configs/endpoints.config.ts
+++ b/packages/@n8n/config/src/configs/endpoints.config.ts
@@ -65,6 +65,10 @@ export class EndpointsConfig {
@Env('N8N_PAYLOAD_SIZE_MAX')
payloadSizeMax: number = 16;
+ /** Max payload size for files in form-data webhook payloads in MiB */
+ @Env('N8N_FORMDATA_FILE_SIZE_MAX')
+ formDataFileSizeMax: number = 200;
+
@Nested
metrics: PrometheusMetricsConfig;
diff --git a/packages/@n8n/config/test/config.test.ts b/packages/@n8n/config/test/config.test.ts
index 118cf5a1bf..f7c1d48e21 100644
--- a/packages/@n8n/config/test/config.test.ts
+++ b/packages/@n8n/config/test/config.test.ts
@@ -176,6 +176,7 @@ describe('GlobalConfig', () => {
formTest: 'form-test',
formWaiting: 'form-waiting',
payloadSizeMax: 16,
+ formDataFileSizeMax: 200,
rest: 'rest',
webhook: 'webhook',
webhookTest: 'webhook-test',
diff --git a/packages/cli/src/webhooks/webhook-helpers.ts b/packages/cli/src/webhooks/webhook-helpers.ts
index 3b5ffa3466..064ef95eb1 100644
--- a/packages/cli/src/webhooks/webhook-helpers.ts
+++ b/packages/cli/src/webhooks/webhook-helpers.ts
@@ -6,6 +6,7 @@
/* eslint-disable prefer-spread */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
+import { GlobalConfig } from '@n8n/config';
import type express from 'express';
import formidable from 'formidable';
import get from 'lodash/get';
@@ -214,9 +215,11 @@ export async function executeWebhook(
if (!binaryData) {
const { contentType, encoding } = req;
if (contentType === 'multipart/form-data') {
+ const { formDataFileSizeMax } = Container.get(GlobalConfig).endpoints;
const form = formidable({
multiples: true,
encoding: encoding as formidable.BufferEncoding,
+ maxFileSize: formDataFileSizeMax,
// TODO: pass a custom `fileWriteStreamHandler` to create binary data files directly
});
req.body = await new Promise((resolve) => {
From d65ade4e92eed3cfc47854d493fac6885a1a852b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Milorad=20FIlipovi=C4=87?=
Date: Tue, 17 Sep 2024 16:14:02 +0200
Subject: [PATCH 27/42] fix(editor): Add missing node parameter values to AI
Assistant request (#10788)
---
.../__tests__/useWorkflowHelpers.spec.ts | 158 ++++++++
.../src/composables/useWorkflowHelpers.ts | 37 ++
.../stores/__tests__/assistant.store.test.ts | 1 +
.../editor-ui/src/stores/assistant.store.ts | 18 +-
.../editor-ui/src/types/assistant.types.ts | 5 +-
.../utils/__tests__/nodeTypesUtils.spec.ts | 383 ++++++++++++++++++
.../editor-ui/src/utils/nodeTypesUtils.ts | 152 +++----
7 files changed, 678 insertions(+), 76 deletions(-)
create mode 100644 packages/editor-ui/src/utils/__tests__/nodeTypesUtils.spec.ts
diff --git a/packages/editor-ui/src/composables/__tests__/useWorkflowHelpers.spec.ts b/packages/editor-ui/src/composables/__tests__/useWorkflowHelpers.spec.ts
index f50261a1aa..ac7d5addab 100644
--- a/packages/editor-ui/src/composables/__tests__/useWorkflowHelpers.spec.ts
+++ b/packages/editor-ui/src/composables/__tests__/useWorkflowHelpers.spec.ts
@@ -7,6 +7,7 @@ import { useWorkflowsStore } from '@/stores/workflows.store';
import { useWorkflowsEEStore } from '@/stores/workflows.ee.store';
import { useTagsStore } from '@/stores/tags.store';
import { createTestWorkflow } from '@/__tests__/mocks';
+import type { AssignmentCollectionValue } from 'n8n-workflow';
const getDuplicateTestWorkflow = (): IWorkflowDataUpdate => ({
name: 'Duplicate webhook test',
@@ -70,6 +71,163 @@ describe('useWorkflowHelpers', () => {
vi.clearAllMocks();
});
+ describe('getNodeParametersWithResolvedExpressions', () => {
+ it('should correctly detect and resolve expressions in a regular node ', () => {
+ const nodeParameters = {
+ curlImport: '',
+ method: 'GET',
+ url: '={{ $json.name }}',
+ authentication: 'none',
+ provideSslCertificates: false,
+ sendQuery: false,
+ sendHeaders: false,
+ sendBody: false,
+ options: {},
+ infoMessage: '',
+ };
+ const workflowHelpers = useWorkflowHelpers({ router });
+ const resolvedParameters =
+ workflowHelpers.getNodeParametersWithResolvedExpressions(nodeParameters);
+ expect(resolvedParameters.url).toHaveProperty('resolvedExpressionValue');
+ });
+
+ it('should correctly detect and resolve expressions in a node with assignments (set node) ', () => {
+ const nodeParameters = {
+ mode: 'manual',
+ duplicateItem: false,
+ assignments: {
+ assignments: [
+ {
+ id: '25d2d012-089b-424d-bfc6-642982a0711f',
+ name: 'date',
+ value:
+ "={{ DateTime.fromFormat('2023-12-12', 'dd/MM/yyyy').toISODate().plus({7, 'days' }) }}",
+ type: 'number',
+ },
+ ],
+ },
+ includeOtherFields: false,
+ options: {},
+ };
+ const workflowHelpers = useWorkflowHelpers({ router });
+ const resolvedParameters =
+ workflowHelpers.getNodeParametersWithResolvedExpressions(nodeParameters);
+ expect(resolvedParameters).toHaveProperty('assignments');
+ const assignments = resolvedParameters.assignments as AssignmentCollectionValue;
+ expect(assignments).toHaveProperty('assignments');
+ expect(assignments.assignments[0].value).toHaveProperty('resolvedExpressionValue');
+ });
+
+ it('should correctly detect and resolve expressions in a node with filter component', () => {
+ const nodeParameters = {
+ mode: 'rules',
+ rules: {
+ values: [
+ {
+ conditions: {
+ options: {
+ caseSensitive: true,
+ leftValue: '',
+ typeValidation: 'strict',
+ version: 2,
+ },
+ conditions: [
+ {
+ leftValue: "={{ $('Edit Fields 1').item.json.name }}",
+ rightValue: 12,
+ operator: {
+ type: 'number',
+ operation: 'equals',
+ },
+ },
+ ],
+ combinator: 'and',
+ },
+ renameOutput: false,
+ },
+ ],
+ },
+ looseTypeValidation: false,
+ options: {},
+ };
+ const workflowHelpers = useWorkflowHelpers({ router });
+ const resolvedParameters = workflowHelpers.getNodeParametersWithResolvedExpressions(
+ nodeParameters,
+ ) as typeof nodeParameters;
+ expect(resolvedParameters).toHaveProperty('rules');
+ expect(resolvedParameters.rules).toHaveProperty('values');
+ expect(resolvedParameters.rules.values[0].conditions.conditions[0].leftValue).toHaveProperty(
+ 'resolvedExpressionValue',
+ );
+ });
+ it('should correctly detect and resolve expressions in a node with resource locator component', () => {
+ const nodeParameters = {
+ authentication: 'oAuth2',
+ resource: 'sheet',
+ operation: 'read',
+ documentId: {
+ __rl: true,
+ value: "={{ $('Edit Fields').item.json.document }}",
+ mode: 'id',
+ },
+ sheetName: {
+ __rl: true,
+ value: "={{ $('Edit Fields').item.json.sheet }}",
+ mode: 'id',
+ },
+ filtersUI: {},
+ combineFilters: 'AND',
+ options: {},
+ };
+ const workflowHelpers = useWorkflowHelpers({ router });
+ const resolvedParameters = workflowHelpers.getNodeParametersWithResolvedExpressions(
+ nodeParameters,
+ ) as typeof nodeParameters;
+ expect(resolvedParameters.documentId.value).toHaveProperty('resolvedExpressionValue');
+ expect(resolvedParameters.sheetName.value).toHaveProperty('resolvedExpressionValue');
+ });
+ it('should correctly detect and resolve expressions in a node with resource mapper component', () => {
+ const nodeParameters = {
+ authentication: 'oAuth2',
+ resource: 'sheet',
+ operation: 'read',
+ documentId: {
+ __rl: true,
+ value: '1BAjxEhlUu5tXDCMQcjqjguIZDFuct3FYkdo7flxl3yc',
+ mode: 'list',
+ cachedResultName: 'Mapping sheet',
+ cachedResultUrl:
+ 'https://docs.google.com/spreadsheets/d/1BAjxEhlUu5tXDCMQcjqjguIZDFuct3FYkdo7flxl3yc/edit?usp=drivesdk',
+ },
+ sheetName: {
+ __rl: true,
+ value: 'gid=0',
+ mode: 'list',
+ cachedResultName: 'Users',
+ cachedResultUrl:
+ 'https://docs.google.com/spreadsheets/d/1BAjxEhlUu5tXDCMQcjqjguIZDFuct3FYkdo7flxl3yc/edit#gid=0',
+ },
+ filtersUI: {
+ values: [
+ {
+ lookupColumn: 'First name',
+ lookupValue: "={{ $('Edit Fields 1').item.json.userName }}",
+ },
+ ],
+ },
+ combineFilters: 'AND',
+ options: {},
+ };
+ const workflowHelpers = useWorkflowHelpers({ router });
+ const resolvedParameters = workflowHelpers.getNodeParametersWithResolvedExpressions(
+ nodeParameters,
+ ) as typeof nodeParameters;
+ expect(resolvedParameters.filtersUI.values[0].lookupValue).toHaveProperty(
+ 'resolvedExpressionValue',
+ );
+ });
+ });
+
describe('saveAsNewWorkflow', () => {
it('should respect `resetWebhookUrls: false` when duplicating workflows', async () => {
const workflow = getDuplicateTestWorkflow();
diff --git a/packages/editor-ui/src/composables/useWorkflowHelpers.ts b/packages/editor-ui/src/composables/useWorkflowHelpers.ts
index fd203a9ecc..2c79cb2639 100644
--- a/packages/editor-ui/src/composables/useWorkflowHelpers.ts
+++ b/packages/editor-ui/src/composables/useWorkflowHelpers.ts
@@ -693,6 +693,42 @@ export function useWorkflowHelpers(options: { router: ReturnType ({
name: ENABLED_VIEWS[0],
}),
),
+ useRouter: vi.fn(),
RouterLink: vi.fn(),
}));
diff --git a/packages/editor-ui/src/stores/assistant.store.ts b/packages/editor-ui/src/stores/assistant.store.ts
index 0bd883ad51..0ac0346e9c 100644
--- a/packages/editor-ui/src/stores/assistant.store.ts
+++ b/packages/editor-ui/src/stores/assistant.store.ts
@@ -17,7 +17,7 @@ import { useRoute } from 'vue-router';
import { useSettingsStore } from './settings.store';
import { assert } from '@/utils/assert';
import { useWorkflowsStore } from './workflows.store';
-import type { ICredentialType, INodeParameters } from 'n8n-workflow';
+import type { IDataObject, ICredentialType, INodeParameters } from 'n8n-workflow';
import { deepCopy } from 'n8n-workflow';
import { ndvEventBus, codeNodeEditorEventBus } from '@/event-bus';
import { useNDVStore } from './ndv.store';
@@ -27,7 +27,8 @@ import {
getNodeAuthOptions,
getReferencedNodes,
getNodesSchemas,
- pruneNodeProperties,
+ processNodeForAssistant,
+ isNodeReferencingInputData,
} from '@/utils/nodeTypesUtils';
import { useNodeTypesStore } from './nodeTypes.store';
import { usePostHog } from './posthog.store';
@@ -421,6 +422,16 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
const availableAuthOptions = getNodeAuthOptions(nodeType);
authType = availableAuthOptions.find((option) => option.value === credentialInUse);
}
+ let nodeInputData: { inputNodeName?: string; inputData?: IDataObject } | undefined = undefined;
+ const ndvInput = ndvStore.ndvInputData;
+ if (isNodeReferencingInputData(context.node) && ndvInput?.length) {
+ const inputData = ndvStore.ndvInputData[0].json;
+ const inputNodeName = ndvStore.input.nodeName;
+ nodeInputData = {
+ inputNodeName,
+ inputData,
+ };
+ }
addLoadingAssistantMessage(locale.baseText('aiAssistant.thinkingSteps.analyzingError'));
openChat();
@@ -435,7 +446,8 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
firstName: usersStore.currentUser?.firstName ?? '',
},
error: context.error,
- node: pruneNodeProperties(context.node, ['position']),
+ node: processNodeForAssistant(context.node, ['position']),
+ nodeInputData,
executionSchema: schemas,
authType,
},
diff --git a/packages/editor-ui/src/types/assistant.types.ts b/packages/editor-ui/src/types/assistant.types.ts
index 69749cee73..1b6ea0a089 100644
--- a/packages/editor-ui/src/types/assistant.types.ts
+++ b/packages/editor-ui/src/types/assistant.types.ts
@@ -1,8 +1,8 @@
import type { Schema } from '@/Interface';
-import type { INode, INodeParameters } from 'n8n-workflow';
+import type { IDataObject, INode, INodeParameters } from 'n8n-workflow';
export namespace ChatRequest {
- interface NodeExecutionSchema {
+ export interface NodeExecutionSchema {
nodeName: string;
schema: Schema;
}
@@ -21,6 +21,7 @@ export namespace ChatRequest {
stack?: string;
};
node: INode;
+ nodeInputData?: IDataObject;
}
export interface InitErrorHelper extends ErrorContext, WorkflowContext {
diff --git a/packages/editor-ui/src/utils/__tests__/nodeTypesUtils.spec.ts b/packages/editor-ui/src/utils/__tests__/nodeTypesUtils.spec.ts
new file mode 100644
index 0000000000..118095ed24
--- /dev/null
+++ b/packages/editor-ui/src/utils/__tests__/nodeTypesUtils.spec.ts
@@ -0,0 +1,383 @@
+import { describe, it, expect } from 'vitest';
+import { getReferencedNodes } from '../nodeTypesUtils';
+import type { INode } from 'n8n-workflow';
+
+const referencedNodesTestCases: Array<{ caseName: string; node: INode; expected: string[] }> = [
+ {
+ caseName: 'Should return an empty array if no referenced nodes',
+ node: {
+ parameters: {
+ curlImport: '',
+ method: 'GET',
+ url: 'https://httpbin.org/get1',
+ authentication: 'none',
+ provideSslCertificates: false,
+ sendQuery: false,
+ sendHeaders: false,
+ sendBody: false,
+ options: {},
+ infoMessage: '',
+ },
+ type: 'n8n-nodes-base.httpRequest',
+ typeVersion: 4.2,
+ position: [220, 220],
+ id: 'edc36001-aee7-4052-b66e-cf127f4b6ea5',
+ name: 'HTTP Request',
+ },
+ expected: [],
+ },
+ {
+ caseName: 'Should return an array of references for regular node',
+ node: {
+ parameters: {
+ authentication: 'oAuth2',
+ resource: 'sheet',
+ operation: 'read',
+ documentId: {
+ __rl: true,
+ value: "={{ $('Edit Fields').item.json.document }}",
+ mode: 'id',
+ },
+ sheetName: {
+ __rl: true,
+ value: "={{ $('Edit Fields 2').item.json.sheet }}",
+ mode: 'id',
+ },
+ filtersUI: {},
+ combineFilters: 'AND',
+ options: {},
+ },
+ type: 'n8n-nodes-base.googleSheets',
+ typeVersion: 4.5,
+ position: [440, 0],
+ id: '9a95ad27-06cf-4076-af6b-52846a109a8b',
+ name: 'Google Sheets',
+ credentials: {
+ googleSheetsOAuth2Api: {
+ id: '8QEpi028oHDLXntS',
+ name: 'milorad@n8n.io',
+ },
+ },
+ },
+ expected: ['Edit Fields', 'Edit Fields 2'],
+ },
+ {
+ caseName: 'Should return an array of references for set node',
+ node: {
+ parameters: {
+ mode: 'manual',
+ duplicateItem: false,
+ assignments: {
+ assignments: [
+ {
+ id: '135e0eb0-f412-430d-8990-731c57cf43ae',
+ name: 'document',
+ value: "={{ $('Edit Fields 2').item.json.document}}",
+ type: 'string',
+ },
+ ],
+ },
+ includeOtherFields: false,
+ options: {},
+ },
+ type: 'n8n-nodes-base.set',
+ typeVersion: 3.4,
+ position: [560, -140],
+ id: '7306745f-ba8c-451d-ae1a-c627f60fbdd3',
+ name: 'Edit Fields 2',
+ },
+ expected: ['Edit Fields 2'],
+ },
+ {
+ caseName: 'Should handle expressions with single quotes, double quotes and backticks',
+ node: {
+ parameters: {
+ authentication: 'oAuth2',
+ resource: 'sheet',
+ operation: 'read',
+ documentId: {
+ __rl: true,
+ value: "={{ $('Edit Fields').item.json.document }}",
+ mode: 'id',
+ },
+ sheetName: {
+ __rl: true,
+ value: '={{ $("Edit Fields 2").item.json.sheet }}',
+ mode: 'id',
+ },
+ rowName: {
+ __rl: true,
+ value: '={{ $(`Edit Fields 3`).item.json.row }}',
+ mode: 'id',
+ },
+ filtersUI: {},
+ combineFilters: 'AND',
+ options: {},
+ },
+ type: 'n8n-nodes-base.googleSheets',
+ typeVersion: 4.5,
+ position: [440, 0],
+ id: '9a95ad27-06cf-4076-af6b-52846a109a8b',
+ name: 'Google Sheets',
+ credentials: {
+ googleSheetsOAuth2Api: {
+ id: '8QEpi028oHDLXntS',
+ name: 'milorad@n8n.io',
+ },
+ },
+ },
+ expected: ['Edit Fields', 'Edit Fields 2', 'Edit Fields 3'],
+ },
+ {
+ caseName: 'Should only add one reference for each referenced node',
+ node: {
+ parameters: {
+ authentication: 'oAuth2',
+ resource: 'sheet',
+ operation: 'read',
+ documentId: {
+ __rl: true,
+ value: "={{ $('Edit Fields').item.json.document }}",
+ mode: 'id',
+ },
+ sheetName: {
+ __rl: true,
+ value: "={{ $('Edit Fields').item.json.sheet }}",
+ mode: 'id',
+ },
+ filtersUI: {},
+ combineFilters: 'AND',
+ options: {},
+ },
+ type: 'n8n-nodes-base.googleSheets',
+ typeVersion: 4.5,
+ position: [440, 0],
+ id: '9a95ad27-06cf-4076-af6b-52846a109a8b',
+ name: 'Google Sheets',
+ credentials: {
+ googleSheetsOAuth2Api: {
+ id: '8QEpi028oHDLXntS',
+ name: 'milorad@n8n.io',
+ },
+ },
+ },
+ expected: ['Edit Fields'],
+ },
+ {
+ caseName: 'Should handle multiple node references in one expression',
+ node: {
+ parameters: {
+ curlImport: '',
+ method: 'GET',
+ url: "={{ $('Edit Fields').item.json.one }} {{ $('Edit Fields 2').item.json.two }} {{ $('Edit Fields').item.json.three }}",
+ authentication: 'none',
+ provideSslCertificates: false,
+ sendQuery: false,
+ sendHeaders: false,
+ sendBody: false,
+ options: {},
+ infoMessage: '',
+ },
+ type: 'n8n-nodes-base.httpRequest',
+ typeVersion: 4.2,
+ position: [220, 220],
+ id: 'edc36001-aee7-4052-b66e-cf127f4b6ea5',
+ name: 'HTTP Request',
+ },
+ expected: ['Edit Fields', 'Edit Fields 2'],
+ },
+ {
+ caseName: 'Should respect whitespace around node references',
+ node: {
+ parameters: {
+ curlImport: '',
+ method: 'GET',
+ url: "={{ $(' Edit Fields ').item.json.one }}",
+ authentication: 'none',
+ provideSslCertificates: false,
+ sendQuery: false,
+ sendHeaders: false,
+ sendBody: false,
+ options: {},
+ infoMessage: '',
+ },
+ type: 'n8n-nodes-base.httpRequest',
+ typeVersion: 4.2,
+ position: [220, 220],
+ id: 'edc36001-aee7-4052-b66e-cf127f4b6ea5',
+ name: 'HTTP Request',
+ },
+ expected: [' Edit Fields '],
+ },
+ {
+ caseName: 'Should ignore whitespace inside expressions',
+ node: {
+ parameters: {
+ curlImport: '',
+ method: 'GET',
+ url: "={{ $( 'Edit Fields' ).item.json.one }}",
+ authentication: 'none',
+ provideSslCertificates: false,
+ sendQuery: false,
+ sendHeaders: false,
+ sendBody: false,
+ options: {},
+ infoMessage: '',
+ },
+ type: 'n8n-nodes-base.httpRequest',
+ typeVersion: 4.2,
+ position: [220, 220],
+ id: 'edc36001-aee7-4052-b66e-cf127f4b6ea5',
+ name: 'HTTP Request',
+ },
+ expected: ['Edit Fields'],
+ },
+ {
+ caseName: 'Should ignore special characters in node references',
+ node: {
+ parameters: {
+ curlImport: '',
+ method: 'GET',
+ url: "={{ $( 'Ignore ' this' ).item.json.document }",
+ authentication: 'none',
+ provideSslCertificates: false,
+ sendQuery: false,
+ sendHeaders: false,
+ sendBody: false,
+ options: {},
+ infoMessage: '',
+ },
+ type: 'n8n-nodes-base.httpRequest',
+ typeVersion: 4.2,
+ position: [220, 220],
+ id: 'edc36001-aee7-4052-b66e-cf127f4b6ea5',
+ name: 'HTTP Request',
+ },
+ expected: [],
+ },
+ {
+ caseName: 'Should correctly detect node names that contain single quotes',
+ node: {
+ parameters: {
+ curlImport: '',
+ method: 'GET',
+ // In order to carry over backslashes to test function, the string needs to be double escaped
+ url: "={{ $('Edit \\'Fields\\' 2').item.json.name }}",
+ authentication: 'none',
+ provideSslCertificates: false,
+ sendQuery: false,
+ sendHeaders: false,
+ sendBody: false,
+ options: {},
+ infoMessage: '',
+ },
+ type: 'n8n-nodes-base.httpRequest',
+ typeVersion: 4.2,
+ position: [220, 220],
+ id: 'edc36001-aee7-4052-b66e-cf127f4b6ea5',
+ name: 'HTTP Request',
+ },
+ expected: ["Edit 'Fields' 2"],
+ },
+ {
+ caseName: 'Should correctly detect node names with inner backticks',
+ node: {
+ parameters: {
+ curlImport: '',
+ method: 'GET',
+ url: "={{ $('Edit `Fields` 2').item.json.name }}",
+ authentication: 'none',
+ provideSslCertificates: false,
+ sendQuery: false,
+ sendHeaders: false,
+ sendBody: false,
+ options: {},
+ infoMessage: '',
+ },
+ type: 'n8n-nodes-base.httpRequest',
+ typeVersion: 4.2,
+ position: [220, 220],
+ id: 'edc36001-aee7-4052-b66e-cf127f4b6ea5',
+ name: 'HTTP Request',
+ },
+ expected: ['Edit `Fields` 2'],
+ },
+ {
+ caseName: 'Should correctly detect node names with inner escaped backticks',
+ node: {
+ parameters: {
+ curlImport: '',
+ method: 'GET',
+ url: '={{ $(`Edit \\`Fields\\` 2`).item.json.name }}',
+ authentication: 'none',
+ provideSslCertificates: false,
+ sendQuery: false,
+ sendHeaders: false,
+ sendBody: false,
+ options: {},
+ infoMessage: '',
+ },
+ type: 'n8n-nodes-base.httpRequest',
+ typeVersion: 4.2,
+ position: [220, 220],
+ id: 'edc36001-aee7-4052-b66e-cf127f4b6ea5',
+ name: 'HTTP Request',
+ },
+ expected: ['Edit `Fields` 2'],
+ },
+ {
+ caseName: 'Should correctly detect node names with inner escaped double quotes',
+ node: {
+ parameters: {
+ curlImport: '',
+ method: 'GET',
+ // In order to carry over backslashes to test function, the string needs to be double escaped
+ url: '={{ $("Edit \\"Fields\\" 2").item.json.name }}',
+ authentication: 'none',
+ provideSslCertificates: false,
+ sendQuery: false,
+ sendHeaders: false,
+ sendBody: false,
+ options: {},
+ infoMessage: '',
+ },
+ type: 'n8n-nodes-base.httpRequest',
+ typeVersion: 4.2,
+ position: [220, 220],
+ id: 'edc36001-aee7-4052-b66e-cf127f4b6ea5',
+ name: 'HTTP Request',
+ },
+ expected: ['Edit "Fields" 2'],
+ },
+ {
+ caseName: 'Should not detect invalid expressions',
+ node: {
+ parameters: {
+ curlImport: '',
+ method: 'GET',
+ // String not closed properly
+ url: "={{ $('Edit ' fields').item.json.document }",
+ // Mixed quotes
+ url2: '{{ $("Edit \'Fields" 2").item.json.name }}',
+ url3: '{{ $("Edit `Fields" 2").item.json.name }}',
+ // Quotes not escaped
+ url4: '{{ $("Edit "Fields" 2").item.json.name }}',
+ url5: "{{ $('Edit 'Fields' 2').item.json.name }}",
+ url6: '{{ $(`Edit `Fields` 2`).item.json.name }}',
+ },
+ type: 'n8n-nodes-base.httpRequest',
+ typeVersion: 4.2,
+ position: [220, 220],
+ id: 'edc36001-aee7-4052-b66e-cf127f4b6ea5',
+ name: 'HTTP Request',
+ },
+ expected: [],
+ },
+];
+
+describe.each(referencedNodesTestCases)('getReferencedNodes', (testCase) => {
+ const caseName = testCase.caseName;
+ it(`${caseName}`, () => {
+ expect(getReferencedNodes(testCase.node)).toEqual(testCase.expected);
+ });
+});
diff --git a/packages/editor-ui/src/utils/nodeTypesUtils.ts b/packages/editor-ui/src/utils/nodeTypesUtils.ts
index 70fa803411..03f930c740 100644
--- a/packages/editor-ui/src/utils/nodeTypesUtils.ts
+++ b/packages/editor-ui/src/utils/nodeTypesUtils.ts
@@ -5,10 +5,10 @@ import type {
ITemplatesNode,
IVersionNode,
NodeAuthenticationOption,
- Schema,
SimplifiedNodeType,
} from '@/Interface';
import { useDataSchema } from '@/composables/useDataSchema';
+import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
import {
CORE_NODES_CATEGORY,
MAIN_AUTH_FIELD_NAME,
@@ -20,20 +20,22 @@ import { i18n as locale } from '@/plugins/i18n';
import { useCredentialsStore } from '@/stores/credentials.store';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { useWorkflowsStore } from '@/stores/workflows.store';
+import type { ChatRequest } from '@/types/assistant.types';
import { isResourceLocatorValue } from '@/utils/typeGuards';
import { isJsonKeyObject } from '@/utils/typesUtils';
-import type {
- AssignmentCollectionValue,
- IDataObject,
- INode,
- INodeCredentialDescription,
- INodeExecutionData,
- INodeProperties,
- INodeTypeDescription,
- NodeParameterValueType,
- ResourceMapperField,
- Themed,
+import {
+ deepCopy,
+ type IDataObject,
+ type INode,
+ type INodeCredentialDescription,
+ type INodeExecutionData,
+ type INodeProperties,
+ type INodeTypeDescription,
+ type NodeParameterValueType,
+ type ResourceMapperField,
+ type Themed,
} from 'n8n-workflow';
+import { useRouter } from 'vue-router';
/*
Constants and utility functions mainly used to get information about
@@ -503,9 +505,9 @@ export const getNodeIconColor = (
/**
Regular expression to extract the node names from the expressions in the template.
- Example: $(expression) => expression
+ Supports single quotes, double quotes, and backticks.
*/
-const entityRegex = /\$\((['"])(.*?)\1\)/g;
+const entityRegex = /\$\(\s*(\\?["'`])((?:\\.|(?!\1)[^\\])*)\1\s*\)/g;
/**
* Extract the node names from the expressions in the template.
@@ -520,81 +522,89 @@ function extractNodeNames(template: string): string[] {
}
/**
- * Extract the node names from the expressions in the node parameters.
+ * Unescape quotes in the string. Supports single quotes, double quotes, and backticks.
*/
-export function getReferencedNodes(node: INode): string[] {
- const referencedNodes: string[] = [];
- if (!node) {
- return referencedNodes;
- }
- // Special case for code node
- if (node.type === 'n8n-nodes-base.set' && node.parameters.assignments) {
- const assignments = node.parameters.assignments as AssignmentCollectionValue;
- if (assignments.assignments?.length) {
- assignments.assignments.forEach((assignment) => {
- if (assignment.name && assignment.value && String(assignment.value).startsWith('=')) {
- const nodeNames = extractNodeNames(String(assignment.value));
- if (nodeNames.length) {
- referencedNodes.push(...nodeNames);
- }
- }
- });
- }
- } else {
- Object.values(node.parameters).forEach((value) => {
- if (!value) {
- return;
- }
- let strValue = String(value);
- // Handle resource locator
- if (typeof value === 'object' && 'value' in value) {
- strValue = String(value.value);
- }
- if (strValue.startsWith('=')) {
- const nodeNames = extractNodeNames(strValue);
- if (nodeNames.length) {
- referencedNodes.push(...nodeNames);
- }
- }
- });
- }
- return referencedNodes;
+export function unescapeQuotes(str: string): string {
+ return str.replace(/\\(['"`])/g, '$1');
}
/**
- * Remove properties from a node based on the provided list of property names.
- * Reruns a new node object with the properties removed.
+ * Extract the node names from the expressions in the node parameters.
*/
-export function pruneNodeProperties(node: INode, propsToRemove: string[]): INode {
- const prunedNode = { ...node };
+export function getReferencedNodes(node: INode): string[] {
+ const referencedNodes: Set = new Set();
+ if (!node) {
+ return [];
+ }
+ // Go through all parameters and check if they contain expressions on any level
+ for (const key in node.parameters) {
+ let names: string[] = [];
+ if (
+ node.parameters[key] &&
+ typeof node.parameters[key] === 'object' &&
+ Object.keys(node.parameters[key]).length
+ ) {
+ names = extractNodeNames(JSON.stringify(node.parameters[key]));
+ } else if (typeof node.parameters[key] === 'string' && node.parameters[key]) {
+ names = extractNodeNames(node.parameters[key]);
+ }
+ if (names.length) {
+ names
+ .map((name) => unescapeQuotes(name))
+ .forEach((name) => {
+ referencedNodes.add(name);
+ });
+ }
+ }
+ return referencedNodes.size ? Array.from(referencedNodes) : [];
+}
+
+/**
+ * Processes node object before sending it to AI assistant
+ * - Removes unnecessary properties
+ * - Extracts expressions from the parameters and resolves them
+ * @param node original node object
+ * @param propsToRemove properties to remove from the node object
+ * @returns processed node
+ */
+export function processNodeForAssistant(node: INode, propsToRemove: string[]): INode {
+ // Make a copy of the node object so we don't modify the original
+ const nodeForLLM = deepCopy(node);
propsToRemove.forEach((key) => {
- delete prunedNode[key as keyof INode];
+ delete nodeForLLM[key as keyof INode];
});
- return prunedNode;
+ const workflowHelpers = useWorkflowHelpers({ router: useRouter() });
+ const resolvedParameters = workflowHelpers.getNodeParametersWithResolvedExpressions(
+ nodeForLLM.parameters,
+ );
+ nodeForLLM.parameters = resolvedParameters;
+ return nodeForLLM;
+}
+
+export function isNodeReferencingInputData(node: INode): boolean {
+ const parametersString = JSON.stringify(node.parameters);
+ const references = ['$json', '$input', '$binary'];
+ return references.some((ref) => parametersString.includes(ref));
}
/**
* Get the schema for the referenced nodes as expected by the AI assistant
* @param nodeNames The names of the nodes to get the schema for
- * @returns An array of objects containing the node name and the schema
+ * @returns An array of NodeExecutionSchema objects
*/
export function getNodesSchemas(nodeNames: string[]) {
- return nodeNames.map((name) => {
+ const schemas: ChatRequest.NodeExecutionSchema[] = [];
+ for (const name of nodeNames) {
const node = useWorkflowsStore().getNodeByName(name);
if (!node) {
- return {
- nodeName: name,
- schema: {} as Schema,
- };
+ continue;
}
const { getSchemaForExecutionData, getInputDataWithPinned } = useDataSchema();
- const schema = getSchemaForExecutionData(
- executionDataToJson(getInputDataWithPinned(node)),
- true,
- );
- return {
+ const schema = getSchemaForExecutionData(executionDataToJson(getInputDataWithPinned(node)));
+ schemas.push({
nodeName: node.name,
schema,
- };
- });
+ });
+ }
+ return schemas;
}
From 0b5299a248fdd451ceabb98ff6a2b38e818d02f8 Mon Sep 17 00:00:00 2001
From: Eugene
Date: Tue, 17 Sep 2024 16:27:21 +0200
Subject: [PATCH 28/42] fix(Azure OpenAI Chat Model Node): Add response format
option (#10851)
---
.../LmChatAzureOpenAi.node.ts | 41 +++++++++++++++++--
1 file changed, 37 insertions(+), 4 deletions(-)
diff --git a/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.ts b/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.ts
index 96f5b8e6d8..03548142db 100644
--- a/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.ts
+++ b/packages/@n8n/nodes-langchain/nodes/llms/LmChatAzureOpenAi/LmChatAzureOpenAi.node.ts
@@ -7,7 +7,6 @@ import {
type SupplyData,
} from 'n8n-workflow';
-import type { ClientOptions } from '@langchain/openai';
import { ChatOpenAI } from '@langchain/openai';
import { getConnectionHintNoticeField } from '../../../utils/sharedFields';
import { N8nLlmTracing } from '../N8nLlmTracing';
@@ -51,6 +50,18 @@ export class LmChatAzureOpenAi implements INodeType {
],
properties: [
getConnectionHintNoticeField([NodeConnectionType.AiChain, NodeConnectionType.AiAgent]),
+ {
+ displayName:
+ 'If using JSON response format, you must include word "json" in the prompt in your chain or agent. Also, make sure to select latest models released post November 2023.',
+ name: 'notice',
+ type: 'notice',
+ default: '',
+ displayOptions: {
+ show: {
+ '/options.responseFormat': ['json_object'],
+ },
+ },
+ },
{
displayName: 'Model (Deployment) Name',
name: 'model',
@@ -86,6 +97,25 @@ export class LmChatAzureOpenAi implements INodeType {
maxValue: 32768,
},
},
+ {
+ displayName: 'Response Format',
+ name: 'responseFormat',
+ default: 'text',
+ type: 'options',
+ options: [
+ {
+ name: 'Text',
+ value: 'text',
+ description: 'Regular text response',
+ },
+ {
+ name: 'JSON',
+ value: 'json_object',
+ description:
+ 'Enables JSON mode, which should guarantee the message the model generates is valid JSON',
+ },
+ ],
+ },
{
displayName: 'Presence Penalty',
name: 'presencePenalty',
@@ -148,10 +178,9 @@ export class LmChatAzureOpenAi implements INodeType {
presencePenalty?: number;
temperature?: number;
topP?: number;
+ responseFormat?: 'text' | 'json_object';
};
- const configuration: ClientOptions = {};
-
const model = new ChatOpenAI({
azureOpenAIApiDeploymentName: modelName,
azureOpenAIApiInstanceName: credentials.resourceName,
@@ -160,8 +189,12 @@ export class LmChatAzureOpenAi implements INodeType {
...options,
timeout: options.timeout ?? 60000,
maxRetries: options.maxRetries ?? 2,
- configuration,
callbacks: [new N8nLlmTracing(this)],
+ modelKwargs: options.responseFormat
+ ? {
+ response_format: { type: options.responseFormat },
+ }
+ : undefined,
});
return {
From 3c15890a5bd20ae19dc33e038021df9fe1d4a00b Mon Sep 17 00:00:00 2001
From: Tomi Turtiainen <10324676+tomi@users.noreply.github.com>
Date: Tue, 17 Sep 2024 17:37:07 +0300
Subject: [PATCH 29/42] build(benchmark): Fix docker image build (#10854)
---
.devcontainer/devcontainer.json | 2 +-
docker/images/n8n-custom/Dockerfile | 2 +-
package.json | 2 +-
packages/@n8n/benchmark/Dockerfile | 1 +
scripts/prepare.mjs | 10 ++++++++++
5 files changed, 14 insertions(+), 3 deletions(-)
create mode 100644 scripts/prepare.mjs
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index c81220d1b4..7b7a1e89d2 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -9,7 +9,7 @@
"type=bind,source=${localEnv:HOME}/.n8n,target=/home/node/.n8n,consistency=cached"
],
"forwardPorts": [8080, 5678],
- "postCreateCommand": "corepack prepare --activate && pnpm install ",
+ "postCreateCommand": "corepack prepare --activate && pnpm install",
"postAttachCommand": "pnpm build",
"customizations": {
"codespaces": {
diff --git a/docker/images/n8n-custom/Dockerfile b/docker/images/n8n-custom/Dockerfile
index 17f0d1c517..d0c8f0b157 100644
--- a/docker/images/n8n-custom/Dockerfile
+++ b/docker/images/n8n-custom/Dockerfile
@@ -6,7 +6,7 @@ FROM --platform=linux/amd64 n8nio/base:${NODE_VERSION} AS builder
# Build the application from source
WORKDIR /src
COPY . /src
-RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store --mount=type=cache,id=pnpm-metadata,target=/root/.cache/pnpm/metadata pnpm install --frozen-lockfile
+RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store --mount=type=cache,id=pnpm-metadata,target=/root/.cache/pnpm/metadata DOCKER_BUILD=true pnpm install --frozen-lockfile
RUN pnpm build
# Delete all dev dependencies
diff --git a/package.json b/package.json
index 733edd2e24..98d9b2cc20 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
},
"packageManager": "pnpm@9.6.0",
"scripts": {
- "prepare": "lefthook install",
+ "prepare": "node scripts/prepare.mjs",
"preinstall": "node scripts/block-npm-install.js",
"build": "turbo run build",
"build:backend": "turbo run build:backend",
diff --git a/packages/@n8n/benchmark/Dockerfile b/packages/@n8n/benchmark/Dockerfile
index 5fa1aeae93..9525a9a4c2 100644
--- a/packages/@n8n/benchmark/Dockerfile
+++ b/packages/@n8n/benchmark/Dockerfile
@@ -33,6 +33,7 @@ COPY --chown=node:node ./packages/@n8n/benchmark/package.json /app/packages/@n8n
COPY --chown=node:node ./patches /app/patches
COPY --chown=node:node ./scripts /app/scripts
+ENV DOCKER_BUILD=true
RUN pnpm install --frozen-lockfile
# TS config files
diff --git a/scripts/prepare.mjs b/scripts/prepare.mjs
new file mode 100644
index 0000000000..d729715347
--- /dev/null
+++ b/scripts/prepare.mjs
@@ -0,0 +1,10 @@
+#!/usr/bin/env node
+
+import { execSync } from 'node:child_process';
+
+// Skip lefthook install in CI or Docker build
+if (process.env.CI || process.env.DOCKER_BUILD) {
+ process.exit(0);
+}
+
+execSync('./node_modules/.bin/lefthook install', { stdio: 'inherit' });
From 430c14ad19b4721ff5780d48bec21606061414a1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?=
=?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?=
=?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?=
Date: Tue, 17 Sep 2024 17:21:11 +0200
Subject: [PATCH 30/42] refactor(core): Move frontend settings to
@n8n/api-types (no-changelog) (#10856)
---
cypress/package.json | 1 +
cypress/support/commands.ts | 6 +-
cypress/support/index.ts | 4 +-
.../@n8n/api-types/src/frontend-settings.ts | 172 ++++++++++++++++++
packages/@n8n/api-types/src/index.ts | 3 +-
packages/cli/src/events/relay-event-map.ts | 2 +-
packages/cli/src/interfaces.ts | 6 -
packages/cli/src/server.ts | 4 +-
packages/cli/src/services/frontend.service.ts | 12 +-
packages/editor-ui/src/Interface.ts | 19 +-
packages/editor-ui/src/__tests__/defaults.ts | 4 +-
packages/editor-ui/src/api/settings.ts | 4 +-
.../editor-ui/src/components/Telemetry.vue | 2 +-
.../editor-ui/src/plugins/telemetry/index.ts | 3 +-
.../src/stores/__tests__/posthog.test.ts | 8 +-
.../src/stores/__tests__/sso.test.ts | 4 +-
.../__tests__/workflowHistory.store.test.ts | 4 +-
.../editor-ui/src/stores/settings.store.ts | 16 +-
.../editor-ui/src/stores/versions.store.ts | 3 +-
.../src/views/ProjectSettings.test.ts | 4 +-
packages/workflow/src/Authentication.ts | 1 -
packages/workflow/src/Interfaces.ts | 172 ------------------
packages/workflow/src/index.ts | 1 -
pnpm-lock.yaml | 3 +
24 files changed, 223 insertions(+), 235 deletions(-)
create mode 100644 packages/@n8n/api-types/src/frontend-settings.ts
delete mode 100644 packages/workflow/src/Authentication.ts
diff --git a/cypress/package.json b/cypress/package.json
index 509abca04b..e64be91c2b 100644
--- a/cypress/package.json
+++ b/cypress/package.json
@@ -15,6 +15,7 @@
"start": "cd ..; pnpm start"
},
"devDependencies": {
+ "@n8n/api-types": "workspace:*",
"@types/lodash": "catalog:",
"eslint-plugin-cypress": "^3.3.0",
"n8n-workflow": "workspace:*"
diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts
index 7bef3727dc..808c0b6aca 100644
--- a/cypress/support/commands.ts
+++ b/cypress/support/commands.ts
@@ -1,6 +1,6 @@
import 'cypress-real-events';
import FakeTimers from '@sinonjs/fake-timers';
-import type { IN8nUISettings } from 'n8n-workflow';
+import type { FrontendSettings } from '@n8n/api-types';
import { WorkflowPage } from '../pages';
import {
BACKEND_BASE_URL,
@@ -86,8 +86,8 @@ Cypress.Commands.add('signout', () => {
cy.getCookie(N8N_AUTH_COOKIE).should('not.exist');
});
-export let settings: Partial;
-Cypress.Commands.add('overrideSettings', (value: Partial) => {
+export let settings: Partial;
+Cypress.Commands.add('overrideSettings', (value: Partial) => {
settings = value;
});
diff --git a/cypress/support/index.ts b/cypress/support/index.ts
index 7c1897b11f..a5f1caf5b2 100644
--- a/cypress/support/index.ts
+++ b/cypress/support/index.ts
@@ -1,7 +1,7 @@
// Load type definitions that come with Cypress module
///
-import type { IN8nUISettings } from 'n8n-workflow';
+import type { FrontendSettings } from '@n8n/api-types';
Cypress.Keyboard.defaults({
keystrokeDelay: 0,
@@ -45,7 +45,7 @@ declare global {
*/
signinAsMember(index?: number): void;
signout(): void;
- overrideSettings(value: Partial): void;
+ overrideSettings(value: Partial): void;
enableFeature(feature: string): void;
disableFeature(feature: string): void;
enableQueueMode(): void;
diff --git a/packages/@n8n/api-types/src/frontend-settings.ts b/packages/@n8n/api-types/src/frontend-settings.ts
new file mode 100644
index 0000000000..8815f14f05
--- /dev/null
+++ b/packages/@n8n/api-types/src/frontend-settings.ts
@@ -0,0 +1,172 @@
+import type { ExpressionEvaluatorType, LogLevel, WorkflowSettings } from 'n8n-workflow';
+
+export interface IVersionNotificationSettings {
+ enabled: boolean;
+ endpoint: string;
+ infoUrl: string;
+}
+
+export interface ITelemetryClientConfig {
+ url: string;
+ key: string;
+}
+
+export interface ITelemetrySettings {
+ enabled: boolean;
+ config?: ITelemetryClientConfig;
+}
+
+export type AuthenticationMethod = 'email' | 'ldap' | 'saml';
+
+export interface IUserManagementSettings {
+ quota: number;
+ showSetupOnFirstLoad?: boolean;
+ smtpSetup: boolean;
+ authenticationMethod: AuthenticationMethod;
+}
+
+export interface FrontendSettings {
+ isDocker?: boolean;
+ databaseType: 'sqlite' | 'mariadb' | 'mysqldb' | 'postgresdb';
+ endpointForm: string;
+ endpointFormTest: string;
+ endpointFormWaiting: string;
+ endpointWebhook: string;
+ endpointWebhookTest: string;
+ saveDataErrorExecution: WorkflowSettings.SaveDataExecution;
+ saveDataSuccessExecution: WorkflowSettings.SaveDataExecution;
+ saveManualExecutions: boolean;
+ saveExecutionProgress: boolean;
+ executionTimeout: number;
+ maxExecutionTimeout: number;
+ workflowCallerPolicyDefaultOption: WorkflowSettings.CallerPolicy;
+ oauthCallbackUrls: {
+ oauth1: string;
+ oauth2: string;
+ };
+ timezone: string;
+ urlBaseWebhook: string;
+ urlBaseEditor: string;
+ versionCli: string;
+ nodeJsVersion: string;
+ concurrency: number;
+ authCookie: {
+ secure: boolean;
+ };
+ binaryDataMode: 'default' | 'filesystem' | 's3';
+ releaseChannel: 'stable' | 'beta' | 'nightly' | 'dev';
+ n8nMetadata?: {
+ userId?: string;
+ [key: string]: string | number | undefined;
+ };
+ versionNotifications: IVersionNotificationSettings;
+ instanceId: string;
+ telemetry: ITelemetrySettings;
+ posthog: {
+ enabled: boolean;
+ apiHost: string;
+ apiKey: string;
+ autocapture: boolean;
+ disableSessionRecording: boolean;
+ debug: boolean;
+ };
+ personalizationSurveyEnabled: boolean;
+ defaultLocale: string;
+ userManagement: IUserManagementSettings;
+ sso: {
+ saml: {
+ loginLabel: string;
+ loginEnabled: boolean;
+ };
+ ldap: {
+ loginLabel: string;
+ loginEnabled: boolean;
+ };
+ };
+ publicApi: {
+ enabled: boolean;
+ latestVersion: number;
+ path: string;
+ swaggerUi: {
+ enabled: boolean;
+ };
+ };
+ workflowTagsDisabled: boolean;
+ logLevel: LogLevel;
+ hiringBannerEnabled: boolean;
+ previewMode: boolean;
+ templates: {
+ enabled: boolean;
+ host: string;
+ };
+ missingPackages?: boolean;
+ executionMode: 'regular' | 'queue';
+ pushBackend: 'sse' | 'websocket';
+ communityNodesEnabled: boolean;
+ aiAssistant: {
+ enabled: boolean;
+ };
+ deployment: {
+ type: string;
+ };
+ isNpmAvailable: boolean;
+ allowedModules: {
+ builtIn?: string[];
+ external?: string[];
+ };
+ enterprise: {
+ sharing: boolean;
+ ldap: boolean;
+ saml: boolean;
+ logStreaming: boolean;
+ advancedExecutionFilters: boolean;
+ variables: boolean;
+ sourceControl: boolean;
+ auditLogs: boolean;
+ externalSecrets: boolean;
+ showNonProdBanner: boolean;
+ debugInEditor: boolean;
+ binaryDataS3: boolean;
+ workflowHistory: boolean;
+ workerView: boolean;
+ advancedPermissions: boolean;
+ projects: {
+ team: {
+ limit: number;
+ };
+ };
+ };
+ hideUsagePage: boolean;
+ license: {
+ planName?: string;
+ consumerId: string;
+ environment: 'development' | 'production' | 'staging';
+ };
+ variables: {
+ limit: number;
+ };
+ expressions: {
+ evaluator: ExpressionEvaluatorType;
+ };
+ mfa: {
+ enabled: boolean;
+ };
+ banners: {
+ dismissed: string[];
+ };
+ ai: {
+ enabled: boolean;
+ };
+ workflowHistory: {
+ pruneTime: number;
+ licensePruneTime: number;
+ };
+ pruning: {
+ isEnabled: boolean;
+ maxAge: number;
+ maxCount: number;
+ };
+ security: {
+ blockFileAccessToN8nFiles: boolean;
+ };
+}
diff --git a/packages/@n8n/api-types/src/index.ts b/packages/@n8n/api-types/src/index.ts
index 5d59a1b768..89b6a3541c 100644
--- a/packages/@n8n/api-types/src/index.ts
+++ b/packages/@n8n/api-types/src/index.ts
@@ -1,6 +1,7 @@
+export type * from './datetime';
export type * from './push';
export type * from './scaling';
-export type * from './datetime';
+export type * from './frontend-settings';
export type * from './user';
export type { Collaborator } from './push/collaboration';
diff --git a/packages/cli/src/events/relay-event-map.ts b/packages/cli/src/events/relay-event-map.ts
index cf2d5f9cfb..a53a36842e 100644
--- a/packages/cli/src/events/relay-event-map.ts
+++ b/packages/cli/src/events/relay-event-map.ts
@@ -1,5 +1,5 @@
+import type { AuthenticationMethod } from '@n8n/api-types';
import type {
- AuthenticationMethod,
IPersonalizationSurveyAnswersV4,
IRun,
IWorkflowBase,
diff --git a/packages/cli/src/interfaces.ts b/packages/cli/src/interfaces.ts
index aeb2573d55..02da5a7c77 100644
--- a/packages/cli/src/interfaces.ts
+++ b/packages/cli/src/interfaces.ts
@@ -239,12 +239,6 @@ export interface IExternalHooksFunctions {
};
}
-export interface IVersionNotificationSettings {
- enabled: boolean;
- endpoint: string;
- infoUrl: string;
-}
-
export interface IPersonalizationSurveyAnswers {
email: string | null;
codingSkill: string | null;
diff --git a/packages/cli/src/server.ts b/packages/cli/src/server.ts
index e7d1eaabfb..12bf5e6b40 100644
--- a/packages/cli/src/server.ts
+++ b/packages/cli/src/server.ts
@@ -1,10 +1,10 @@
+import type { FrontendSettings } from '@n8n/api-types';
import { exec as callbackExec } from 'child_process';
import cookieParser from 'cookie-parser';
import express from 'express';
import { access as fsAccess } from 'fs/promises';
import helmet from 'helmet';
import { InstanceSettings } from 'n8n-core';
-import type { IN8nUISettings } from 'n8n-workflow';
import { resolve } from 'path';
import { Container, Service } from 'typedi';
import { promisify } from 'util';
@@ -252,7 +252,7 @@ export class Server extends AbstractServer {
this.app.get(
`/${this.restEndpoint}/settings`,
ResponseHelper.send(
- async (req: express.Request): Promise =>
+ async (req: express.Request): Promise =>
frontendService.getSettings(req.headers['push-ref'] as string),
),
);
diff --git a/packages/cli/src/services/frontend.service.ts b/packages/cli/src/services/frontend.service.ts
index 8459642a5b..ee9e0dabf3 100644
--- a/packages/cli/src/services/frontend.service.ts
+++ b/packages/cli/src/services/frontend.service.ts
@@ -1,14 +1,10 @@
+import type { FrontendSettings, ITelemetrySettings } from '@n8n/api-types';
import { GlobalConfig } from '@n8n/config';
import { createWriteStream } from 'fs';
import { mkdir } from 'fs/promises';
import uniq from 'lodash/uniq';
import { InstanceSettings } from 'n8n-core';
-import type {
- ICredentialType,
- IN8nUISettings,
- INodeTypeBaseDescription,
- ITelemetrySettings,
-} from 'n8n-workflow';
+import type { ICredentialType, INodeTypeBaseDescription } from 'n8n-workflow';
import fs from 'node:fs';
import path from 'path';
import { Container, Service } from 'typedi';
@@ -37,7 +33,7 @@ import { UrlService } from './url.service';
@Service()
export class FrontendService {
- settings: IN8nUISettings;
+ settings: FrontendSettings;
private communityPackagesService?: CommunityPackagesService;
@@ -247,7 +243,7 @@ export class FrontendService {
this.writeStaticJSON('credentials', credentials);
}
- getSettings(pushRef?: string): IN8nUISettings {
+ getSettings(pushRef?: string): FrontendSettings {
this.eventService.emit('session-started', { pushRef });
const restEndpoint = this.globalConfig.endpoints.rest;
diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts
index bcbd8a25f6..e8bc05bec9 100644
--- a/packages/editor-ui/src/Interface.ts
+++ b/packages/editor-ui/src/Interface.ts
@@ -1,7 +1,12 @@
import type { Component } from 'vue';
import type { NotificationOptions as ElementNotificationOptions } from 'element-plus';
import type { Connection } from '@jsplumb/core';
-import type { Iso8601DateTimeString } from '@n8n/api-types';
+import type {
+ FrontendSettings,
+ Iso8601DateTimeString,
+ IUserManagementSettings,
+ IVersionNotificationSettings,
+} from '@n8n/api-types';
import type { Scope } from '@n8n/permissions';
import type { IMenuItem, NodeCreatorTag } from 'n8n-design-system';
import type {
@@ -31,10 +36,8 @@ import type {
FeatureFlags,
ExecutionStatus,
ITelemetryTrackProperties,
- IUserManagementSettings,
WorkflowSettings,
IUserSettings,
- IN8nUISettings,
BannerName,
INodeExecutionData,
INodeProperties,
@@ -496,12 +499,6 @@ export interface IUser extends IUserResponse {
mfaEnabled: boolean;
}
-export interface IVersionNotificationSettings {
- enabled: boolean;
- endpoint: string;
- infoUrl: string;
-}
-
export interface IUserListAction {
label: string;
value: string;
@@ -1090,7 +1087,7 @@ export interface INodeCreatorState {
export interface ISettingsState {
initialized: boolean;
- settings: IN8nUISettings;
+ settings: FrontendSettings;
userManagement: IUserManagementSettings;
templatesEndpointHealthy: boolean;
api: {
@@ -1643,7 +1640,7 @@ export type EnterpriseEditionFeatureKey =
| 'WorkerView'
| 'AdvancedPermissions';
-export type EnterpriseEditionFeatureValue = keyof Omit;
+export type EnterpriseEditionFeatureValue = keyof Omit;
export interface IN8nPromptResponse {
updated: boolean;
diff --git a/packages/editor-ui/src/__tests__/defaults.ts b/packages/editor-ui/src/__tests__/defaults.ts
index 3328ac9100..d2134ebf55 100644
--- a/packages/editor-ui/src/__tests__/defaults.ts
+++ b/packages/editor-ui/src/__tests__/defaults.ts
@@ -1,6 +1,6 @@
-import type { IN8nUISettings } from 'n8n-workflow';
+import type { FrontendSettings } from '@n8n/api-types';
-export const defaultSettings: IN8nUISettings = {
+export const defaultSettings: FrontendSettings = {
databaseType: 'sqlite',
isDocker: false,
pruning: {
diff --git a/packages/editor-ui/src/api/settings.ts b/packages/editor-ui/src/api/settings.ts
index ffac562da9..f26703a1a4 100644
--- a/packages/editor-ui/src/api/settings.ts
+++ b/packages/editor-ui/src/api/settings.ts
@@ -1,9 +1,9 @@
import type { IRestApiContext, IN8nPrompts, IN8nPromptResponse } from '../Interface';
import { makeRestApiRequest, get, post } from '@/utils/apiUtils';
import { N8N_IO_BASE_URL, NPM_COMMUNITY_NODE_SEARCH_API_URL } from '@/constants';
-import type { IN8nUISettings } from 'n8n-workflow';
+import type { FrontendSettings } from '@n8n/api-types';
-export async function getSettings(context: IRestApiContext): Promise {
+export async function getSettings(context: IRestApiContext): Promise {
return await makeRestApiRequest(context, 'GET', '/settings');
}
diff --git a/packages/editor-ui/src/components/Telemetry.vue b/packages/editor-ui/src/components/Telemetry.vue
index df3ed63cc1..b799ad6172 100644
--- a/packages/editor-ui/src/components/Telemetry.vue
+++ b/packages/editor-ui/src/components/Telemetry.vue
@@ -1,8 +1,8 @@
',
+ },
+ global: {
+ directives: {
+ n8nHtml,
+ },
+ },
+ });
+ expect(html()).toBe(
+ '',
+ );
+ });
+
+ it('should not touch safe html', async () => {
+ const { html } = render(TestComponent, {
+ props: {
+ html: 'text safe ',
+ },
+ global: {
+ directives: {
+ n8nHtml,
+ },
+ },
+ });
+ expect(html()).toBe(
+ '',
+ );
+ });
+});
diff --git a/packages/design-system/src/directives/n8n-html.ts b/packages/design-system/src/directives/n8n-html.ts
new file mode 100644
index 0000000000..875905d1d9
--- /dev/null
+++ b/packages/design-system/src/directives/n8n-html.ts
@@ -0,0 +1,37 @@
+import sanitize from 'sanitize-html';
+import type { DirectiveBinding, ObjectDirective } from 'vue';
+
+/**
+ * Custom directive `n8nHtml` to replace v-html from Vue to sanitize content.
+ *
+ * Usage:
+ * In your Vue template, use the directive `v-n8n-html` passing the unsafe HTML.
+ *
+ * Example:
+ * link'">
+ *
+ * Compiles to:
link
+ *
+ * Hint: Do not use it on components
+ * https://vuejs.org/guide/reusability/custom-directives#usage-on-components
+ */
+
+const configuredSanitize = (html: string) =>
+ sanitize(html, {
+ allowedTags: sanitize.defaults.allowedTags.concat(['img', 'input']),
+ allowedAttributes: {
+ ...sanitize.defaults.allowedAttributes,
+ input: ['type', 'id', 'checked'],
+ code: ['class'],
+ a: sanitize.defaults.allowedAttributes.a.concat(['data-*']),
+ },
+ });
+
+export const n8nHtml: ObjectDirective = {
+ beforeMount(el: HTMLElement, binding: DirectiveBinding) {
+ el.innerHTML = configuredSanitize(binding.value);
+ },
+ beforeUpdate(el: HTMLElement, binding: DirectiveBinding) {
+ el.innerHTML = configuredSanitize(binding.value);
+ },
+};
diff --git a/packages/editor-ui/src/components/Error/NodeErrorView.vue b/packages/editor-ui/src/components/Error/NodeErrorView.vue
index 0bc3bf5387..aa0eac4c69 100644
--- a/packages/editor-ui/src/components/Error/NodeErrorView.vue
+++ b/packages/editor-ui/src/components/Error/NodeErrorView.vue
@@ -445,7 +445,7 @@ async function onAskAssistantClick() {
v-if="error.description || error.context?.descriptionKey"
data-test-id="node-error-description"
class="node-error-view__header-description"
- v-html="getErrorDescription()"
+ v-n8n-html="getErrorDescription()"
>
diff --git a/packages/editor-ui/src/components/FeatureComingSoon.vue b/packages/editor-ui/src/components/FeatureComingSoon.vue
index a96b3d0ec3..fd24a35196 100644
--- a/packages/editor-ui/src/components/FeatureComingSoon.vue
+++ b/packages/editor-ui/src/components/FeatureComingSoon.vue
@@ -56,7 +56,7 @@ export default defineComponent({
-
+
@@ -68,7 +68,7 @@ export default defineComponent({
@click:button="openLinkPage"
>
-
+
diff --git a/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionTip.vue b/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionTip.vue
index 9e0744025c..73f12a0ec3 100644
--- a/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionTip.vue
+++ b/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionTip.vue
@@ -115,15 +115,15 @@ watchDebounced(
-
+
-
+
-
+
diff --git a/packages/editor-ui/src/components/InputPanel.vue b/packages/editor-ui/src/components/InputPanel.vue
index 7bc38c811e..a9a6dd3909 100644
--- a/packages/editor-ui/src/components/InputPanel.vue
+++ b/packages/editor-ui/src/components/InputPanel.vue
@@ -406,7 +406,7 @@ export default defineComponent({
(), {
diff --git a/packages/editor-ui/src/components/Node.vue b/packages/editor-ui/src/components/Node.vue
index 83efe3b0a7..56ca2cecf8 100644
--- a/packages/editor-ui/src/components/Node.vue
+++ b/packages/editor-ui/src/components/Node.vue
@@ -633,7 +633,7 @@ function openContextMenu(event: MouseEvent, source: 'node-button' | 'node-right-
-
+
diff --git a/packages/editor-ui/src/components/Node/NodeCreator/ItemTypes/NodeItem.vue b/packages/editor-ui/src/components/Node/NodeCreator/ItemTypes/NodeItem.vue
index ebea3cba41..4991638176 100644
--- a/packages/editor-ui/src/components/Node/NodeCreator/ItemTypes/NodeItem.vue
+++ b/packages/editor-ui/src/components/Node/NodeCreator/ItemTypes/NodeItem.vue
@@ -163,7 +163,7 @@ function onCommunityNodeTooltipClick(event: MouseEvent) {
{
data-test-id="actions-panel-no-triggers-callout"
>
{
@@ -293,13 +293,13 @@ onMounted(() => {
slim
data-test-id="actions-panel-activation-callout"
>
-
+
{
:class="$style.resetSearch"
data-test-id="actions-panel-no-matching-actions"
@click="resetSearch"
- v-html="i18n.baseText('nodeCreator.actionsCategory.noMatchingActions')"
+ v-n8n-html="i18n.baseText('nodeCreator.actionsCategory.noMatchingActions')"
/>
@@ -320,7 +320,7 @@ onMounted(() => {
-
+
diff --git a/packages/editor-ui/src/components/ParameterInput.vue b/packages/editor-ui/src/components/ParameterInput.vue
index b5ca14dd88..df63a8b322 100644
--- a/packages/editor-ui/src/components/ParameterInput.vue
+++ b/packages/editor-ui/src/components/ParameterInput.vue
@@ -1391,7 +1391,7 @@ onUpdated(async () => {
@@ -1424,7 +1424,7 @@ onUpdated(async () => {
diff --git a/packages/editor-ui/src/components/ParameterInputHint.vue b/packages/editor-ui/src/components/ParameterInputHint.vue
index 87ce1a92ab..1c28e4e44a 100644
--- a/packages/editor-ui/src/components/ParameterInputHint.vue
+++ b/packages/editor-ui/src/components/ParameterInputHint.vue
@@ -46,13 +46,13 @@ const simplyText = computed(() => {
[$style.highlight]: highlight,
}"
>
-
+
diff --git a/packages/editor-ui/src/components/PushConnectionTracker.vue b/packages/editor-ui/src/components/PushConnectionTracker.vue
index bd4ecc8654..1be8fb5b6a 100644
--- a/packages/editor-ui/src/components/PushConnectionTracker.vue
+++ b/packages/editor-ui/src/components/PushConnectionTracker.vue
@@ -16,7 +16,7 @@ export default defineComponent({
-
+
diff --git a/packages/editor-ui/src/components/ResourceMapper/MappingModeSelect.vue b/packages/editor-ui/src/components/ResourceMapper/MappingModeSelect.vue
index 2c69e15550..ab250a07d3 100644
--- a/packages/editor-ui/src/components/ResourceMapper/MappingModeSelect.vue
+++ b/packages/editor-ui/src/components/ResourceMapper/MappingModeSelect.vue
@@ -131,7 +131,7 @@ defineExpose({
{{ option.name }}
-
+
diff --git a/packages/editor-ui/src/components/RunData.vue b/packages/editor-ui/src/components/RunData.vue
index f85800fa16..348e3a455b 100644
--- a/packages/editor-ui/src/components/RunData.vue
+++ b/packages/editor-ui/src/components/RunData.vue
@@ -1350,7 +1350,7 @@ export default defineComponent({
:class="$style.hintCallout"
:theme="hint.type || 'info'"
>
-
+
{{ tooMuchDataTitle }}
{
data-test-id="node-run-info-stale"
>
diff --git a/packages/editor-ui/src/components/TriggerPanel.vue b/packages/editor-ui/src/components/TriggerPanel.vue
index db8a64736a..0702f34c49 100644
--- a/packages/editor-ui/src/components/TriggerPanel.vue
+++ b/packages/editor-ui/src/components/TriggerPanel.vue
@@ -441,7 +441,7 @@ export default defineComponent({
-
+
{
{{ `${$locale.baseText('versionCard.version')} ${version.name}` }}
-
+
{
diff --git a/packages/editor-ui/src/components/WorkflowSettings.vue b/packages/editor-ui/src/components/WorkflowSettings.vue
index 2d915611ff..1ecac538a6 100644
--- a/packages/editor-ui/src/components/WorkflowSettings.vue
+++ b/packages/editor-ui/src/components/WorkflowSettings.vue
@@ -573,7 +573,7 @@ export default defineComponent({
{{ $locale.baseText('workflowSettings.errorWorkflow') + ':' }}
-
+
diff --git a/packages/editor-ui/src/components/banners/V1Banner.vue b/packages/editor-ui/src/components/banners/V1Banner.vue
index 72077f1a3c..6ea90c712c 100644
--- a/packages/editor-ui/src/components/banners/V1Banner.vue
+++ b/packages/editor-ui/src/components/banners/V1Banner.vue
@@ -17,14 +17,14 @@ const hasOwnerPermission = computed(() => hasPermission(['instanceOwner']));
-
+
-
+
diff --git a/packages/editor-ui/src/components/banners/__tests__/V1Banner.spec.ts b/packages/editor-ui/src/components/banners/__tests__/V1Banner.spec.ts
index d9cbedd9e8..f01ea4c3f6 100644
--- a/packages/editor-ui/src/components/banners/__tests__/V1Banner.spec.ts
+++ b/packages/editor-ui/src/components/banners/__tests__/V1Banner.spec.ts
@@ -1,10 +1,12 @@
-import { render } from '@testing-library/vue';
+import { createComponentRenderer } from '@/__tests__/render';
import V1Banner from '../V1Banner.vue';
import { createPinia, setActivePinia } from 'pinia';
import { useUsersStore } from '@/stores/users.store';
import { ROLE } from '@/constants';
import type { IUser } from '@/Interface';
+const renderComponent = createComponentRenderer(V1Banner);
+
describe('V1 Banner', () => {
let pinia: ReturnType;
let usersStore: ReturnType;
@@ -17,7 +19,7 @@ describe('V1 Banner', () => {
});
it('should render banner', () => {
- const { container } = render(V1Banner);
+ const { container } = renderComponent();
expect(container).toMatchSnapshot();
expect(container.querySelectorAll('a')).toHaveLength(1);
});
@@ -26,7 +28,7 @@ describe('V1 Banner', () => {
usersStore.usersById = { '1': { role: ROLE.Owner } as IUser };
usersStore.currentUserId = '1';
- const { container } = render(V1Banner);
+ const { container } = renderComponent();
expect(container).toMatchSnapshot();
expect(container.querySelectorAll('a')).toHaveLength(2);
});
diff --git a/packages/editor-ui/src/components/banners/__tests__/__snapshots__/V1Banner.spec.ts.snap b/packages/editor-ui/src/components/banners/__tests__/__snapshots__/V1Banner.spec.ts.snap
index afc0523e0c..41daf10517 100644
--- a/packages/editor-ui/src/components/banners/__tests__/__snapshots__/V1Banner.spec.ts.snap
+++ b/packages/editor-ui/src/components/banners/__tests__/__snapshots__/V1Banner.spec.ts.snap
@@ -2,69 +2,209 @@
exports[`V1 Banner > should render banner 1`] = `
-
-
-
- n8n has been updated to version 1, introducing some breaking changes. Please consult the
-
+
- migration guide
-
- for more information.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ n8n has been updated to version 1, introducing some breaking changes. Please consult the
+
+ migration guide
+
+ for more information.
+
+
+
+
+
+
-
+ Â
+
-
+
+
+
+
`;
exports[`V1 Banner > should render banner with dismiss call if user is owner 1`] = `
-
+
+
+
+
`;
diff --git a/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeDefault.vue b/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeDefault.vue
index 85d8dedad7..bc443c1732 100644
--- a/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeDefault.vue
+++ b/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeDefault.vue
@@ -96,7 +96,7 @@ function openContextMenu(event: MouseEvent) {
-
+
diff --git a/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionAnnotationPanel.vue b/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionAnnotationPanel.vue
index 1ae4ef0e5c..437cfd8e12 100644
--- a/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionAnnotationPanel.vue
+++ b/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionAnnotationPanel.vue
@@ -179,7 +179,7 @@ const onTagsEditEsc = () => {
-
+
diff --git a/packages/editor-ui/src/views/SettingsLdapView.vue b/packages/editor-ui/src/views/SettingsLdapView.vue
index 8feca49da7..6cab6b1ba5 100644
--- a/packages/editor-ui/src/views/SettingsLdapView.vue
+++ b/packages/editor-ui/src/views/SettingsLdapView.vue
@@ -633,7 +633,7 @@ export default defineComponent({
-
+
diff --git a/packages/editor-ui/src/views/SettingsLogStreamingView.vue b/packages/editor-ui/src/views/SettingsLogStreamingView.vue
index e8abb67e64..0f62a31aee 100644
--- a/packages/editor-ui/src/views/SettingsLogStreamingView.vue
+++ b/packages/editor-ui/src/views/SettingsLogStreamingView.vue
@@ -175,7 +175,7 @@ export default defineComponent({
-
+
@@ -207,7 +207,7 @@ export default defineComponent({
@click:button="addDestination"
>
-
+
@@ -215,7 +215,7 @@ export default defineComponent({
-
+
@@ -225,7 +225,7 @@ export default defineComponent({
@click:button="goToUpgrade"
>
-
+
diff --git a/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/AppsRequiringCredsNotice.vue b/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/AppsRequiringCredsNotice.vue
index a640b49f9d..ac5bfbe63a 100644
--- a/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/AppsRequiringCredsNotice.vue
+++ b/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/AppsRequiringCredsNotice.vue
@@ -28,7 +28,7 @@ const appNodeCounts = computed(() => {
-
+
diff --git a/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/SetupTemplateFormStep.vue b/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/SetupTemplateFormStep.vue
index 2e64dece86..c482d31114 100644
--- a/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/SetupTemplateFormStep.vue
+++ b/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/SetupTemplateFormStep.vue
@@ -95,7 +95,7 @@ const onCredentialModalOpened = () => {
:plural="credentials.usedBy.length"
scope="global"
>
-
+
diff --git a/packages/editor-ui/src/views/TemplatesSearchView.vue b/packages/editor-ui/src/views/TemplatesSearchView.vue
index 61ce8b42e0..af0381d820 100644
--- a/packages/editor-ui/src/views/TemplatesSearchView.vue
+++ b/packages/editor-ui/src/views/TemplatesSearchView.vue
@@ -410,7 +410,7 @@ export default defineComponent({
/>
-
+
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0b9e8ca051..d94ec4a582 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1205,9 +1205,6 @@ importers:
'@vitest/coverage-v8':
specifier: catalog:frontend
version: 1.6.0(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1))
- '@vue/test-utils':
- specifier: ^2.4.3
- version: 2.4.3(@vue/server-renderer@3.4.21(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))
autoprefixer:
specifier: ^10.4.19
version: 10.4.19(postcss@8.4.38)
From c1d88f3bb3c24b22051276b75ef9182c887d52c8 Mon Sep 17 00:00:00 2001
From: Tomi Turtiainen <10324676+tomi@users.noreply.github.com>
Date: Wed, 18 Sep 2024 09:58:09 +0300
Subject: [PATCH 35/42] ci(benchmark): Fix bootstrap script (#10863)
---
packages/@n8n/benchmark/scripts/bootstrap.sh | 1 -
packages/@n8n/benchmark/scripts/vm-benchmark.sh | 4 ++--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/packages/@n8n/benchmark/scripts/bootstrap.sh b/packages/@n8n/benchmark/scripts/bootstrap.sh
index c5a8b57db5..02858094c8 100644
--- a/packages/@n8n/benchmark/scripts/bootstrap.sh
+++ b/packages/@n8n/benchmark/scripts/bootstrap.sh
@@ -43,7 +43,6 @@ sudo systemctl disable getty@tty1.service
sudo systemctl disable serial-getty@ttyS0.service
# Snap
sudo systemctl disable snapd.service
-sudo apt remove snapd
# Unattended upgrades
sudo systemctl disable unattended-upgrades.service
# Cron
diff --git a/packages/@n8n/benchmark/scripts/vm-benchmark.sh b/packages/@n8n/benchmark/scripts/vm-benchmark.sh
index 6bf215cd67..13b7eb2b1a 100644
--- a/packages/@n8n/benchmark/scripts/vm-benchmark.sh
+++ b/packages/@n8n/benchmark/scripts/vm-benchmark.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# Install fio
-sudo apt-get -y install fio > /dev/null
+DEBIAN_FRONTEND=noninteractive sudo apt-get -y install fio > /dev/null
# Run the disk benchmark
fio --name=rand_rw --ioengine=libaio --rw=randrw --rwmixread=70 --bs=4k --numjobs=4 --size=1G --runtime=30 --directory=/n8n --group_reporting
@@ -10,4 +10,4 @@ fio --name=rand_rw --ioengine=libaio --rw=randrw --rwmixread=70 --bs=4k --numjob
sudo rm /n8n/rand_rw.*
# Uninstall fio
-sudo apt-get -y remove fio
+DEBIAN_FRONTEND=noninteractive sudo apt-get -y remove fio > /dev/null
From 5a1db6db1adad43887e839181719818474bc66b0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ra=C3=BAl=20G=C3=B3mez=20Morales?=
Date: Wed, 18 Sep 2024 09:10:51 +0200
Subject: [PATCH 36/42] fix(editor): Restore V1 keybinding, Space Key to toggle
panning (#10841)
---
packages/editor-ui/src/components/canvas/Canvas.spec.ts | 8 ++++----
packages/editor-ui/src/components/canvas/Canvas.vue | 5 ++++-
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/packages/editor-ui/src/components/canvas/Canvas.spec.ts b/packages/editor-ui/src/components/canvas/Canvas.spec.ts
index 256377a8f3..8768e97798 100644
--- a/packages/editor-ui/src/components/canvas/Canvas.spec.ts
+++ b/packages/editor-ui/src/components/canvas/Canvas.spec.ts
@@ -164,7 +164,7 @@ describe('Canvas', () => {
const pane = canvas.querySelector('.vue-flow__pane');
if (!pane) throw new Error('VueFlow pane not found');
- await fireEvent.keyDown(pane, { view: window, key: 'Shift' });
+ await fireEvent.keyDown(pane, { view: window, key: ' ' });
await fireEvent.mouseDown(pane, { view: window });
await fireEvent.mouseMove(pane, {
view: window,
@@ -172,7 +172,7 @@ describe('Canvas', () => {
clientY: 100,
});
await fireEvent.mouseUp(pane, { view: window });
- await fireEvent.keyUp(pane, { view: window, key: 'Shift' });
+ await fireEvent.keyUp(pane, { view: window, key: ' ' });
vi.advanceTimersByTime(minimapTransitionDuration);
await waitFor(() => expect(getByTestId('canvas-minimap')).toBeVisible());
@@ -196,7 +196,7 @@ describe('Canvas', () => {
const pane = canvas.querySelector('.vue-flow__pane');
if (!pane) throw new Error('VueFlow pane not found');
- await fireEvent.keyDown(pane, { view: window, key: 'Shift' });
+ await fireEvent.keyDown(pane, { view: window, key: ' ' });
await fireEvent.mouseDown(pane, { view: window });
await fireEvent.mouseMove(pane, {
view: window,
@@ -204,7 +204,7 @@ describe('Canvas', () => {
clientY: 100,
});
await fireEvent.mouseUp(pane, { view: window });
- await fireEvent.keyUp(pane, { view: window, key: 'Shift' });
+ await fireEvent.keyUp(pane, { view: window, key: ' ' });
vi.advanceTimersByTime(minimapTransitionDuration);
await waitFor(() => expect(getByTestId('canvas-minimap')).toBeVisible());
diff --git a/packages/editor-ui/src/components/canvas/Canvas.vue b/packages/editor-ui/src/components/canvas/Canvas.vue
index 8efb8fcea5..a552c5bd23 100644
--- a/packages/editor-ui/src/components/canvas/Canvas.vue
+++ b/packages/editor-ui/src/components/canvas/Canvas.vue
@@ -122,7 +122,10 @@ const classes = computed(() => ({
const disableKeyBindings = computed(() => !props.keyBindings);
-const panningKeyCode = 'Shift';
+/**
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values#whitespace_keys
+ */
+const panningKeyCode = ' ';
const isPanningEnabled = ref(false);
onKeyDown(panningKeyCode, () => {
From 4f0a1a953f9cbd9ff069b906cc8b15ec83a5a543 Mon Sep 17 00:00:00 2001
From: Tomi Turtiainen <10324676+tomi@users.noreply.github.com>
Date: Wed, 18 Sep 2024 10:19:33 +0300
Subject: [PATCH 37/42] refactor: Enable import/order for design-system,
workflow and @n8n packages (#10847)
---
cypress/composables/workflow.ts | 2 +-
cypress/e2e/1-workflows.cy.ts | 2 +-
cypress/e2e/10-settings-log-streaming.cy.ts | 2 +-
cypress/e2e/10-undo-redo.cy.ts | 2 +-
cypress/e2e/12-canvas-actions.cy.ts | 4 +-
cypress/e2e/12-canvas.cy.ts | 4 +-
cypress/e2e/14-mapping.cy.ts | 4 +-
cypress/e2e/16-webhook-node.cy.ts | 3 +-
cypress/e2e/17-sharing.cy.ts | 2 +-
cypress/e2e/18-user-management.cy.ts | 2 +-
cypress/e2e/19-execution.cy.ts | 2 +-
cypress/e2e/2-credentials.cy.ts | 1 +
cypress/e2e/20-workflow-executions.cy.ts | 3 +-
cypress/e2e/21-community-nodes.cy.ts | 13 ++--
.../e2e/233-AI-switch-to-logs-on-error.cy.ts | 29 +++++----
cypress/e2e/27-cloud.cy.ts | 2 +-
.../e2e/27-two-factor-authentication.cy.ts | 5 +-
cypress/e2e/29-templates.cy.ts | 6 +-
cypress/e2e/30-langchain.cy.ts | 64 +++++++++----------
cypress/e2e/31-demo.cy.ts | 2 +-
.../e2e/34-template-credentials-setup.cy.ts | 10 +--
cypress/e2e/36-versions.cy.ts | 2 +-
cypress/e2e/39-projects.cy.ts | 2 +-
cypress/e2e/4-node-creator.cy.ts | 8 +--
cypress/e2e/44-routing.cy.ts | 6 +-
cypress/e2e/5-ndv.cy.ts | 4 +-
cypress/e2e/6-code-node.cy.ts | 3 +-
cypress/e2e/7-workflow-actions.cy.ts | 4 +-
cypress/pages/mfa-login.ts | 2 +-
cypress/pages/modals/credentials-modal.ts | 2 +-
cypress/pages/ndv.ts | 2 +-
cypress/pages/settings-log-streaming.ts | 2 +-
cypress/pages/settings-personal.ts | 3 +-
cypress/pages/settings-users.ts | 4 +-
cypress/pages/signin.ts | 2 +-
cypress/pages/template-credential-setup.ts | 4 +-
cypress/pages/variables.ts | 1 +
cypress/pages/workflow.ts | 4 +-
cypress/support/commands.ts | 5 +-
cypress/support/e2e.ts | 1 +
cypress/utils/executions.ts | 3 +-
packages/@n8n/api-types/src/push/index.ts | 10 +--
packages/@n8n/benchmark/src/commands/list.ts | 3 +-
packages/@n8n/benchmark/src/commands/run.ts | 7 +-
.../authenticated-n8n-api-client.ts | 1 +
.../n8n-api-client/workflows-api-client.ts | 3 +-
.../src/scenario/scenario-data-loader.ts | 3 +-
.../benchmark/src/scenario/scenario-loader.ts | 3 +-
.../src/test-execution/k6-executor.ts | 5 +-
.../src/test-execution/scenario-runner.ts | 7 +-
.../src/test-execution/test-report.ts | 1 +
packages/@n8n/chat/src/App.vue | 5 +-
.../@n8n/chat/src/__stories__/App.stories.ts | 3 +-
.../@n8n/chat/src/__tests__/index.spec.ts | 1 +
.../chat/src/__tests__/utils/selectors.ts | 1 +
packages/@n8n/chat/src/components/Chat.vue | 5 +-
.../@n8n/chat/src/components/ChatFile.vue | 9 ++-
.../@n8n/chat/src/components/ChatWindow.vue | 1 +
.../chat/src/components/GetStartedFooter.vue | 2 +-
packages/@n8n/chat/src/components/Input.vue | 10 +--
packages/@n8n/chat/src/components/Layout.vue | 1 +
packages/@n8n/chat/src/components/Message.vue | 22 ++++---
.../chat/src/components/MessageTyping.vue | 4 +-
.../@n8n/chat/src/components/MessagesList.vue | 3 +-
packages/@n8n/chat/src/composables/useChat.ts | 1 +
packages/@n8n/chat/src/composables/useI18n.ts | 1 +
.../@n8n/chat/src/composables/useOptions.ts | 1 +
packages/@n8n/chat/src/constants/symbols.ts | 1 +
packages/@n8n/chat/src/index.ts | 8 ++-
packages/@n8n/chat/src/plugins/chat.ts | 7 +-
packages/@n8n/chat/src/types/chat.ts | 1 +
.../@n8n/client-oauth2/src/ClientOAuth2.ts | 7 +-
.../client-oauth2/src/ClientOAuth2Token.ts | 2 +-
packages/@n8n/client-oauth2/src/CodeFlow.ts | 1 +
.../@n8n/client-oauth2/test/CodeFlow.test.ts | 6 +-
.../test/CredentialsFlow.test.ts | 5 +-
.../codemirror-lang/src/expressions/index.ts | 1 +
.../test/expressions/expressions.test.ts | 3 +-
packages/@n8n/config/src/index.ts | 20 +++---
packages/@n8n/config/test/config.test.ts | 3 +-
packages/@n8n/config/test/decorators.test.ts | 1 +
packages/@n8n/imap/src/ImapSimple.ts | 2 +-
packages/@n8n/imap/src/PartData.ts | 2 +-
packages/@n8n/imap/src/helpers/getMessage.ts | 1 +
packages/@n8n/imap/src/index.ts | 3 +-
packages/@n8n_io/eslint-config/base.js | 12 +++-
packages/cli/.eslintrc.js | 11 ----
packages/design-system/.eslintrc.js | 1 -
packages/design-system/src/__tests__/setup.ts | 1 +
.../AssistantAvatar.stories.ts | 3 +-
.../__tests__/AskAssistantAvatar.spec.ts | 1 +
.../AskAssistantButton.stories.ts | 3 +-
.../AskAssistantButton/AskAssistantButton.vue | 3 +-
.../__tests__/AskAssistantButton.spec.ts | 1 +
.../AskAssistantChat.stories.ts | 3 +-
.../AskAssistantChat/AskAssistantChat.vue | 24 +++----
.../__tests__/AskAssistantChat.spec.ts | 1 +
.../AskAssistantIcon/AssistantIcon.stories.ts | 3 +-
.../__tests__/AssistantIcon.spec.ts | 1 +
.../AssistantLoadingMessage.stories.ts | 3 +-
.../AssistantLoadingMessage.vue | 1 +
.../DemoComponent.stories.ts | 3 +-
.../DemoComponent.vue | 1 +
.../AskAssistantLoadingMessage.spec.ts | 1 +
.../AskAssistantText/AssistantText.stories.ts | 3 +-
.../__tests__/AssistantText.spec.ts | 1 +
.../src/components/BetaTag/BetaTag.stories.ts | 3 +-
.../BetaTag/__tests__/BetaTag.spec.ts | 1 +
.../BlinkingCursor/BlinkingCursor.stories.ts | 3 +-
.../__tests__/BlinkingCursor.spec.ts | 1 +
.../components/CodeDiff/CodeDiff.stories.ts | 3 +-
.../src/components/CodeDiff/CodeDiff.vue | 1 +
.../CodeDiff/__tests__/CodeDiff.spec.ts | 1 +
.../__tests__/ConditionalRouterLink.spec.ts | 1 +
.../InlineAskAssistantButton.stories.ts | 3 +-
.../InlineAskAssistantButton.vue | 3 +-
.../N8nActionBox/ActionBox.stories.ts | 3 +-
.../src/components/N8nActionBox/ActionBox.vue | 7 +-
.../N8nActionBox/__tests__/ActionBox.spec.ts | 1 +
.../ActionDropdown.stories.ts | 3 +-
.../N8nActionDropdown/ActionDropdown.vue | 8 ++-
.../__tests__/ActionDropdown.spec.ts | 1 +
.../N8nActionToggle/ActionToggle.stories.ts | 3 +-
.../N8nActionToggle/ActionToggle.vue | 4 +-
.../src/components/N8nAlert/Alert.stories.ts | 1 +
.../src/components/N8nAlert/Alert.vue | 1 +
.../N8nAlert/__tests__/Alert.spec.ts | 3 +-
.../components/N8nAvatar/Avatar.stories.ts | 1 +
.../src/components/N8nAvatar/Avatar.vue | 1 +
.../N8nAvatar/__tests__/Avatar.test.ts | 1 +
.../src/components/N8nBadge/Badge.stories.ts | 1 +
.../src/components/N8nBadge/Badge.vue | 1 +
.../N8nBadge/__tests__/Badge.spec.ts | 1 +
.../components/N8nBlockUi/BlockUi.stories.ts | 1 +
.../N8nBlockUi/__tests__/BlockUi.spec.ts | 1 +
.../components/N8nButton/Button.stories.ts | 3 +-
.../src/components/N8nButton/Button.vue | 4 +-
.../N8nButton/__tests__/Button.spec.ts | 1 +
.../components/N8nCallout/Callout.stories.ts | 3 +-
.../src/components/N8nCallout/Callout.vue | 6 +-
.../N8nCallout/__tests__/Callout.spec.ts | 1 +
.../src/components/N8nCard/Card.stories.ts | 3 +-
.../components/N8nCard/__tests__/Card.spec.ts | 1 +
.../N8nCheckbox/Checkbox.stories.ts | 5 +-
.../src/components/N8nCheckbox/Checkbox.vue | 3 +-
.../N8nCheckbox/__tests__/Checkbox.spec.ts | 1 +
.../N8nCircleLoader/CircleLoader.stories.ts | 3 +-
.../__tests__/CircleLoader.spec.ts | 1 +
.../N8nColorPicker/ColorPicker.stories.ts | 1 +
.../components/N8nColorPicker/ColorPicker.vue | 5 +-
.../__tests__/ColorPicker.spec.ts | 1 +
.../N8nDatatable/Datatable.stories.ts | 3 +-
.../src/components/N8nDatatable/Datatable.vue | 9 +--
.../N8nDatatable/__tests__/Datatable.spec.ts | 6 +-
.../components/N8nDatatable/__tests__/data.ts | 1 +
.../components/N8nFormBox/FormBox.stories.ts | 3 +-
.../src/components/N8nFormBox/FormBox.vue | 7 +-
.../N8nFormInput/FormInput.stories.ts | 3 +-
.../src/components/N8nFormInput/FormInput.vue | 16 ++---
.../N8nFormInputs/FormInputs.stories.ts | 3 +-
.../components/N8nFormInputs/FormInputs.vue | 5 +-
.../components/N8nHeading/Heading.stories.ts | 1 +
.../src/components/N8nIcon/Icon.stories.ts | 1 +
.../src/components/N8nIcon/Icon.vue | 2 +
.../N8nIconButton/IconButton.stories.ts | 3 +-
.../components/N8nIconButton/IconButton.vue | 1 +
.../N8nInfoAccordion/InfoAccordion.stories.ts | 5 +-
.../N8nInfoAccordion/InfoAccordion.vue | 6 +-
.../components/N8nInfoTip/InfoTip.stories.ts | 1 +
.../src/components/N8nInfoTip/InfoTip.vue | 3 +-
.../N8nInfoTip/__tests__/InfoTip.spec.ts | 1 +
.../src/components/N8nInput/Input.stories.ts | 5 +-
.../src/components/N8nInput/Input.vue | 8 ++-
.../N8nInput/__tests__/Input.spec.ts | 1 +
.../N8nInputLabel/InputLabel.stories.ts | 3 +-
.../components/N8nInputLabel/InputLabel.vue | 7 +-
.../N8nInputNumber/InputNumber.stories.ts | 3 +-
.../components/N8nInputNumber/InputNumber.vue | 3 +-
.../KeyboardShortcut.stories.ts | 3 +-
.../N8nKeyboardShortcut.vue | 1 +
.../src/components/N8nLink/Link.stories.ts | 3 +-
.../src/components/N8nLink/Link.vue | 6 +-
.../components/N8nLoading/Loading.stories.ts | 1 +
.../N8nMarkdown/Markdown.stories.ts | 1 +
.../src/components/N8nMarkdown/Markdown.vue | 6 +-
.../N8nMarkdown/__tests__/Markdown.spec.ts | 1 +
.../src/components/N8nMenu/Menu.stories.ts | 5 +-
.../src/components/N8nMenu/Menu.vue | 5 +-
.../N8nMenuItem/MenuItem.stories.ts | 5 +-
.../src/components/N8nMenuItem/MenuItem.vue | 11 ++--
.../src/components/N8nMenuItem/routerUtil.ts | 3 +-
.../NodeCreatorNode.stories.ts | 3 +-
.../N8nNodeCreatorNode/NodeCreatorNode.vue | 5 +-
.../N8nNodeIcon/NodeIcon.stories.ts | 3 +-
.../src/components/N8nNodeIcon/NodeIcon.vue | 3 +-
.../components/N8nNotice/Notice.stories.ts | 3 +-
.../src/components/N8nNotice/Notice.vue | 3 +-
.../N8nNotice/__tests__/Notice.spec.ts | 4 +-
.../N8nPagination/Pagination.stories.ts | 1 +
.../components/N8nPopover/Popover.stories.ts | 1 +
.../src/components/N8nPulse/Pulse.stories.ts | 3 +-
.../N8nRadioButtons/RadioButtons.stories.ts | 4 +-
.../RecycleScroller.stories.ts | 3 +-
.../__tests__/RecycleScroller.spec.ts | 1 +
.../N8nResizeWrapper/ResizeWrapper.stories.ts | 5 +-
.../ResizeableSticky.stories.ts | 1 +
.../N8nResizeableSticky/ResizeableSticky.vue | 3 +-
.../N8nRoute/__tests__/Route.spec.ts | 1 +
.../components/N8nSelect/Select.stories.ts | 9 +--
.../src/components/N8nSelect/Select.vue | 4 +-
.../N8nSelect/__tests__/Select.spec.ts | 10 +--
.../components/N8nSpinner/Spinner.stories.ts | 1 +
.../src/components/N8nSpinner/Spinner.vue | 1 +
.../components/N8nSticky/Sticky.stories.ts | 1 +
.../src/components/N8nSticky/Sticky.vue | 7 +-
.../src/components/N8nTabs/Tabs.stories.ts | 4 +-
.../src/components/N8nTabs/Tabs.vue | 3 +-
.../src/components/N8nTag/Tag.stories.ts | 1 +
.../src/components/N8nTags/Tags.stories.ts | 1 +
.../src/components/N8nTags/Tags.vue | 5 +-
.../src/components/N8nText/Text.stories.ts | 1 +
.../src/components/N8nText/Text.vue | 1 +
.../components/N8nTooltip/Tooltip.stories.ts | 1 +
.../src/components/N8nTooltip/Tooltip.vue | 4 +-
.../N8nTooltip/__tests__/Tooltip.spec.ts | 3 +-
.../src/components/N8nTree/Tree.stories.ts | 3 +-
.../components/N8nTree/__tests__/Tree.spec.ts | 1 +
.../N8nUserInfo/UserInfo.stories.ts | 1 +
.../src/components/N8nUserInfo/UserInfo.vue | 5 +-
.../N8nUserSelect/UserSelect.stories.ts | 3 +-
.../components/N8nUserSelect/UserSelect.vue | 7 +-
.../N8nUserStack/UserStack.stories.ts | 1 +
.../src/components/N8nUserStack/UserStack.vue | 2 +
.../N8nUserStack/__tests__/UserStack.spec.ts | 4 +-
.../N8nUsersList/UsersList.stories.ts | 4 +-
.../src/components/N8nUsersList/UsersList.vue | 5 +-
.../src/directives/n8n-truncate.test.ts | 1 +
.../src/directives/n8n-truncate.ts | 1 +
packages/design-system/src/locale/index.ts | 5 +-
packages/design-system/src/plugin.ts | 1 +
.../src/styleguide/ColorCircles.vue | 1 +
.../styleguide/__tests__/ColorCircles.spec.ts | 1 +
.../src/styleguide/__tests__/Sizes.spec.ts | 1 +
.../src/styleguide/border.stories.ts | 1 +
.../src/styleguide/colors.stories.ts | 1 +
.../styleguide/colorsprimitives.stories.ts | 1 +
.../src/styleguide/fonts.stories.ts | 1 +
.../src/styleguide/spacing.stories.ts | 1 +
.../styleguide/utilities/spacing.stories.ts | 1 +
packages/workflow/.eslintrc.js | 1 -
packages/workflow/src/ErrorReporterProxy.ts | 2 +-
packages/workflow/src/Expression.ts | 23 ++++---
.../workflow/src/ExpressionEvaluatorProxy.ts | 5 +-
packages/workflow/src/ExpressionSandboxing.ts | 1 +
.../src/Extensions/ArrayExtensions.ts | 4 +-
.../workflow/src/Extensions/DateExtensions.ts | 6 +-
.../src/Extensions/ExpressionExtension.ts | 23 ++++---
.../src/Extensions/ExtendedFunctions.ts | 4 +-
.../src/Extensions/NumberExtensions.ts | 3 +-
.../src/Extensions/ObjectExtensions.ts | 2 +-
.../src/Extensions/StringExtensions.ts | 15 +++--
packages/workflow/src/Extensions/utils.ts | 1 +
packages/workflow/src/Interfaces.ts | 20 +++---
packages/workflow/src/MessageEventBus.ts | 1 +
packages/workflow/src/NativeMethods/index.ts | 4 +-
packages/workflow/src/NodeHelpers.ts | 11 ++--
.../src/NodeParameters/FilterParameter.ts | 5 +-
packages/workflow/src/RoutingNode.ts | 9 +--
packages/workflow/src/TelemetryHelpers.ts | 26 ++++----
packages/workflow/src/TypeValidation.ts | 5 +-
packages/workflow/src/Workflow.ts | 19 +++---
packages/workflow/src/WorkflowDataProxy.ts | 14 ++--
.../src/errors/abstract/node.error.ts | 4 +-
.../workflow/src/errors/application.error.ts | 2 +-
.../src/errors/credential-access-error.ts | 2 +-
.../workflow/src/errors/expression.error.ts | 2 +-
.../workflow/src/errors/node-api.error.ts | 19 +++---
.../src/errors/node-operation.error.ts | 4 +-
.../src/errors/trigger-close.error.ts | 2 +-
.../src/errors/workflow-activation.error.ts | 2 +-
.../src/errors/workflow-operation.error.ts | 2 +-
packages/workflow/src/utils.ts | 13 ++--
packages/workflow/test/AugmentObject.test.ts | 2 +-
packages/workflow/test/Expression.test.ts | 14 ++--
.../ArrayExtensions.test.ts | 2 +-
.../BooleanExtensions.test.ts | 2 +-
.../DateExtensions.test.ts | 2 +
.../ExpressionExtension.test.ts | 1 +
.../test/ExpressionExtensions/Helpers.ts | 1 +
.../NumberExtensions.test.ts | 1 +
.../ObjectExtensions.test.ts | 2 +-
.../StringExtensions.test.ts | 1 +
.../workflow/test/ExpressionFixtures/base.ts | 2 +-
.../test/ExpressionSandboxing.test.ts | 3 +-
.../workflow/test/FilterParameter.test.ts | 5 +-
packages/workflow/test/Helpers.ts | 3 +-
packages/workflow/test/NodeErrors.test.ts | 3 +-
packages/workflow/test/NodeHelpers.test.ts | 2 +-
packages/workflow/test/NodeTypes.ts | 1 +
packages/workflow/test/RoutingNode.test.ts | 8 +--
.../workflow/test/TelemetryHelpers.test.ts | 13 ++--
packages/workflow/test/TypeValidation.test.ts | 1 +
packages/workflow/test/Workflow.test.ts | 3 +-
.../workflow/test/WorkflowDataProxy.test.ts | 3 +-
.../workflow/test/errors/node.error.test.ts | 3 +-
305 files changed, 746 insertions(+), 507 deletions(-)
diff --git a/cypress/composables/workflow.ts b/cypress/composables/workflow.ts
index b3d6f20c28..8d37d5f2ad 100644
--- a/cypress/composables/workflow.ts
+++ b/cypress/composables/workflow.ts
@@ -1,5 +1,5 @@
-import { ROUTES } from '../constants';
import { getManualChatModal } from './modals/chat-modal';
+import { ROUTES } from '../constants';
/**
* Types
diff --git a/cypress/e2e/1-workflows.cy.ts b/cypress/e2e/1-workflows.cy.ts
index d01f046d75..6835346012 100644
--- a/cypress/e2e/1-workflows.cy.ts
+++ b/cypress/e2e/1-workflows.cy.ts
@@ -1,5 +1,5 @@
-import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows';
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
+import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows';
import { getUniqueWorkflowName } from '../utils/workflowUtils';
const WorkflowsPage = new WorkflowsPageClass();
diff --git a/cypress/e2e/10-settings-log-streaming.cy.ts b/cypress/e2e/10-settings-log-streaming.cy.ts
index 9acec76f42..1954543ca0 100644
--- a/cypress/e2e/10-settings-log-streaming.cy.ts
+++ b/cypress/e2e/10-settings-log-streaming.cy.ts
@@ -1,6 +1,6 @@
import { SettingsLogStreamingPage } from '../pages';
-import { getVisibleModalOverlay } from '../utils/modal';
import { getVisibleDropdown } from '../utils';
+import { getVisibleModalOverlay } from '../utils/modal';
const settingsLogStreamingPage = new SettingsLogStreamingPage();
diff --git a/cypress/e2e/10-undo-redo.cy.ts b/cypress/e2e/10-undo-redo.cy.ts
index 6453443376..7e3b5ef8ad 100644
--- a/cypress/e2e/10-undo-redo.cy.ts
+++ b/cypress/e2e/10-undo-redo.cy.ts
@@ -4,9 +4,9 @@ import {
SET_NODE_NAME,
EDIT_FIELDS_SET_NODE_NAME,
} from '../constants';
-import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
import { MessageBox as MessageBoxClass } from '../pages/modals/message-box';
import { NDV } from '../pages/ndv';
+import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
// Suite-specific constants
const CODE_NODE_NEW_NAME = 'Something else';
diff --git a/cypress/e2e/12-canvas-actions.cy.ts b/cypress/e2e/12-canvas-actions.cy.ts
index 53dad1cc89..8a42521d84 100644
--- a/cypress/e2e/12-canvas-actions.cy.ts
+++ b/cypress/e2e/12-canvas-actions.cy.ts
@@ -1,5 +1,3 @@
-import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
-import { successToast } from '../pages/notifications';
import {
MANUAL_TRIGGER_NODE_NAME,
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
@@ -9,6 +7,8 @@ import {
IF_NODE_NAME,
HTTP_REQUEST_NODE_NAME,
} from './../constants';
+import { successToast } from '../pages/notifications';
+import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
const WorkflowPage = new WorkflowPageClass();
describe('Canvas Actions', () => {
diff --git a/cypress/e2e/12-canvas.cy.ts b/cypress/e2e/12-canvas.cy.ts
index 325e509e79..4c7cccaafe 100644
--- a/cypress/e2e/12-canvas.cy.ts
+++ b/cypress/e2e/12-canvas.cy.ts
@@ -1,5 +1,3 @@
-import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
-import { NDV, WorkflowExecutionsTab } from '../pages';
import {
MANUAL_TRIGGER_NODE_NAME,
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
@@ -9,6 +7,8 @@ import {
SWITCH_NODE_NAME,
MERGE_NODE_NAME,
} from './../constants';
+import { NDV, WorkflowExecutionsTab } from '../pages';
+import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
const WorkflowPage = new WorkflowPageClass();
const ExecutionsTab = new WorkflowExecutionsTab();
diff --git a/cypress/e2e/14-mapping.cy.ts b/cypress/e2e/14-mapping.cy.ts
index 43103415e3..3bbbd0b293 100644
--- a/cypress/e2e/14-mapping.cy.ts
+++ b/cypress/e2e/14-mapping.cy.ts
@@ -1,10 +1,10 @@
-import { WorkflowPage, NDV } from '../pages';
-import { getVisibleSelect } from '../utils';
import {
MANUAL_TRIGGER_NODE_NAME,
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
SCHEDULE_TRIGGER_NODE_NAME,
} from './../constants';
+import { WorkflowPage, NDV } from '../pages';
+import { getVisibleSelect } from '../utils';
const workflowPage = new WorkflowPage();
const ndv = new NDV();
diff --git a/cypress/e2e/16-webhook-node.cy.ts b/cypress/e2e/16-webhook-node.cy.ts
index 791a704174..9346004388 100644
--- a/cypress/e2e/16-webhook-node.cy.ts
+++ b/cypress/e2e/16-webhook-node.cy.ts
@@ -1,7 +1,8 @@
import { nanoid } from 'nanoid';
+
+import { BACKEND_BASE_URL, EDIT_FIELDS_SET_NODE_NAME } from '../constants';
import { WorkflowPage, NDV, CredentialsModal } from '../pages';
import { cowBase64 } from '../support/binaryTestFiles';
-import { BACKEND_BASE_URL, EDIT_FIELDS_SET_NODE_NAME } from '../constants';
import { getVisibleSelect } from '../utils';
const workflowPage = new WorkflowPage();
diff --git a/cypress/e2e/17-sharing.cy.ts b/cypress/e2e/17-sharing.cy.ts
index c90a884325..e2af15f101 100644
--- a/cypress/e2e/17-sharing.cy.ts
+++ b/cypress/e2e/17-sharing.cy.ts
@@ -1,3 +1,4 @@
+import * as projects from '../composables/projects';
import { INSTANCE_MEMBERS, INSTANCE_OWNER, INSTANCE_ADMIN, NOTION_NODE_NAME } from '../constants';
import {
CredentialsModal,
@@ -8,7 +9,6 @@ import {
WorkflowsPage,
} from '../pages';
import { getVisibleDropdown, getVisiblePopper, getVisibleSelect } from '../utils';
-import * as projects from '../composables/projects';
/**
* User U1 - Instance owner
diff --git a/cypress/e2e/18-user-management.cy.ts b/cypress/e2e/18-user-management.cy.ts
index b53b0fdf53..fe91a72935 100644
--- a/cypress/e2e/18-user-management.cy.ts
+++ b/cypress/e2e/18-user-management.cy.ts
@@ -1,8 +1,8 @@
import { INSTANCE_MEMBERS, INSTANCE_OWNER, INSTANCE_ADMIN } from '../constants';
import { MainSidebar, SettingsSidebar, SettingsUsersPage } from '../pages';
+import { errorToast, successToast } from '../pages/notifications';
import { PersonalSettingsPage } from '../pages/settings-personal';
import { getVisibleSelect } from '../utils';
-import { errorToast, successToast } from '../pages/notifications';
/**
* User A - Instance owner
diff --git a/cypress/e2e/19-execution.cy.ts b/cypress/e2e/19-execution.cy.ts
index d2c463f1bb..d6b8d08fd5 100644
--- a/cypress/e2e/19-execution.cy.ts
+++ b/cypress/e2e/19-execution.cy.ts
@@ -1,5 +1,5 @@
-import { NDV, WorkflowExecutionsTab, WorkflowPage as WorkflowPageClass } from '../pages';
import { SCHEDULE_TRIGGER_NODE_NAME, EDIT_FIELDS_SET_NODE_NAME } from '../constants';
+import { NDV, WorkflowExecutionsTab, WorkflowPage as WorkflowPageClass } from '../pages';
import { clearNotifications, errorToast, successToast } from '../pages/notifications';
const workflowPage = new WorkflowPageClass();
diff --git a/cypress/e2e/2-credentials.cy.ts b/cypress/e2e/2-credentials.cy.ts
index 9dbe6c6b5d..dbc613bd64 100644
--- a/cypress/e2e/2-credentials.cy.ts
+++ b/cypress/e2e/2-credentials.cy.ts
@@ -1,4 +1,5 @@
import { type ICredentialType } from 'n8n-workflow';
+
import {
AGENT_NODE_NAME,
AI_TOOL_HTTP_NODE_NAME,
diff --git a/cypress/e2e/20-workflow-executions.cy.ts b/cypress/e2e/20-workflow-executions.cy.ts
index 5f2a260143..19256f3bf9 100644
--- a/cypress/e2e/20-workflow-executions.cy.ts
+++ b/cypress/e2e/20-workflow-executions.cy.ts
@@ -1,7 +1,8 @@
import type { RouteHandler } from 'cypress/types/net-stubbing';
+
+import executionOutOfMemoryServerResponse from '../fixtures/responses/execution-out-of-memory-server-response.json';
import { WorkflowPage } from '../pages';
import { WorkflowExecutionsTab } from '../pages/workflow-executions-tab';
-import executionOutOfMemoryServerResponse from '../fixtures/responses/execution-out-of-memory-server-response.json';
import { getVisibleSelect } from '../utils';
const workflowPage = new WorkflowPage();
diff --git a/cypress/e2e/21-community-nodes.cy.ts b/cypress/e2e/21-community-nodes.cy.ts
index bf88d3d24c..17f82ec573 100644
--- a/cypress/e2e/21-community-nodes.cy.ts
+++ b/cypress/e2e/21-community-nodes.cy.ts
@@ -1,11 +1,11 @@
import type { ICredentialType } from 'n8n-workflow';
-import { NodeCreator } from '../pages/features/node-creator';
-import CustomNodeFixture from '../fixtures/Custom_node.json';
-import { CredentialsModal, WorkflowPage } from '../pages';
-import CustomNodeWithN8nCredentialFixture from '../fixtures/Custom_node_n8n_credential.json';
-import CustomNodeWithCustomCredentialFixture from '../fixtures/Custom_node_custom_credential.json';
+
import CustomCredential from '../fixtures/Custom_credential.json';
-import { getVisibleSelect } from '../utils';
+import CustomNodeFixture from '../fixtures/Custom_node.json';
+import CustomNodeWithCustomCredentialFixture from '../fixtures/Custom_node_custom_credential.json';
+import CustomNodeWithN8nCredentialFixture from '../fixtures/Custom_node_n8n_credential.json';
+import { CredentialsModal, WorkflowPage } from '../pages';
+import { NodeCreator } from '../pages/features/node-creator';
import {
confirmCommunityNodeUninstall,
confirmCommunityNodeUpdate,
@@ -13,6 +13,7 @@ import {
installFirstCommunityNode,
visitCommunityNodesSettings,
} from '../pages/settings-community-nodes';
+import { getVisibleSelect } from '../utils';
const credentialsModal = new CredentialsModal();
const nodeCreatorFeature = new NodeCreator();
diff --git a/cypress/e2e/233-AI-switch-to-logs-on-error.cy.ts b/cypress/e2e/233-AI-switch-to-logs-on-error.cy.ts
index 4c733df90d..eca3af81fb 100644
--- a/cypress/e2e/233-AI-switch-to-logs-on-error.cy.ts
+++ b/cypress/e2e/233-AI-switch-to-logs-on-error.cy.ts
@@ -1,5 +1,18 @@
import type { ExecutionError } from 'n8n-workflow/src';
-import { NDV, WorkflowPage as WorkflowPageClass } from '../pages';
+
+import {
+ closeManualChatModal,
+ getManualChatMessages,
+ getManualChatModalLogs,
+ getManualChatModalLogsEntries,
+ sendManualChatMessage,
+} from '../composables/modals/chat-modal';
+import { setCredentialValues } from '../composables/modals/credential-modal';
+import {
+ clickCreateNewCredential,
+ clickExecuteNode,
+ clickGetBackToCanvas,
+} from '../composables/ndv';
import {
addLanguageModelNodeToParent,
addMemoryNodeToParent,
@@ -18,19 +31,7 @@ import {
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
MANUAL_TRIGGER_NODE_NAME,
} from '../constants';
-import {
- clickCreateNewCredential,
- clickExecuteNode,
- clickGetBackToCanvas,
-} from '../composables/ndv';
-import { setCredentialValues } from '../composables/modals/credential-modal';
-import {
- closeManualChatModal,
- getManualChatMessages,
- getManualChatModalLogs,
- getManualChatModalLogsEntries,
- sendManualChatMessage,
-} from '../composables/modals/chat-modal';
+import { NDV, WorkflowPage as WorkflowPageClass } from '../pages';
import { createMockNodeExecutionData, getVisibleSelect, runMockWorkflowExecution } from '../utils';
const ndv = new NDV();
diff --git a/cypress/e2e/27-cloud.cy.ts b/cypress/e2e/27-cloud.cy.ts
index dd0d3b06ba..e9b814597d 100644
--- a/cypress/e2e/27-cloud.cy.ts
+++ b/cypress/e2e/27-cloud.cy.ts
@@ -1,3 +1,4 @@
+import planData from '../fixtures/Plan_data_opt_in_trial.json';
import {
BannerStack,
MainSidebar,
@@ -5,7 +6,6 @@ import {
visitPublicApiPage,
getPublicApiUpgradeCTA,
} from '../pages';
-import planData from '../fixtures/Plan_data_opt_in_trial.json';
const mainSidebar = new MainSidebar();
const bannerStack = new BannerStack();
diff --git a/cypress/e2e/27-two-factor-authentication.cy.ts b/cypress/e2e/27-two-factor-authentication.cy.ts
index 21319dd79b..dc62a0c58c 100644
--- a/cypress/e2e/27-two-factor-authentication.cy.ts
+++ b/cypress/e2e/27-two-factor-authentication.cy.ts
@@ -1,9 +1,10 @@
import generateOTPToken from 'cypress-otp';
+
+import { MainSidebar } from './../pages/sidebar/main-sidebar';
import { INSTANCE_OWNER, INSTANCE_ADMIN, BACKEND_BASE_URL } from '../constants';
import { SigninPage } from '../pages';
-import { PersonalSettingsPage } from '../pages/settings-personal';
import { MfaLoginPage } from '../pages/mfa-login';
-import { MainSidebar } from './../pages/sidebar/main-sidebar';
+import { PersonalSettingsPage } from '../pages/settings-personal';
const MFA_SECRET = 'KVKFKRCPNZQUYMLXOVYDSQKJKZDTSRLD';
diff --git a/cypress/e2e/29-templates.cy.ts b/cypress/e2e/29-templates.cy.ts
index 5cc6657416..5b52889c94 100644
--- a/cypress/e2e/29-templates.cy.ts
+++ b/cypress/e2e/29-templates.cy.ts
@@ -1,9 +1,9 @@
+import OnboardingWorkflow from '../fixtures/Onboarding_workflow.json';
+import WorkflowTemplate from '../fixtures/Workflow_template_write_http_query.json';
+import { MainSidebar } from '../pages/sidebar/main-sidebar';
import { TemplatesPage } from '../pages/templates';
import { WorkflowPage } from '../pages/workflow';
import { WorkflowsPage } from '../pages/workflows';
-import { MainSidebar } from '../pages/sidebar/main-sidebar';
-import OnboardingWorkflow from '../fixtures/Onboarding_workflow.json';
-import WorkflowTemplate from '../fixtures/Workflow_template_write_http_query.json';
const templatesPage = new TemplatesPage();
const workflowPage = new WorkflowPage();
diff --git a/cypress/e2e/30-langchain.cy.ts b/cypress/e2e/30-langchain.cy.ts
index c6d0f4ab4d..0deec76e9f 100644
--- a/cypress/e2e/30-langchain.cy.ts
+++ b/cypress/e2e/30-langchain.cy.ts
@@ -1,4 +1,34 @@
-import { createMockNodeExecutionData, runMockWorkflowExecution } from '../utils';
+import {
+ AGENT_NODE_NAME,
+ MANUAL_CHAT_TRIGGER_NODE_NAME,
+ AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
+ MANUAL_TRIGGER_NODE_NAME,
+ AI_MEMORY_WINDOW_BUFFER_MEMORY_NODE_NAME,
+ AI_TOOL_CALCULATOR_NODE_NAME,
+ AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME,
+ AI_TOOL_CODE_NODE_NAME,
+ AI_TOOL_WIKIPEDIA_NODE_NAME,
+ BASIC_LLM_CHAIN_NODE_NAME,
+ EDIT_FIELDS_SET_NODE_NAME,
+ CHAT_TRIGGER_NODE_DISPLAY_NAME,
+} from './../constants';
+import {
+ closeManualChatModal,
+ getManualChatDialog,
+ getManualChatMessages,
+ getManualChatModal,
+ getManualChatModalLogs,
+ getManualChatModalLogsEntries,
+ getManualChatModalLogsTree,
+ sendManualChatMessage,
+} from '../composables/modals/chat-modal';
+import { setCredentialValues } from '../composables/modals/credential-modal';
+import {
+ clickCreateNewCredential,
+ clickExecuteNode,
+ clickGetBackToCanvas,
+ toggleParameterCheckboxInputByName,
+} from '../composables/ndv';
import {
addLanguageModelNodeToParent,
addMemoryNodeToParent,
@@ -14,37 +44,7 @@ import {
openNode,
getConnectionBySourceAndTarget,
} from '../composables/workflow';
-import {
- clickCreateNewCredential,
- clickExecuteNode,
- clickGetBackToCanvas,
- toggleParameterCheckboxInputByName,
-} from '../composables/ndv';
-import { setCredentialValues } from '../composables/modals/credential-modal';
-import {
- closeManualChatModal,
- getManualChatDialog,
- getManualChatMessages,
- getManualChatModal,
- getManualChatModalLogs,
- getManualChatModalLogsEntries,
- getManualChatModalLogsTree,
- sendManualChatMessage,
-} from '../composables/modals/chat-modal';
-import {
- AGENT_NODE_NAME,
- MANUAL_CHAT_TRIGGER_NODE_NAME,
- AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
- MANUAL_TRIGGER_NODE_NAME,
- AI_MEMORY_WINDOW_BUFFER_MEMORY_NODE_NAME,
- AI_TOOL_CALCULATOR_NODE_NAME,
- AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME,
- AI_TOOL_CODE_NODE_NAME,
- AI_TOOL_WIKIPEDIA_NODE_NAME,
- BASIC_LLM_CHAIN_NODE_NAME,
- EDIT_FIELDS_SET_NODE_NAME,
- CHAT_TRIGGER_NODE_DISPLAY_NAME,
-} from './../constants';
+import { createMockNodeExecutionData, runMockWorkflowExecution } from '../utils';
describe('Langchain Integration', () => {
beforeEach(() => {
diff --git a/cypress/e2e/31-demo.cy.ts b/cypress/e2e/31-demo.cy.ts
index eed3198c83..32307361fd 100644
--- a/cypress/e2e/31-demo.cy.ts
+++ b/cypress/e2e/31-demo.cy.ts
@@ -1,7 +1,7 @@
import workflow from '../fixtures/Manual_wait_set.json';
import { importWorkflow, visitDemoPage } from '../pages/demo';
-import { WorkflowPage } from '../pages/workflow';
import { errorToast } from '../pages/notifications';
+import { WorkflowPage } from '../pages/workflow';
const workflowPage = new WorkflowPage();
diff --git a/cypress/e2e/34-template-credentials-setup.cy.ts b/cypress/e2e/34-template-credentials-setup.cy.ts
index c5d9f2643f..815f4b1ceb 100644
--- a/cypress/e2e/34-template-credentials-setup.cy.ts
+++ b/cypress/e2e/34-template-credentials-setup.cy.ts
@@ -1,3 +1,8 @@
+import * as setupCredsModal from '../composables/modals/workflow-credential-setup-modal';
+import * as formStep from '../composables/setup-template-form-step';
+import { getSetupWorkflowCredentialsButton } from '../composables/setup-workflow-credentials-button';
+import TestTemplate1 from '../fixtures/Test_Template_1.json';
+import TestTemplate2 from '../fixtures/Test_Template_2.json';
import {
clickUseWorkflowButtonByTitle,
visitTemplateCollectionPage,
@@ -5,11 +10,6 @@ import {
} from '../pages/template-collection';
import * as templateCredentialsSetupPage from '../pages/template-credential-setup';
import { WorkflowPage } from '../pages/workflow';
-import * as formStep from '../composables/setup-template-form-step';
-import { getSetupWorkflowCredentialsButton } from '../composables/setup-workflow-credentials-button';
-import * as setupCredsModal from '../composables/modals/workflow-credential-setup-modal';
-import TestTemplate1 from '../fixtures/Test_Template_1.json';
-import TestTemplate2 from '../fixtures/Test_Template_2.json';
const workflowPage = new WorkflowPage();
diff --git a/cypress/e2e/36-versions.cy.ts b/cypress/e2e/36-versions.cy.ts
index 1d4fc51808..d749ae4537 100644
--- a/cypress/e2e/36-versions.cy.ts
+++ b/cypress/e2e/36-versions.cy.ts
@@ -1,10 +1,10 @@
-import { WorkflowsPage } from '../pages/workflows';
import {
closeVersionUpdatesPanel,
getVersionCard,
getVersionUpdatesPanelOpenButton,
openVersionUpdatesPanel,
} from '../composables/versions';
+import { WorkflowsPage } from '../pages/workflows';
const workflowsPage = new WorkflowsPage();
diff --git a/cypress/e2e/39-projects.cy.ts b/cypress/e2e/39-projects.cy.ts
index e2bf63df7d..59ed6bcb84 100644
--- a/cypress/e2e/39-projects.cy.ts
+++ b/cypress/e2e/39-projects.cy.ts
@@ -1,3 +1,4 @@
+import * as projects from '../composables/projects';
import { INSTANCE_MEMBERS, MANUAL_TRIGGER_NODE_NAME, NOTION_NODE_NAME } from '../constants';
import {
WorkflowsPage,
@@ -8,7 +9,6 @@ import {
NDV,
MainSidebar,
} from '../pages';
-import * as projects from '../composables/projects';
import { getVisibleDropdown, getVisibleModalOverlay, getVisibleSelect } from '../utils';
const workflowsPage = new WorkflowsPage();
diff --git a/cypress/e2e/4-node-creator.cy.ts b/cypress/e2e/4-node-creator.cy.ts
index bb47ef4765..9dfe128322 100644
--- a/cypress/e2e/4-node-creator.cy.ts
+++ b/cypress/e2e/4-node-creator.cy.ts
@@ -1,8 +1,8 @@
-import { NodeCreator } from '../pages/features/node-creator';
-import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
-import { NDV } from '../pages/ndv';
-import { getVisibleSelect } from '../utils';
import { IF_NODE_NAME } from '../constants';
+import { NodeCreator } from '../pages/features/node-creator';
+import { NDV } from '../pages/ndv';
+import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
+import { getVisibleSelect } from '../utils';
const nodeCreatorFeature = new NodeCreator();
const WorkflowPage = new WorkflowPageClass();
diff --git a/cypress/e2e/44-routing.cy.ts b/cypress/e2e/44-routing.cy.ts
index 67a092235b..1d3a8746a9 100644
--- a/cypress/e2e/44-routing.cy.ts
+++ b/cypress/e2e/44-routing.cy.ts
@@ -1,7 +1,7 @@
-import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows';
-import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
-import { EDIT_FIELDS_SET_NODE_NAME } from '../constants';
import { getSaveChangesModal } from '../composables/modals/save-changes-modal';
+import { EDIT_FIELDS_SET_NODE_NAME } from '../constants';
+import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
+import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows';
const WorkflowsPage = new WorkflowsPageClass();
const WorkflowPage = new WorkflowPageClass();
diff --git a/cypress/e2e/5-ndv.cy.ts b/cypress/e2e/5-ndv.cy.ts
index d38eb402b9..4608b5eefc 100644
--- a/cypress/e2e/5-ndv.cy.ts
+++ b/cypress/e2e/5-ndv.cy.ts
@@ -1,8 +1,8 @@
+import { setCredentialValues } from '../composables/modals/credential-modal';
+import { clickCreateNewCredential } from '../composables/ndv';
import { MANUAL_TRIGGER_NODE_DISPLAY_NAME, NOTION_NODE_NAME } from '../constants';
import { NDV, WorkflowPage } from '../pages';
import { NodeCreator } from '../pages/features/node-creator';
-import { clickCreateNewCredential } from '../composables/ndv';
-import { setCredentialValues } from '../composables/modals/credential-modal';
const workflowPage = new WorkflowPage();
const ndv = new NDV();
diff --git a/cypress/e2e/6-code-node.cy.ts b/cypress/e2e/6-code-node.cy.ts
index d26e0ded5d..5b422b4589 100644
--- a/cypress/e2e/6-code-node.cy.ts
+++ b/cypress/e2e/6-code-node.cy.ts
@@ -1,7 +1,8 @@
import { nanoid } from 'nanoid';
-import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
+
import { NDV } from '../pages/ndv';
import { successToast } from '../pages/notifications';
+import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
const WorkflowPage = new WorkflowPageClass();
const ndv = new NDV();
diff --git a/cypress/e2e/7-workflow-actions.cy.ts b/cypress/e2e/7-workflow-actions.cy.ts
index 7c7c3be554..8571b174d9 100644
--- a/cypress/e2e/7-workflow-actions.cy.ts
+++ b/cypress/e2e/7-workflow-actions.cy.ts
@@ -5,11 +5,11 @@ import {
EDIT_FIELDS_SET_NODE_NAME,
NOTION_NODE_NAME,
} from '../constants';
+import { WorkflowExecutionsTab } from '../pages';
+import { errorToast, successToast } from '../pages/notifications';
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows';
import { getVisibleSelect } from '../utils';
-import { WorkflowExecutionsTab } from '../pages';
-import { errorToast, successToast } from '../pages/notifications';
const NEW_WORKFLOW_NAME = 'Something else';
const DUPLICATE_WORKFLOW_NAME = 'Duplicated workflow';
diff --git a/cypress/pages/mfa-login.ts b/cypress/pages/mfa-login.ts
index ae4d916ba9..66fc197e3f 100644
--- a/cypress/pages/mfa-login.ts
+++ b/cypress/pages/mfa-login.ts
@@ -1,7 +1,7 @@
-import { N8N_AUTH_COOKIE } from '../constants';
import { BasePage } from './base';
import { SigninPage } from './signin';
import { WorkflowsPage } from './workflows';
+import { N8N_AUTH_COOKIE } from '../constants';
export class MfaLoginPage extends BasePage {
url = '/mfa';
diff --git a/cypress/pages/modals/credentials-modal.ts b/cypress/pages/modals/credentials-modal.ts
index f14f6be0b7..cd3ded63f8 100644
--- a/cypress/pages/modals/credentials-modal.ts
+++ b/cypress/pages/modals/credentials-modal.ts
@@ -1,5 +1,5 @@
-import { BasePage } from '../base';
import { getVisibleSelect } from '../../utils';
+import { BasePage } from '../base';
export class CredentialsModal extends BasePage {
getters = {
diff --git a/cypress/pages/ndv.ts b/cypress/pages/ndv.ts
index 8bd7ccf95f..b775deec6d 100644
--- a/cypress/pages/ndv.ts
+++ b/cypress/pages/ndv.ts
@@ -1,5 +1,5 @@
-import { getVisiblePopper, getVisibleSelect } from '../utils';
import { BasePage } from './base';
+import { getVisiblePopper, getVisibleSelect } from '../utils';
export class NDV extends BasePage {
getters = {
diff --git a/cypress/pages/settings-log-streaming.ts b/cypress/pages/settings-log-streaming.ts
index cc1ea1250d..9063b8dc41 100644
--- a/cypress/pages/settings-log-streaming.ts
+++ b/cypress/pages/settings-log-streaming.ts
@@ -1,5 +1,5 @@
-import { getVisibleSelect } from '../utils';
import { BasePage } from './base';
+import { getVisibleSelect } from '../utils';
export class SettingsLogStreamingPage extends BasePage {
url = '/settings/log-streaming';
diff --git a/cypress/pages/settings-personal.ts b/cypress/pages/settings-personal.ts
index 9872fbc668..4574f95691 100644
--- a/cypress/pages/settings-personal.ts
+++ b/cypress/pages/settings-personal.ts
@@ -1,7 +1,8 @@
import generateOTPToken from 'cypress-otp';
+
+import { BasePage } from './base';
import { ChangePasswordModal } from './modals/change-password-modal';
import { MfaSetupModal } from './modals/mfa-setup-modal';
-import { BasePage } from './base';
const changePasswordModal = new ChangePasswordModal();
const mfaSetupModal = new MfaSetupModal();
diff --git a/cypress/pages/settings-users.ts b/cypress/pages/settings-users.ts
index d188896225..1eaebc911a 100644
--- a/cypress/pages/settings-users.ts
+++ b/cypress/pages/settings-users.ts
@@ -1,8 +1,8 @@
-import { SettingsSidebar } from './sidebar/settings-sidebar';
+import { BasePage } from './base';
import { MainSidebar } from './sidebar/main-sidebar';
+import { SettingsSidebar } from './sidebar/settings-sidebar';
import { WorkflowPage } from './workflow';
import { WorkflowsPage } from './workflows';
-import { BasePage } from './base';
const workflowPage = new WorkflowPage();
const workflowsPage = new WorkflowsPage();
diff --git a/cypress/pages/signin.ts b/cypress/pages/signin.ts
index 22d0fd163a..a97fe4888e 100644
--- a/cypress/pages/signin.ts
+++ b/cypress/pages/signin.ts
@@ -1,6 +1,6 @@
-import { N8N_AUTH_COOKIE } from '../constants';
import { BasePage } from './base';
import { WorkflowsPage } from './workflows';
+import { N8N_AUTH_COOKIE } from '../constants';
export class SigninPage extends BasePage {
url = '/signin';
diff --git a/cypress/pages/template-credential-setup.ts b/cypress/pages/template-credential-setup.ts
index 3fa4d20671..06c1baab8e 100644
--- a/cypress/pages/template-credential-setup.ts
+++ b/cypress/pages/template-credential-setup.ts
@@ -1,6 +1,6 @@
-import * as formStep from '../composables/setup-template-form-step';
-import { overrideFeatureFlag } from '../composables/featureFlags';
import { CredentialsModal, MessageBox } from './modals';
+import { overrideFeatureFlag } from '../composables/featureFlags';
+import * as formStep from '../composables/setup-template-form-step';
const credentialsModal = new CredentialsModal();
const messageBox = new MessageBox();
diff --git a/cypress/pages/variables.ts b/cypress/pages/variables.ts
index c74624686e..6ac9a939b2 100644
--- a/cypress/pages/variables.ts
+++ b/cypress/pages/variables.ts
@@ -1,4 +1,5 @@
import { BasePage } from './base';
+
import Chainable = Cypress.Chainable;
export class VariablesPage extends BasePage {
diff --git a/cypress/pages/workflow.ts b/cypress/pages/workflow.ts
index 0c2a269607..89186ee34e 100644
--- a/cypress/pages/workflow.ts
+++ b/cypress/pages/workflow.ts
@@ -1,8 +1,8 @@
+import { BasePage } from './base';
+import { NodeCreator } from './features/node-creator';
import { META_KEY } from '../constants';
import { getVisibleSelect } from '../utils';
import { getUniqueWorkflowName } from '../utils/workflowUtils';
-import { BasePage } from './base';
-import { NodeCreator } from './features/node-creator';
const nodeCreator = new NodeCreator();
export class WorkflowPage extends BasePage {
diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts
index 808c0b6aca..35f100fded 100644
--- a/cypress/support/commands.ts
+++ b/cypress/support/commands.ts
@@ -1,7 +1,7 @@
import 'cypress-real-events';
-import FakeTimers from '@sinonjs/fake-timers';
import type { FrontendSettings } from '@n8n/api-types';
-import { WorkflowPage } from '../pages';
+import FakeTimers from '@sinonjs/fake-timers';
+
import {
BACKEND_BASE_URL,
INSTANCE_ADMIN,
@@ -9,6 +9,7 @@ import {
INSTANCE_OWNER,
N8N_AUTH_COOKIE,
} from '../constants';
+import { WorkflowPage } from '../pages';
import { getUniqueWorkflowName } from '../utils/workflowUtils';
Cypress.Commands.add('setAppDate', (targetDate: number | Date) => {
diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts
index 4d5d7a7f9a..0fe782499d 100644
--- a/cypress/support/e2e.ts
+++ b/cypress/support/e2e.ts
@@ -1,5 +1,6 @@
import cloneDeep from 'lodash/cloneDeep';
import merge from 'lodash/merge';
+
import { settings } from './commands';
before(() => {
diff --git a/cypress/utils/executions.ts b/cypress/utils/executions.ts
index e42e2152d6..eb0dbfc251 100644
--- a/cypress/utils/executions.ts
+++ b/cypress/utils/executions.ts
@@ -1,5 +1,6 @@
-import { nanoid } from 'nanoid';
import type { IDataObject, IPinData, ITaskData, ITaskDataConnections } from 'n8n-workflow';
+import { nanoid } from 'nanoid';
+
import { clickExecuteWorkflowButton } from '../composables/workflow';
export function createMockNodeExecutionData(
diff --git a/packages/@n8n/api-types/src/push/index.ts b/packages/@n8n/api-types/src/push/index.ts
index 3eefb0851c..b97a179141 100644
--- a/packages/@n8n/api-types/src/push/index.ts
+++ b/packages/@n8n/api-types/src/push/index.ts
@@ -1,10 +1,10 @@
-import type { ExecutionPushMessage } from './execution';
-import type { WorkflowPushMessage } from './workflow';
-import type { HotReloadPushMessage } from './hot-reload';
-import type { WorkerPushMessage } from './worker';
-import type { WebhookPushMessage } from './webhook';
import type { CollaborationPushMessage } from './collaboration';
import type { DebugPushMessage } from './debug';
+import type { ExecutionPushMessage } from './execution';
+import type { HotReloadPushMessage } from './hot-reload';
+import type { WebhookPushMessage } from './webhook';
+import type { WorkerPushMessage } from './worker';
+import type { WorkflowPushMessage } from './workflow';
export type PushMessage =
| ExecutionPushMessage
diff --git a/packages/@n8n/benchmark/src/commands/list.ts b/packages/@n8n/benchmark/src/commands/list.ts
index 7bcc4de2ce..34b8273209 100644
--- a/packages/@n8n/benchmark/src/commands/list.ts
+++ b/packages/@n8n/benchmark/src/commands/list.ts
@@ -1,6 +1,7 @@
import { Command } from '@oclif/core';
-import { ScenarioLoader } from '@/scenario/scenario-loader';
+
import { testScenariosPath } from '@/config/common-flags';
+import { ScenarioLoader } from '@/scenario/scenario-loader';
export default class ListCommand extends Command {
static description = 'List all available scenarios';
diff --git a/packages/@n8n/benchmark/src/commands/run.ts b/packages/@n8n/benchmark/src/commands/run.ts
index eb895c842e..164eef0f41 100644
--- a/packages/@n8n/benchmark/src/commands/run.ts
+++ b/packages/@n8n/benchmark/src/commands/run.ts
@@ -1,11 +1,12 @@
import { Command, Flags } from '@oclif/core';
-import { ScenarioLoader } from '@/scenario/scenario-loader';
-import { ScenarioRunner } from '@/test-execution/scenario-runner';
+
+import { testScenariosPath } from '@/config/common-flags';
import { N8nApiClient } from '@/n8n-api-client/n8n-api-client';
import { ScenarioDataFileLoader } from '@/scenario/scenario-data-loader';
+import { ScenarioLoader } from '@/scenario/scenario-loader';
import type { K6Tag } from '@/test-execution/k6-executor';
import { K6Executor } from '@/test-execution/k6-executor';
-import { testScenariosPath } from '@/config/common-flags';
+import { ScenarioRunner } from '@/test-execution/scenario-runner';
export default class RunCommand extends Command {
static description = 'Run all (default) or specified test scenarios';
diff --git a/packages/@n8n/benchmark/src/n8n-api-client/authenticated-n8n-api-client.ts b/packages/@n8n/benchmark/src/n8n-api-client/authenticated-n8n-api-client.ts
index 0272181799..2555ea8061 100644
--- a/packages/@n8n/benchmark/src/n8n-api-client/authenticated-n8n-api-client.ts
+++ b/packages/@n8n/benchmark/src/n8n-api-client/authenticated-n8n-api-client.ts
@@ -1,4 +1,5 @@
import type { AxiosRequestConfig } from 'axios';
+
import { N8nApiClient } from './n8n-api-client';
export class AuthenticatedN8nApiClient extends N8nApiClient {
diff --git a/packages/@n8n/benchmark/src/n8n-api-client/workflows-api-client.ts b/packages/@n8n/benchmark/src/n8n-api-client/workflows-api-client.ts
index 76eea4284c..92bcfad89f 100644
--- a/packages/@n8n/benchmark/src/n8n-api-client/workflows-api-client.ts
+++ b/packages/@n8n/benchmark/src/n8n-api-client/workflows-api-client.ts
@@ -1,6 +1,7 @@
-import type { AuthenticatedN8nApiClient } from './authenticated-n8n-api-client';
import type { Workflow } from '@/n8n-api-client/n8n-api-client.types';
+import type { AuthenticatedN8nApiClient } from './authenticated-n8n-api-client';
+
export class WorkflowApiClient {
constructor(private readonly apiClient: AuthenticatedN8nApiClient) {}
diff --git a/packages/@n8n/benchmark/src/scenario/scenario-data-loader.ts b/packages/@n8n/benchmark/src/scenario/scenario-data-loader.ts
index b601d19902..12fcc58ee3 100644
--- a/packages/@n8n/benchmark/src/scenario/scenario-data-loader.ts
+++ b/packages/@n8n/benchmark/src/scenario/scenario-data-loader.ts
@@ -1,7 +1,8 @@
import * as fs from 'node:fs';
import * as path from 'node:path';
-import type { Scenario } from '@/types/scenario';
+
import type { Workflow } from '@/n8n-api-client/n8n-api-client.types';
+import type { Scenario } from '@/types/scenario';
/**
* Loads scenario data files from FS
diff --git a/packages/@n8n/benchmark/src/scenario/scenario-loader.ts b/packages/@n8n/benchmark/src/scenario/scenario-loader.ts
index 13cc52daf0..4f315c1bf7 100644
--- a/packages/@n8n/benchmark/src/scenario/scenario-loader.ts
+++ b/packages/@n8n/benchmark/src/scenario/scenario-loader.ts
@@ -1,6 +1,7 @@
+import { createHash } from 'node:crypto';
import * as fs from 'node:fs';
import * as path from 'path';
-import { createHash } from 'node:crypto';
+
import type { Scenario, ScenarioManifest } from '@/types/scenario';
export class ScenarioLoader {
diff --git a/packages/@n8n/benchmark/src/test-execution/k6-executor.ts b/packages/@n8n/benchmark/src/test-execution/k6-executor.ts
index f99ffd80d9..4fcf11d45b 100644
--- a/packages/@n8n/benchmark/src/test-execution/k6-executor.ts
+++ b/packages/@n8n/benchmark/src/test-execution/k6-executor.ts
@@ -1,9 +1,10 @@
import fs from 'fs';
-import path from 'path';
import assert from 'node:assert/strict';
+import path from 'path';
import { $, which, tmpfile } from 'zx';
-import type { Scenario } from '@/types/scenario';
+
import { buildTestReport, type K6Tag } from '@/test-execution/test-report';
+import type { Scenario } from '@/types/scenario';
export type { K6Tag };
export type K6ExecutorOpts = {
diff --git a/packages/@n8n/benchmark/src/test-execution/scenario-runner.ts b/packages/@n8n/benchmark/src/test-execution/scenario-runner.ts
index 135ebc6007..84d1d8b096 100644
--- a/packages/@n8n/benchmark/src/test-execution/scenario-runner.ts
+++ b/packages/@n8n/benchmark/src/test-execution/scenario-runner.ts
@@ -1,9 +1,10 @@
-import type { K6Executor } from './k6-executor';
-import type { Scenario } from '@/types/scenario';
+import { AuthenticatedN8nApiClient } from '@/n8n-api-client/authenticated-n8n-api-client';
import type { N8nApiClient } from '@/n8n-api-client/n8n-api-client';
import type { ScenarioDataFileLoader } from '@/scenario/scenario-data-loader';
import { ScenarioDataImporter } from '@/test-execution/scenario-data-importer';
-import { AuthenticatedN8nApiClient } from '@/n8n-api-client/authenticated-n8n-api-client';
+import type { Scenario } from '@/types/scenario';
+
+import type { K6Executor } from './k6-executor';
/**
* Runs scenarios
diff --git a/packages/@n8n/benchmark/src/test-execution/test-report.ts b/packages/@n8n/benchmark/src/test-execution/test-report.ts
index d3177aeb79..8e858c5e66 100644
--- a/packages/@n8n/benchmark/src/test-execution/test-report.ts
+++ b/packages/@n8n/benchmark/src/test-execution/test-report.ts
@@ -1,4 +1,5 @@
import { nanoid } from 'nanoid';
+
import type { Scenario } from '@/types/scenario';
export type K6Tag = {
diff --git a/packages/@n8n/chat/src/App.vue b/packages/@n8n/chat/src/App.vue
index ec90bef425..0614b7cf73 100644
--- a/packages/@n8n/chat/src/App.vue
+++ b/packages/@n8n/chat/src/App.vue
@@ -1,8 +1,9 @@
diff --git a/packages/@n8n/chat/src/components/Input.vue b/packages/@n8n/chat/src/components/Input.vue
index af4b3343b7..3e823917e0 100644
--- a/packages/@n8n/chat/src/components/Input.vue
+++ b/packages/@n8n/chat/src/components/Input.vue
@@ -1,12 +1,14 @@