mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
Merge remote-tracking branch 'origin/master' into postExecutePromises
This commit is contained in:
commit
d62b174db4
1
.github/workflows/benchmark-nightly.yml
vendored
1
.github/workflows/benchmark-nightly.yml
vendored
|
@ -84,6 +84,7 @@ jobs:
|
||||||
|
|
||||||
# We need to login again because the access token expires
|
# We need to login again because the access token expires
|
||||||
- name: Azure login
|
- name: Azure login
|
||||||
|
if: always()
|
||||||
uses: azure/login@v2.1.1
|
uses: azure/login@v2.1.1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ env.ARM_CLIENT_ID }}
|
client-id: ${{ env.ARM_CLIENT_ID }}
|
||||||
|
|
42
.github/workflows/chromatic.yml
vendored
42
.github/workflows/chromatic.yml
vendored
|
@ -4,19 +4,49 @@ on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
pull_request_review:
|
pull_request_review:
|
||||||
types: [submitted]
|
types: [submitted]
|
||||||
branches:
|
|
||||||
- 'master'
|
|
||||||
paths:
|
|
||||||
- packages/design-system/**
|
|
||||||
- .github/workflows/chromatic.yml
|
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: chromatic-${{ github.event.pull_request.number || github.ref }}
|
group: chromatic-${{ github.event.pull_request.number || github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
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:
|
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
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.1
|
- uses: actions/checkout@v4.1.1
|
||||||
|
|
3
.github/workflows/ci-postgres-mysql.yml
vendored
3
.github/workflows/ci-postgres-mysql.yml
vendored
|
@ -10,8 +10,6 @@ on:
|
||||||
- .github/workflows/ci-postgres-mysql.yml
|
- .github/workflows/ci-postgres-mysql.yml
|
||||||
pull_request_review:
|
pull_request_review:
|
||||||
types: [submitted]
|
types: [submitted]
|
||||||
branches:
|
|
||||||
- 'release/*'
|
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: db-${{ github.event.pull_request.number || github.ref }}
|
group: db-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
@ -21,6 +19,7 @@ jobs:
|
||||||
build:
|
build:
|
||||||
name: Install & Build
|
name: Install & Build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name != 'pull_request_review' || startsWith(github.event.pull_request.base.ref, 'release/')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.1
|
- uses: actions/checkout@v4.1.1
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
|
|
3
.github/workflows/ci-pull-requests.yml
vendored
3
.github/workflows/ci-pull-requests.yml
vendored
|
@ -30,6 +30,9 @@ jobs:
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
|
||||||
|
- name: Run formatcheck
|
||||||
|
run: pnpm format:check
|
||||||
|
|
||||||
- name: Run typecheck
|
- name: Run typecheck
|
||||||
run: pnpm typecheck
|
run: pnpm typecheck
|
||||||
|
|
||||||
|
|
44
.github/workflows/e2e-tests-pr.yml
vendored
44
.github/workflows/e2e-tests-pr.yml
vendored
|
@ -3,19 +3,51 @@ name: PR E2E
|
||||||
on:
|
on:
|
||||||
pull_request_review:
|
pull_request_review:
|
||||||
types: [submitted]
|
types: [submitted]
|
||||||
branches:
|
|
||||||
- 'master'
|
|
||||||
- 'release/*'
|
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: e2e-${{ github.event.pull_request.number || github.ref }}
|
group: e2e-${{ github.event.pull_request.number || github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
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:
|
run-e2e-tests:
|
||||||
name: E2E [Electron/Node 18]
|
name: E2E [Electron/Node 18]
|
||||||
uses: ./.github/workflows/e2e-reusable.yml
|
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:
|
with:
|
||||||
pr_number: ${{ github.event.pull_request.number }}
|
pr_number: ${{ github.event.pull_request.number }}
|
||||||
user: ${{ github.event.pull_request.user.login || 'PR User' }}
|
user: ${{ github.event.pull_request.user.login || 'PR User' }}
|
||||||
|
@ -25,11 +57,11 @@ jobs:
|
||||||
post-e2e-tests:
|
post-e2e-tests:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: E2E [Electron/Node 18] - Checks
|
name: E2E [Electron/Node 18] - Checks
|
||||||
needs: [run-e2e-tests]
|
needs: [get-metadata, run-e2e-tests]
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- name: E2E success comment
|
- 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
|
uses: peter-evans/create-or-update-comment@v4.0.0
|
||||||
with:
|
with:
|
||||||
issue-number: ${{ github.event.pull_request.number }}
|
issue-number: ${{ github.event.pull_request.number }}
|
||||||
|
|
|
@ -7,3 +7,11 @@ packages/nodes-base/nodes/**/test
|
||||||
packages/cli/templates/form-trigger.handlebars
|
packages/cli/templates/form-trigger.handlebars
|
||||||
cypress/fixtures
|
cypress/fixtures
|
||||||
CHANGELOG.md
|
CHANGELOG.md
|
||||||
|
.github/pull_request_template.md
|
||||||
|
# Ignored for now
|
||||||
|
**/*.md
|
||||||
|
# Handled by biome
|
||||||
|
**/*.ts
|
||||||
|
**/*.js
|
||||||
|
**/*.json
|
||||||
|
**/*.jsonc
|
||||||
|
|
1
.vscode/extensions.json
vendored
1
.vscode/extensions.json
vendored
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
|
"biomejs.biome",
|
||||||
"streetsidesoftware.code-spell-checker",
|
"streetsidesoftware.code-spell-checker",
|
||||||
"dangmai.workspace-default-settings",
|
"dangmai.workspace-default-settings",
|
||||||
"dbaeumer.vscode-eslint",
|
"dbaeumer.vscode-eslint",
|
||||||
|
|
16
.vscode/settings.default.json
vendored
16
.vscode/settings.default.json
vendored
|
@ -1,6 +1,22 @@
|
||||||
{
|
{
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"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": {
|
"search.exclude": {
|
||||||
"node_modules": true,
|
"node_modules": true,
|
||||||
"dist": true,
|
"dist": true,
|
||||||
|
|
48
biome.jsonc
Normal file
48
biome.jsonc
Normal file
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
cypress/biome.jsonc
Normal file
7
cypress/biome.jsonc
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"$schema": "../node_modules/@biomejs/biome/configuration_schema.json",
|
||||||
|
"extends": ["../biome.jsonc"],
|
||||||
|
"formatter": {
|
||||||
|
"ignore": ["fixtures/**"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { ROUTES } from '../constants';
|
|
||||||
import { getManualChatModal } from './modals/chat-modal';
|
import { getManualChatModal } from './modals/chat-modal';
|
||||||
|
import { ROUTES } from '../constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types
|
* Types
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows';
|
|
||||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||||
|
import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows';
|
||||||
import { getUniqueWorkflowName } from '../utils/workflowUtils';
|
import { getUniqueWorkflowName } from '../utils/workflowUtils';
|
||||||
|
|
||||||
const WorkflowsPage = new WorkflowsPageClass();
|
const WorkflowsPage = new WorkflowsPageClass();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { SettingsLogStreamingPage } from '../pages';
|
import { SettingsLogStreamingPage } from '../pages';
|
||||||
import { getVisibleModalOverlay } from '../utils/modal';
|
|
||||||
import { getVisibleDropdown } from '../utils';
|
import { getVisibleDropdown } from '../utils';
|
||||||
|
import { getVisibleModalOverlay } from '../utils/modal';
|
||||||
|
|
||||||
const settingsLogStreamingPage = new SettingsLogStreamingPage();
|
const settingsLogStreamingPage = new SettingsLogStreamingPage();
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@ import {
|
||||||
SET_NODE_NAME,
|
SET_NODE_NAME,
|
||||||
EDIT_FIELDS_SET_NODE_NAME,
|
EDIT_FIELDS_SET_NODE_NAME,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
|
||||||
import { MessageBox as MessageBoxClass } from '../pages/modals/message-box';
|
import { MessageBox as MessageBoxClass } from '../pages/modals/message-box';
|
||||||
import { NDV } from '../pages/ndv';
|
import { NDV } from '../pages/ndv';
|
||||||
|
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||||
|
|
||||||
// Suite-specific constants
|
// Suite-specific constants
|
||||||
const CODE_NODE_NEW_NAME = 'Something else';
|
const CODE_NODE_NEW_NAME = 'Something else';
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
|
||||||
import { successToast } from '../pages/notifications';
|
|
||||||
import {
|
import {
|
||||||
MANUAL_TRIGGER_NODE_NAME,
|
MANUAL_TRIGGER_NODE_NAME,
|
||||||
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
||||||
|
@ -9,6 +7,8 @@ import {
|
||||||
IF_NODE_NAME,
|
IF_NODE_NAME,
|
||||||
HTTP_REQUEST_NODE_NAME,
|
HTTP_REQUEST_NODE_NAME,
|
||||||
} from './../constants';
|
} from './../constants';
|
||||||
|
import { successToast } from '../pages/notifications';
|
||||||
|
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||||
|
|
||||||
const WorkflowPage = new WorkflowPageClass();
|
const WorkflowPage = new WorkflowPageClass();
|
||||||
describe('Canvas Actions', () => {
|
describe('Canvas Actions', () => {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
|
||||||
import { NDV, WorkflowExecutionsTab } from '../pages';
|
|
||||||
import {
|
import {
|
||||||
MANUAL_TRIGGER_NODE_NAME,
|
MANUAL_TRIGGER_NODE_NAME,
|
||||||
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
||||||
|
@ -9,6 +7,8 @@ import {
|
||||||
SWITCH_NODE_NAME,
|
SWITCH_NODE_NAME,
|
||||||
MERGE_NODE_NAME,
|
MERGE_NODE_NAME,
|
||||||
} from './../constants';
|
} from './../constants';
|
||||||
|
import { NDV, WorkflowExecutionsTab } from '../pages';
|
||||||
|
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||||
|
|
||||||
const WorkflowPage = new WorkflowPageClass();
|
const WorkflowPage = new WorkflowPageClass();
|
||||||
const ExecutionsTab = new WorkflowExecutionsTab();
|
const ExecutionsTab = new WorkflowExecutionsTab();
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { WorkflowPage, NDV } from '../pages';
|
|
||||||
import { getVisibleSelect } from '../utils';
|
|
||||||
import {
|
import {
|
||||||
MANUAL_TRIGGER_NODE_NAME,
|
MANUAL_TRIGGER_NODE_NAME,
|
||||||
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
||||||
SCHEDULE_TRIGGER_NODE_NAME,
|
SCHEDULE_TRIGGER_NODE_NAME,
|
||||||
} from './../constants';
|
} from './../constants';
|
||||||
|
import { WorkflowPage, NDV } from '../pages';
|
||||||
|
import { getVisibleSelect } from '../utils';
|
||||||
|
|
||||||
const workflowPage = new WorkflowPage();
|
const workflowPage = new WorkflowPage();
|
||||||
const ndv = new NDV();
|
const ndv = new NDV();
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
|
|
||||||
|
import { BACKEND_BASE_URL, EDIT_FIELDS_SET_NODE_NAME } from '../constants';
|
||||||
import { WorkflowPage, NDV, CredentialsModal } from '../pages';
|
import { WorkflowPage, NDV, CredentialsModal } from '../pages';
|
||||||
import { cowBase64 } from '../support/binaryTestFiles';
|
import { cowBase64 } from '../support/binaryTestFiles';
|
||||||
import { BACKEND_BASE_URL, EDIT_FIELDS_SET_NODE_NAME } from '../constants';
|
|
||||||
import { getVisibleSelect } from '../utils';
|
import { getVisibleSelect } from '../utils';
|
||||||
|
|
||||||
const workflowPage = new WorkflowPage();
|
const workflowPage = new WorkflowPage();
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import * as projects from '../composables/projects';
|
||||||
import { INSTANCE_MEMBERS, INSTANCE_OWNER, INSTANCE_ADMIN, NOTION_NODE_NAME } from '../constants';
|
import { INSTANCE_MEMBERS, INSTANCE_OWNER, INSTANCE_ADMIN, NOTION_NODE_NAME } from '../constants';
|
||||||
import {
|
import {
|
||||||
CredentialsModal,
|
CredentialsModal,
|
||||||
|
@ -8,7 +9,6 @@ import {
|
||||||
WorkflowsPage,
|
WorkflowsPage,
|
||||||
} from '../pages';
|
} from '../pages';
|
||||||
import { getVisibleDropdown, getVisiblePopper, getVisibleSelect } from '../utils';
|
import { getVisibleDropdown, getVisiblePopper, getVisibleSelect } from '../utils';
|
||||||
import * as projects from '../composables/projects';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User U1 - Instance owner
|
* User U1 - Instance owner
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { INSTANCE_MEMBERS, INSTANCE_OWNER, INSTANCE_ADMIN } from '../constants';
|
import { INSTANCE_MEMBERS, INSTANCE_OWNER, INSTANCE_ADMIN } from '../constants';
|
||||||
import { MainSidebar, SettingsSidebar, SettingsUsersPage } from '../pages';
|
import { MainSidebar, SettingsSidebar, SettingsUsersPage } from '../pages';
|
||||||
|
import { errorToast, successToast } from '../pages/notifications';
|
||||||
import { PersonalSettingsPage } from '../pages/settings-personal';
|
import { PersonalSettingsPage } from '../pages/settings-personal';
|
||||||
import { getVisibleSelect } from '../utils';
|
import { getVisibleSelect } from '../utils';
|
||||||
import { errorToast, successToast } from '../pages/notifications';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User A - Instance owner
|
* User A - Instance owner
|
||||||
|
|
|
@ -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 { 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';
|
import { clearNotifications, errorToast, successToast } from '../pages/notifications';
|
||||||
|
|
||||||
const workflowPage = new WorkflowPageClass();
|
const workflowPage = new WorkflowPageClass();
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { type ICredentialType } from 'n8n-workflow';
|
import { type ICredentialType } from 'n8n-workflow';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AGENT_NODE_NAME,
|
AGENT_NODE_NAME,
|
||||||
AI_TOOL_HTTP_NODE_NAME,
|
AI_TOOL_HTTP_NODE_NAME,
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import type { RouteHandler } from 'cypress/types/net-stubbing';
|
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 { WorkflowPage } from '../pages';
|
||||||
import { WorkflowExecutionsTab } from '../pages/workflow-executions-tab';
|
import { WorkflowExecutionsTab } from '../pages/workflow-executions-tab';
|
||||||
import executionOutOfMemoryServerResponse from '../fixtures/responses/execution-out-of-memory-server-response.json';
|
|
||||||
import { getVisibleSelect } from '../utils';
|
import { getVisibleSelect } from '../utils';
|
||||||
|
|
||||||
const workflowPage = new WorkflowPage();
|
const workflowPage = new WorkflowPage();
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import type { ICredentialType } from 'n8n-workflow';
|
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 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 {
|
import {
|
||||||
confirmCommunityNodeUninstall,
|
confirmCommunityNodeUninstall,
|
||||||
confirmCommunityNodeUpdate,
|
confirmCommunityNodeUpdate,
|
||||||
|
@ -13,6 +13,7 @@ import {
|
||||||
installFirstCommunityNode,
|
installFirstCommunityNode,
|
||||||
visitCommunityNodesSettings,
|
visitCommunityNodesSettings,
|
||||||
} from '../pages/settings-community-nodes';
|
} from '../pages/settings-community-nodes';
|
||||||
|
import { getVisibleSelect } from '../utils';
|
||||||
|
|
||||||
const credentialsModal = new CredentialsModal();
|
const credentialsModal = new CredentialsModal();
|
||||||
const nodeCreatorFeature = new NodeCreator();
|
const nodeCreatorFeature = new NodeCreator();
|
||||||
|
|
|
@ -1,5 +1,18 @@
|
||||||
import type { ExecutionError } from 'n8n-workflow/src';
|
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 {
|
import {
|
||||||
addLanguageModelNodeToParent,
|
addLanguageModelNodeToParent,
|
||||||
addMemoryNodeToParent,
|
addMemoryNodeToParent,
|
||||||
|
@ -18,19 +31,7 @@ import {
|
||||||
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
||||||
MANUAL_TRIGGER_NODE_NAME,
|
MANUAL_TRIGGER_NODE_NAME,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
import {
|
import { NDV, WorkflowPage as WorkflowPageClass } from '../pages';
|
||||||
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 { createMockNodeExecutionData, getVisibleSelect, runMockWorkflowExecution } from '../utils';
|
import { createMockNodeExecutionData, getVisibleSelect, runMockWorkflowExecution } from '../utils';
|
||||||
|
|
||||||
const ndv = new NDV();
|
const ndv = new NDV();
|
||||||
|
|
|
@ -227,7 +227,7 @@ describe('NDV', () => {
|
||||||
|
|
||||||
workflowPage.actions.zoomToFit();
|
workflowPage.actions.zoomToFit();
|
||||||
|
|
||||||
/* prettier-ignore */
|
// biome-ignore format:
|
||||||
const PINNED_DATA = [
|
const PINNED_DATA = [
|
||||||
{
|
{
|
||||||
"id": "abc",
|
"id": "abc",
|
||||||
|
@ -263,7 +263,6 @@ describe('NDV', () => {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
/* prettier-ignore */
|
|
||||||
workflowPage.actions.openNode('Get thread details1');
|
workflowPage.actions.openNode('Get thread details1');
|
||||||
ndv.actions.pastePinnedData(PINNED_DATA);
|
ndv.actions.pastePinnedData(PINNED_DATA);
|
||||||
ndv.actions.close();
|
ndv.actions.close();
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import planData from '../fixtures/Plan_data_opt_in_trial.json';
|
||||||
import {
|
import {
|
||||||
BannerStack,
|
BannerStack,
|
||||||
MainSidebar,
|
MainSidebar,
|
||||||
|
@ -5,7 +6,6 @@ import {
|
||||||
visitPublicApiPage,
|
visitPublicApiPage,
|
||||||
getPublicApiUpgradeCTA,
|
getPublicApiUpgradeCTA,
|
||||||
} from '../pages';
|
} from '../pages';
|
||||||
import planData from '../fixtures/Plan_data_opt_in_trial.json';
|
|
||||||
|
|
||||||
const mainSidebar = new MainSidebar();
|
const mainSidebar = new MainSidebar();
|
||||||
const bannerStack = new BannerStack();
|
const bannerStack = new BannerStack();
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import generateOTPToken from 'cypress-otp';
|
import generateOTPToken from 'cypress-otp';
|
||||||
|
|
||||||
|
import { MainSidebar } from './../pages/sidebar/main-sidebar';
|
||||||
import { INSTANCE_OWNER, INSTANCE_ADMIN, BACKEND_BASE_URL } from '../constants';
|
import { INSTANCE_OWNER, INSTANCE_ADMIN, BACKEND_BASE_URL } from '../constants';
|
||||||
import { SigninPage } from '../pages';
|
import { SigninPage } from '../pages';
|
||||||
import { PersonalSettingsPage } from '../pages/settings-personal';
|
|
||||||
import { MfaLoginPage } from '../pages/mfa-login';
|
import { MfaLoginPage } from '../pages/mfa-login';
|
||||||
import { MainSidebar } from './../pages/sidebar/main-sidebar';
|
import { PersonalSettingsPage } from '../pages/settings-personal';
|
||||||
|
|
||||||
const MFA_SECRET = 'KVKFKRCPNZQUYMLXOVYDSQKJKZDTSRLD';
|
const MFA_SECRET = 'KVKFKRCPNZQUYMLXOVYDSQKJKZDTSRLD';
|
||||||
|
|
||||||
|
|
|
@ -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 { TemplatesPage } from '../pages/templates';
|
||||||
import { WorkflowPage } from '../pages/workflow';
|
import { WorkflowPage } from '../pages/workflow';
|
||||||
import { WorkflowsPage } from '../pages/workflows';
|
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 templatesPage = new TemplatesPage();
|
||||||
const workflowPage = new WorkflowPage();
|
const workflowPage = new WorkflowPage();
|
||||||
|
|
|
@ -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 {
|
import {
|
||||||
addLanguageModelNodeToParent,
|
addLanguageModelNodeToParent,
|
||||||
addMemoryNodeToParent,
|
addMemoryNodeToParent,
|
||||||
|
@ -14,37 +44,7 @@ import {
|
||||||
openNode,
|
openNode,
|
||||||
getConnectionBySourceAndTarget,
|
getConnectionBySourceAndTarget,
|
||||||
} from '../composables/workflow';
|
} from '../composables/workflow';
|
||||||
import {
|
import { createMockNodeExecutionData, runMockWorkflowExecution } from '../utils';
|
||||||
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';
|
|
||||||
|
|
||||||
describe('Langchain Integration', () => {
|
describe('Langchain Integration', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import workflow from '../fixtures/Manual_wait_set.json';
|
import workflow from '../fixtures/Manual_wait_set.json';
|
||||||
import { importWorkflow, visitDemoPage } from '../pages/demo';
|
import { importWorkflow, visitDemoPage } from '../pages/demo';
|
||||||
import { WorkflowPage } from '../pages/workflow';
|
|
||||||
import { errorToast } from '../pages/notifications';
|
import { errorToast } from '../pages/notifications';
|
||||||
|
import { WorkflowPage } from '../pages/workflow';
|
||||||
|
|
||||||
const workflowPage = new WorkflowPage();
|
const workflowPage = new WorkflowPage();
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
import {
|
||||||
clickUseWorkflowButtonByTitle,
|
clickUseWorkflowButtonByTitle,
|
||||||
visitTemplateCollectionPage,
|
visitTemplateCollectionPage,
|
||||||
|
@ -5,11 +10,6 @@ import {
|
||||||
} from '../pages/template-collection';
|
} from '../pages/template-collection';
|
||||||
import * as templateCredentialsSetupPage from '../pages/template-credential-setup';
|
import * as templateCredentialsSetupPage from '../pages/template-credential-setup';
|
||||||
import { WorkflowPage } from '../pages/workflow';
|
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();
|
const workflowPage = new WorkflowPage();
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { WorkflowsPage } from '../pages/workflows';
|
|
||||||
import {
|
import {
|
||||||
closeVersionUpdatesPanel,
|
closeVersionUpdatesPanel,
|
||||||
getVersionCard,
|
getVersionCard,
|
||||||
getVersionUpdatesPanelOpenButton,
|
getVersionUpdatesPanelOpenButton,
|
||||||
openVersionUpdatesPanel,
|
openVersionUpdatesPanel,
|
||||||
} from '../composables/versions';
|
} from '../composables/versions';
|
||||||
|
import { WorkflowsPage } from '../pages/workflows';
|
||||||
|
|
||||||
const workflowsPage = new WorkflowsPage();
|
const workflowsPage = new WorkflowsPage();
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import * as projects from '../composables/projects';
|
||||||
import { INSTANCE_MEMBERS, MANUAL_TRIGGER_NODE_NAME, NOTION_NODE_NAME } from '../constants';
|
import { INSTANCE_MEMBERS, MANUAL_TRIGGER_NODE_NAME, NOTION_NODE_NAME } from '../constants';
|
||||||
import {
|
import {
|
||||||
WorkflowsPage,
|
WorkflowsPage,
|
||||||
|
@ -8,7 +9,6 @@ import {
|
||||||
NDV,
|
NDV,
|
||||||
MainSidebar,
|
MainSidebar,
|
||||||
} from '../pages';
|
} from '../pages';
|
||||||
import * as projects from '../composables/projects';
|
|
||||||
import { getVisibleDropdown, getVisibleModalOverlay, getVisibleSelect } from '../utils';
|
import { getVisibleDropdown, getVisibleModalOverlay, getVisibleSelect } from '../utils';
|
||||||
|
|
||||||
const workflowsPage = new WorkflowsPage();
|
const workflowsPage = new WorkflowsPage();
|
||||||
|
|
|
@ -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 { 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 nodeCreatorFeature = new NodeCreator();
|
||||||
const WorkflowPage = new WorkflowPageClass();
|
const WorkflowPage = new WorkflowPageClass();
|
||||||
|
|
|
@ -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 { 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 WorkflowsPage = new WorkflowsPageClass();
|
||||||
const WorkflowPage = new WorkflowPageClass();
|
const WorkflowPage = new WorkflowPageClass();
|
||||||
|
|
|
@ -374,3 +374,58 @@ describe('AI Assistant Credential Help', () => {
|
||||||
aiAssistant.getters.credentialEditAssistantButton().should('be.disabled');
|
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',
|
||||||
|
`<pre><code class="language-json">[
|
||||||
|
{
|
||||||
|
"headers": {
|
||||||
|
"host": "n8n.instance.address",
|
||||||
|
...
|
||||||
|
},
|
||||||
|
"params": {},
|
||||||
|
"query": {},
|
||||||
|
"body": {
|
||||||
|
"name": "Jim",
|
||||||
|
"age": 30,
|
||||||
|
"city": "New York"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
</code></pre>`,
|
||||||
|
);
|
||||||
|
aiAssistant.getters.codeSnippet().should('have.text', '{{$json.body.city}}');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -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 { MANUAL_TRIGGER_NODE_DISPLAY_NAME, NOTION_NODE_NAME } from '../constants';
|
||||||
import { NDV, WorkflowPage } from '../pages';
|
import { NDV, WorkflowPage } from '../pages';
|
||||||
import { NodeCreator } from '../pages/features/node-creator';
|
import { NodeCreator } from '../pages/features/node-creator';
|
||||||
import { clickCreateNewCredential } from '../composables/ndv';
|
|
||||||
import { setCredentialValues } from '../composables/modals/credential-modal';
|
|
||||||
|
|
||||||
const workflowPage = new WorkflowPage();
|
const workflowPage = new WorkflowPage();
|
||||||
const ndv = new NDV();
|
const ndv = new NDV();
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
|
||||||
import { NDV } from '../pages/ndv';
|
import { NDV } from '../pages/ndv';
|
||||||
import { successToast } from '../pages/notifications';
|
import { successToast } from '../pages/notifications';
|
||||||
|
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||||
|
|
||||||
const WorkflowPage = new WorkflowPageClass();
|
const WorkflowPage = new WorkflowPageClass();
|
||||||
const ndv = new NDV();
|
const ndv = new NDV();
|
||||||
|
@ -43,7 +44,9 @@ describe('Code node', () => {
|
||||||
const getParameter = () => ndv.getters.parameterInput('jsCode').should('be.visible');
|
const getParameter = () => ndv.getters.parameterInput('jsCode').should('be.visible');
|
||||||
const getEditor = () => getParameter().find('.cm-content').should('exist');
|
const getEditor = () => getParameter().find('.cm-content').should('exist');
|
||||||
|
|
||||||
getEditor().type('{selectall}').paste(`$input.itemMatching()
|
getEditor()
|
||||||
|
.type('{selectall}')
|
||||||
|
.paste(`$input.itemMatching()
|
||||||
$input.item
|
$input.item
|
||||||
$('When clicking ‘Test workflow’').item
|
$('When clicking ‘Test workflow’').item
|
||||||
$input.first(1)
|
$input.first(1)
|
||||||
|
@ -68,7 +71,9 @@ return
|
||||||
|
|
||||||
ndv.getters.parameterInput('mode').click();
|
ndv.getters.parameterInput('mode').click();
|
||||||
ndv.actions.selectOptionInParameterDropdown('mode', 'Run Once for Each Item');
|
ndv.actions.selectOptionInParameterDropdown('mode', 'Run Once for Each Item');
|
||||||
getEditor().type('{selectall}').paste(`$input.itemMatching()
|
getEditor()
|
||||||
|
.type('{selectall}')
|
||||||
|
.paste(`$input.itemMatching()
|
||||||
$input.all()
|
$input.all()
|
||||||
$input.first()
|
$input.first()
|
||||||
$input.item()
|
$input.item()
|
||||||
|
|
|
@ -5,11 +5,11 @@ import {
|
||||||
EDIT_FIELDS_SET_NODE_NAME,
|
EDIT_FIELDS_SET_NODE_NAME,
|
||||||
NOTION_NODE_NAME,
|
NOTION_NODE_NAME,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
|
import { WorkflowExecutionsTab } from '../pages';
|
||||||
|
import { errorToast, successToast } from '../pages/notifications';
|
||||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||||
import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows';
|
import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows';
|
||||||
import { getVisibleSelect } from '../utils';
|
import { getVisibleSelect } from '../utils';
|
||||||
import { WorkflowExecutionsTab } from '../pages';
|
|
||||||
import { errorToast, successToast } from '../pages/notifications';
|
|
||||||
|
|
||||||
const NEW_WORKFLOW_NAME = 'Something else';
|
const NEW_WORKFLOW_NAME = 'Something else';
|
||||||
const DUPLICATE_WORKFLOW_NAME = 'Duplicated workflow';
|
const DUPLICATE_WORKFLOW_NAME = 'Duplicated workflow';
|
||||||
|
|
28
cypress/fixtures/aiAssistant/code_snippet_response.json
Normal file
28
cypress/fixtures/aiAssistant/code_snippet_response.json
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -7,13 +7,15 @@
|
||||||
"test:e2e:ui": "scripts/run-e2e.js ui",
|
"test:e2e:ui": "scripts/run-e2e.js ui",
|
||||||
"test:e2e:dev": "scripts/run-e2e.js dev",
|
"test:e2e:dev": "scripts/run-e2e.js dev",
|
||||||
"test:e2e:all": "scripts/run-e2e.js all",
|
"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",
|
"lint": "eslint . --quiet",
|
||||||
"lintfix": "eslint . --fix",
|
"lintfix": "eslint . --fix",
|
||||||
"develop": "cd ..; pnpm dev",
|
"develop": "cd ..; pnpm dev",
|
||||||
"start": "cd ..; pnpm start"
|
"start": "cd ..; pnpm start"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@n8n/api-types": "workspace:*",
|
||||||
"@types/lodash": "catalog:",
|
"@types/lodash": "catalog:",
|
||||||
"eslint-plugin-cypress": "^3.3.0",
|
"eslint-plugin-cypress": "^3.3.0",
|
||||||
"n8n-workflow": "workspace:*"
|
"n8n-workflow": "workspace:*"
|
||||||
|
|
|
@ -37,6 +37,7 @@ export class AIAssistant extends BasePage {
|
||||||
cy.getByTestId('node-error-view-ask-assistant-button').find('button').first(),
|
cy.getByTestId('node-error-view-ask-assistant-button').find('button').first(),
|
||||||
credentialEditAssistantButton: () =>
|
credentialEditAssistantButton: () =>
|
||||||
cy.getByTestId('credentail-edit-ask-assistant-button').find('button').first(),
|
cy.getByTestId('credentail-edit-ask-assistant-button').find('button').first(),
|
||||||
|
codeSnippet: () => cy.getByTestId('assistant-code-snippet'),
|
||||||
};
|
};
|
||||||
|
|
||||||
actions = {
|
actions = {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { N8N_AUTH_COOKIE } from '../constants';
|
|
||||||
import { BasePage } from './base';
|
import { BasePage } from './base';
|
||||||
import { SigninPage } from './signin';
|
import { SigninPage } from './signin';
|
||||||
import { WorkflowsPage } from './workflows';
|
import { WorkflowsPage } from './workflows';
|
||||||
|
import { N8N_AUTH_COOKIE } from '../constants';
|
||||||
|
|
||||||
export class MfaLoginPage extends BasePage {
|
export class MfaLoginPage extends BasePage {
|
||||||
url = '/mfa';
|
url = '/mfa';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { BasePage } from '../base';
|
|
||||||
import { getVisibleSelect } from '../../utils';
|
import { getVisibleSelect } from '../../utils';
|
||||||
|
import { BasePage } from '../base';
|
||||||
|
|
||||||
export class CredentialsModal extends BasePage {
|
export class CredentialsModal extends BasePage {
|
||||||
getters = {
|
getters = {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { getVisiblePopper, getVisibleSelect } from '../utils';
|
|
||||||
import { BasePage } from './base';
|
import { BasePage } from './base';
|
||||||
|
import { getVisiblePopper, getVisibleSelect } from '../utils';
|
||||||
|
|
||||||
export class NDV extends BasePage {
|
export class NDV extends BasePage {
|
||||||
getters = {
|
getters = {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { getVisibleSelect } from '../utils';
|
|
||||||
import { BasePage } from './base';
|
import { BasePage } from './base';
|
||||||
|
import { getVisibleSelect } from '../utils';
|
||||||
|
|
||||||
export class SettingsLogStreamingPage extends BasePage {
|
export class SettingsLogStreamingPage extends BasePage {
|
||||||
url = '/settings/log-streaming';
|
url = '/settings/log-streaming';
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import generateOTPToken from 'cypress-otp';
|
import generateOTPToken from 'cypress-otp';
|
||||||
|
|
||||||
|
import { BasePage } from './base';
|
||||||
import { ChangePasswordModal } from './modals/change-password-modal';
|
import { ChangePasswordModal } from './modals/change-password-modal';
|
||||||
import { MfaSetupModal } from './modals/mfa-setup-modal';
|
import { MfaSetupModal } from './modals/mfa-setup-modal';
|
||||||
import { BasePage } from './base';
|
|
||||||
|
|
||||||
const changePasswordModal = new ChangePasswordModal();
|
const changePasswordModal = new ChangePasswordModal();
|
||||||
const mfaSetupModal = new MfaSetupModal();
|
const mfaSetupModal = new MfaSetupModal();
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { SettingsSidebar } from './sidebar/settings-sidebar';
|
import { BasePage } from './base';
|
||||||
import { MainSidebar } from './sidebar/main-sidebar';
|
import { MainSidebar } from './sidebar/main-sidebar';
|
||||||
|
import { SettingsSidebar } from './sidebar/settings-sidebar';
|
||||||
import { WorkflowPage } from './workflow';
|
import { WorkflowPage } from './workflow';
|
||||||
import { WorkflowsPage } from './workflows';
|
import { WorkflowsPage } from './workflows';
|
||||||
import { BasePage } from './base';
|
|
||||||
|
|
||||||
const workflowPage = new WorkflowPage();
|
const workflowPage = new WorkflowPage();
|
||||||
const workflowsPage = new WorkflowsPage();
|
const workflowsPage = new WorkflowsPage();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { N8N_AUTH_COOKIE } from '../constants';
|
|
||||||
import { BasePage } from './base';
|
import { BasePage } from './base';
|
||||||
import { WorkflowsPage } from './workflows';
|
import { WorkflowsPage } from './workflows';
|
||||||
|
import { N8N_AUTH_COOKIE } from '../constants';
|
||||||
|
|
||||||
export class SigninPage extends BasePage {
|
export class SigninPage extends BasePage {
|
||||||
url = '/signin';
|
url = '/signin';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as formStep from '../composables/setup-template-form-step';
|
|
||||||
import { overrideFeatureFlag } from '../composables/featureFlags';
|
|
||||||
import { CredentialsModal, MessageBox } from './modals';
|
import { CredentialsModal, MessageBox } from './modals';
|
||||||
|
import { overrideFeatureFlag } from '../composables/featureFlags';
|
||||||
|
import * as formStep from '../composables/setup-template-form-step';
|
||||||
|
|
||||||
const credentialsModal = new CredentialsModal();
|
const credentialsModal = new CredentialsModal();
|
||||||
const messageBox = new MessageBox();
|
const messageBox = new MessageBox();
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { BasePage } from './base';
|
import { BasePage } from './base';
|
||||||
|
|
||||||
import Chainable = Cypress.Chainable;
|
import Chainable = Cypress.Chainable;
|
||||||
|
|
||||||
export class VariablesPage extends BasePage {
|
export class VariablesPage extends BasePage {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
import { BasePage } from './base';
|
||||||
|
import { NodeCreator } from './features/node-creator';
|
||||||
import { META_KEY } from '../constants';
|
import { META_KEY } from '../constants';
|
||||||
import { getVisibleSelect } from '../utils';
|
import { getVisibleSelect } from '../utils';
|
||||||
import { getUniqueWorkflowName } from '../utils/workflowUtils';
|
import { getUniqueWorkflowName } from '../utils/workflowUtils';
|
||||||
import { BasePage } from './base';
|
|
||||||
import { NodeCreator } from './features/node-creator';
|
|
||||||
|
|
||||||
const nodeCreator = new NodeCreator();
|
const nodeCreator = new NodeCreator();
|
||||||
export class WorkflowPage extends BasePage {
|
export class WorkflowPage extends BasePage {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'cypress-real-events';
|
import 'cypress-real-events';
|
||||||
|
import type { FrontendSettings } from '@n8n/api-types';
|
||||||
import FakeTimers from '@sinonjs/fake-timers';
|
import FakeTimers from '@sinonjs/fake-timers';
|
||||||
import type { IN8nUISettings } from 'n8n-workflow';
|
|
||||||
import { WorkflowPage } from '../pages';
|
|
||||||
import {
|
import {
|
||||||
BACKEND_BASE_URL,
|
BACKEND_BASE_URL,
|
||||||
INSTANCE_ADMIN,
|
INSTANCE_ADMIN,
|
||||||
|
@ -9,6 +9,7 @@ import {
|
||||||
INSTANCE_OWNER,
|
INSTANCE_OWNER,
|
||||||
N8N_AUTH_COOKIE,
|
N8N_AUTH_COOKIE,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
|
import { WorkflowPage } from '../pages';
|
||||||
import { getUniqueWorkflowName } from '../utils/workflowUtils';
|
import { getUniqueWorkflowName } from '../utils/workflowUtils';
|
||||||
|
|
||||||
Cypress.Commands.add('setAppDate', (targetDate: number | Date) => {
|
Cypress.Commands.add('setAppDate', (targetDate: number | Date) => {
|
||||||
|
@ -86,8 +87,8 @@ Cypress.Commands.add('signout', () => {
|
||||||
cy.getCookie(N8N_AUTH_COOKIE).should('not.exist');
|
cy.getCookie(N8N_AUTH_COOKIE).should('not.exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
export let settings: Partial<IN8nUISettings>;
|
export let settings: Partial<FrontendSettings>;
|
||||||
Cypress.Commands.add('overrideSettings', (value: Partial<IN8nUISettings>) => {
|
Cypress.Commands.add('overrideSettings', (value: Partial<FrontendSettings>) => {
|
||||||
settings = value;
|
settings = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
import merge from 'lodash/merge';
|
import merge from 'lodash/merge';
|
||||||
|
|
||||||
import { settings } from './commands';
|
import { settings } from './commands';
|
||||||
|
|
||||||
before(() => {
|
before(() => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Load type definitions that come with Cypress module
|
// Load type definitions that come with Cypress module
|
||||||
/// <reference types="cypress" />
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
import type { IN8nUISettings } from 'n8n-workflow';
|
import type { FrontendSettings } from '@n8n/api-types';
|
||||||
|
|
||||||
Cypress.Keyboard.defaults({
|
Cypress.Keyboard.defaults({
|
||||||
keystrokeDelay: 0,
|
keystrokeDelay: 0,
|
||||||
|
@ -45,7 +45,7 @@ declare global {
|
||||||
*/
|
*/
|
||||||
signinAsMember(index?: number): void;
|
signinAsMember(index?: number): void;
|
||||||
signout(): void;
|
signout(): void;
|
||||||
overrideSettings(value: Partial<IN8nUISettings>): void;
|
overrideSettings(value: Partial<FrontendSettings>): void;
|
||||||
enableFeature(feature: string): void;
|
enableFeature(feature: string): void;
|
||||||
disableFeature(feature: string): void;
|
disableFeature(feature: string): void;
|
||||||
enableQueueMode(): void;
|
enableQueueMode(): void;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { nanoid } from 'nanoid';
|
|
||||||
import type { IDataObject, IPinData, ITaskData, ITaskDataConnections } from 'n8n-workflow';
|
import type { IDataObject, IPinData, ITaskData, ITaskDataConnections } from 'n8n-workflow';
|
||||||
|
import { nanoid } from 'nanoid';
|
||||||
|
|
||||||
import { clickExecuteWorkflowButton } from '../composables/workflow';
|
import { clickExecuteWorkflowButton } from '../composables/workflow';
|
||||||
|
|
||||||
export function createMockNodeExecutionData(
|
export function createMockNodeExecutionData(
|
||||||
|
|
|
@ -6,7 +6,7 @@ FROM --platform=linux/amd64 n8nio/base:${NODE_VERSION} AS builder
|
||||||
# Build the application from source
|
# Build the application from source
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY . /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
|
RUN pnpm build
|
||||||
|
|
||||||
# Delete all dev dependencies
|
# Delete all dev dependencies
|
||||||
|
@ -18,7 +18,7 @@ RUN find . -type f -name "*.ts" -o -name "*.js.map" -o -name "*.vue" -o -name "t
|
||||||
|
|
||||||
# Deploy the `n8n` package into /compiled
|
# Deploy the `n8n` package into /compiled
|
||||||
RUN mkdir /compiled
|
RUN mkdir /compiled
|
||||||
RUN NODE_ENV=production pnpm --filter=n8n --prod --no-optional deploy /compiled
|
RUN NODE_ENV=production DOCKER_BUILD=true pnpm --filter=n8n --prod --no-optional deploy /compiled
|
||||||
|
|
||||||
# 2. Start with a new clean image with just the code that is needed to run n8n
|
# 2. Start with a new clean image with just the code that is needed to run n8n
|
||||||
FROM n8nio/base:${NODE_VERSION}
|
FROM n8nio/base:${NODE_VERSION}
|
||||||
|
|
10
lefthook.yml
Normal file
10
lefthook.yml
Normal file
|
@ -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
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"folders": [
|
"folders": [
|
||||||
{
|
{
|
||||||
"path": ".",
|
"path": "."
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.6.0",
|
"packageManager": "pnpm@9.6.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"prepare": "node scripts/prepare.mjs",
|
||||||
"preinstall": "node scripts/block-npm-install.js",
|
"preinstall": "node scripts/block-npm-install.js",
|
||||||
"build": "turbo run build",
|
"build": "turbo run build",
|
||||||
"build:backend": "turbo run build:backend",
|
"build:backend": "turbo run build:backend",
|
||||||
|
@ -19,6 +20,7 @@
|
||||||
"clean": "turbo run clean --parallel",
|
"clean": "turbo run clean --parallel",
|
||||||
"reset": "node scripts/ensure-zx.mjs && zx scripts/reset.mjs",
|
"reset": "node scripts/ensure-zx.mjs && zx scripts/reset.mjs",
|
||||||
"format": "turbo run format && node scripts/format.mjs",
|
"format": "turbo run format && node scripts/format.mjs",
|
||||||
|
"format:check": "turbo run format:check",
|
||||||
"lint": "turbo run lint",
|
"lint": "turbo run lint",
|
||||||
"lintfix": "turbo run lintfix",
|
"lintfix": "turbo run lintfix",
|
||||||
"lint:backend": "turbo run lint:backend",
|
"lint:backend": "turbo run lint:backend",
|
||||||
|
@ -38,6 +40,7 @@
|
||||||
"worker": "./packages/cli/bin/n8n worker"
|
"worker": "./packages/cli/bin/n8n worker"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "^1.9.0",
|
||||||
"@n8n_io/eslint-config": "workspace:*",
|
"@n8n_io/eslint-config": "workspace:*",
|
||||||
"@types/jest": "^29.5.3",
|
"@types/jest": "^29.5.3",
|
||||||
"@types/supertest": "^6.0.2",
|
"@types/supertest": "^6.0.2",
|
||||||
|
@ -46,6 +49,7 @@
|
||||||
"jest-expect-message": "^1.1.3",
|
"jest-expect-message": "^1.1.3",
|
||||||
"jest-mock": "^29.6.2",
|
"jest-mock": "^29.6.2",
|
||||||
"jest-mock-extended": "^3.0.4",
|
"jest-mock-extended": "^3.0.4",
|
||||||
|
"lefthook": "^1.7.15",
|
||||||
"nock": "^13.3.2",
|
"nock": "^13.3.2",
|
||||||
"nodemon": "^3.0.1",
|
"nodemon": "^3.0.1",
|
||||||
"p-limit": "^3.1.0",
|
"p-limit": "^3.1.0",
|
||||||
|
@ -73,7 +77,7 @@
|
||||||
"semver": "^7.5.4",
|
"semver": "^7.5.4",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.6.2",
|
||||||
"tsconfig-paths": "^4.2.0",
|
"tsconfig-paths": "^4.2.0",
|
||||||
"typescript": "^5.5.2",
|
"typescript": "^5.6.2",
|
||||||
"ws": ">=8.17.1"
|
"ws": ">=8.17.1"
|
||||||
},
|
},
|
||||||
"patchedDependencies": {
|
"patchedDependencies": {
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
"dev": "pnpm watch",
|
"dev": "pnpm watch",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"build": "tsc -p tsconfig.build.json",
|
"build": "tsc -p tsconfig.build.json",
|
||||||
"format": "prettier --write . --ignore-path ../../../.prettierignore",
|
"format": "biome format --write .",
|
||||||
|
"format:check": "biome ci .",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"lintfix": "eslint . --fix",
|
"lintfix": "eslint . --fix",
|
||||||
"watch": "tsc -p tsconfig.build.json --watch",
|
"watch": "tsc -p tsconfig.build.json --watch",
|
||||||
|
|
172
packages/@n8n/api-types/src/frontend-settings.ts
Normal file
172
packages/@n8n/api-types/src/frontend-settings.ts
Normal file
|
@ -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;
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
|
export type * from './datetime';
|
||||||
export type * from './push';
|
export type * from './push';
|
||||||
export type * from './scaling';
|
export type * from './scaling';
|
||||||
export type * from './datetime';
|
export type * from './frontend-settings';
|
||||||
export type * from './user';
|
export type * from './user';
|
||||||
|
|
||||||
export type { Collaborator } from './push/collaboration';
|
export type { Collaborator } from './push/collaboration';
|
||||||
|
|
|
@ -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 { CollaborationPushMessage } from './collaboration';
|
||||||
import type { DebugPushMessage } from './debug';
|
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 =
|
export type PushMessage =
|
||||||
| ExecutionPushMessage
|
| ExecutionPushMessage
|
||||||
|
|
|
@ -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 ./patches /app/patches
|
||||||
COPY --chown=node:node ./scripts /app/scripts
|
COPY --chown=node:node ./scripts /app/scripts
|
||||||
|
|
||||||
|
ENV DOCKER_BUILD=true
|
||||||
RUN pnpm install --frozen-lockfile
|
RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
# TS config files
|
# TS config files
|
||||||
|
|
7
packages/@n8n/benchmark/biome.jsonc
Normal file
7
packages/@n8n/benchmark/biome.jsonc
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"$schema": "../node_modules/@biomejs/biome/configuration_schema.json",
|
||||||
|
"extends": ["../../../biome.jsonc"],
|
||||||
|
"files": {
|
||||||
|
"ignore": ["scripts/mock-api/**"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,8 @@
|
||||||
"main": "dist/index",
|
"main": "dist/index",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
||||||
|
"format": "biome format --write .",
|
||||||
|
"format:check": "biome ci .",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"lintfix": "eslint . --fix",
|
"lintfix": "eslint . --fix",
|
||||||
"start": "./bin/n8n-benchmark",
|
"start": "./bin/n8n-benchmark",
|
||||||
|
|
|
@ -37,6 +37,17 @@ else
|
||||||
sudo chown -R "$CURRENT_USER":"$CURRENT_USER" /n8n
|
sudo chown -R "$CURRENT_USER":"$CURRENT_USER" /n8n
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
### Remove unneeded dependencies
|
||||||
|
# TTY
|
||||||
|
sudo systemctl disable getty@tty1.service
|
||||||
|
sudo systemctl disable serial-getty@ttyS0.service
|
||||||
|
# Snap
|
||||||
|
sudo systemctl disable snapd.service
|
||||||
|
# Unattended upgrades
|
||||||
|
sudo systemctl disable unattended-upgrades.service
|
||||||
|
# Cron
|
||||||
|
sudo systemctl disable cron.service
|
||||||
|
|
||||||
# Include nodejs v20 repository
|
# Include nodejs v20 repository
|
||||||
curl -fsSL https://deb.nodesource.com/setup_20.x -o nodesource_setup.sh
|
curl -fsSL https://deb.nodesource.com/setup_20.x -o nodesource_setup.sh
|
||||||
sudo -E bash nodesource_setup.sh
|
sudo -E bash nodesource_setup.sh
|
||||||
|
|
|
@ -78,6 +78,12 @@ async function runBenchmarksOnVm(config, benchmarkEnv) {
|
||||||
const bootstrapScriptPath = path.join(scriptsDir, 'bootstrap.sh');
|
const bootstrapScriptPath = path.join(scriptsDir, 'bootstrap.sh');
|
||||||
await sshClient.ssh(`chmod a+x ${bootstrapScriptPath} && ${bootstrapScriptPath}`);
|
await sshClient.ssh(`chmod a+x ${bootstrapScriptPath} && ${bootstrapScriptPath}`);
|
||||||
|
|
||||||
|
// Benchmarking the VM
|
||||||
|
const vmBenchmarkScriptPath = path.join(scriptsDir, 'vm-benchmark.sh');
|
||||||
|
await sshClient.ssh(`chmod a+x ${vmBenchmarkScriptPath} && ${vmBenchmarkScriptPath}`, {
|
||||||
|
verbose: true,
|
||||||
|
});
|
||||||
|
|
||||||
// Give some time for the VM to be ready
|
// Give some time for the VM to be ready
|
||||||
await sleep(1000);
|
await sleep(1000);
|
||||||
|
|
||||||
|
|
13
packages/@n8n/benchmark/scripts/vm-benchmark.sh
Normal file
13
packages/@n8n/benchmark/scripts/vm-benchmark.sh
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Install fio
|
||||||
|
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
|
||||||
|
|
||||||
|
# Remove files
|
||||||
|
sudo rm /n8n/rand_rw.*
|
||||||
|
|
||||||
|
# Uninstall fio
|
||||||
|
DEBIAN_FRONTEND=noninteractive sudo apt-get -y remove fio > /dev/null
|
|
@ -1,6 +1,7 @@
|
||||||
import { Command } from '@oclif/core';
|
import { Command } from '@oclif/core';
|
||||||
import { ScenarioLoader } from '@/scenario/scenario-loader';
|
|
||||||
import { testScenariosPath } from '@/config/common-flags';
|
import { testScenariosPath } from '@/config/common-flags';
|
||||||
|
import { ScenarioLoader } from '@/scenario/scenario-loader';
|
||||||
|
|
||||||
export default class ListCommand extends Command {
|
export default class ListCommand extends Command {
|
||||||
static description = 'List all available scenarios';
|
static description = 'List all available scenarios';
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { Command, Flags } from '@oclif/core';
|
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 { N8nApiClient } from '@/n8n-api-client/n8n-api-client';
|
||||||
import { ScenarioDataFileLoader } from '@/scenario/scenario-data-loader';
|
import { ScenarioDataFileLoader } from '@/scenario/scenario-data-loader';
|
||||||
|
import { ScenarioLoader } from '@/scenario/scenario-loader';
|
||||||
import type { K6Tag } from '@/test-execution/k6-executor';
|
import type { K6Tag } from '@/test-execution/k6-executor';
|
||||||
import { K6Executor } 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 {
|
export default class RunCommand extends Command {
|
||||||
static description = 'Run all (default) or specified test scenarios';
|
static description = 'Run all (default) or specified test scenarios';
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import type { AxiosRequestConfig } from 'axios';
|
import type { AxiosRequestConfig } from 'axios';
|
||||||
|
|
||||||
import { N8nApiClient } from './n8n-api-client';
|
import { N8nApiClient } from './n8n-api-client';
|
||||||
|
|
||||||
export class AuthenticatedN8nApiClient extends N8nApiClient {
|
export class AuthenticatedN8nApiClient extends N8nApiClient {
|
||||||
|
|
|
@ -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 { Workflow } from '@/n8n-api-client/n8n-api-client.types';
|
||||||
|
|
||||||
|
import type { AuthenticatedN8nApiClient } from './authenticated-n8n-api-client';
|
||||||
|
|
||||||
export class WorkflowApiClient {
|
export class WorkflowApiClient {
|
||||||
constructor(private readonly apiClient: AuthenticatedN8nApiClient) {}
|
constructor(private readonly apiClient: AuthenticatedN8nApiClient) {}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import * as fs from 'node:fs';
|
import * as fs from 'node:fs';
|
||||||
import * as path from 'node:path';
|
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 { Workflow } from '@/n8n-api-client/n8n-api-client.types';
|
||||||
|
import type { Scenario } from '@/types/scenario';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads scenario data files from FS
|
* Loads scenario data files from FS
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
import { createHash } from 'node:crypto';
|
||||||
import * as fs from 'node:fs';
|
import * as fs from 'node:fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { createHash } from 'node:crypto';
|
|
||||||
import type { Scenario, ScenarioManifest } from '@/types/scenario';
|
import type { Scenario, ScenarioManifest } from '@/types/scenario';
|
||||||
|
|
||||||
export class ScenarioLoader {
|
export class ScenarioLoader {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
|
import path from 'path';
|
||||||
import { $, which, tmpfile } from 'zx';
|
import { $, which, tmpfile } from 'zx';
|
||||||
import type { Scenario } from '@/types/scenario';
|
|
||||||
import { buildTestReport, type K6Tag } from '@/test-execution/test-report';
|
import { buildTestReport, type K6Tag } from '@/test-execution/test-report';
|
||||||
|
import type { Scenario } from '@/types/scenario';
|
||||||
export type { K6Tag };
|
export type { K6Tag };
|
||||||
|
|
||||||
export type K6ExecutorOpts = {
|
export type K6ExecutorOpts = {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import type { K6Executor } from './k6-executor';
|
import { AuthenticatedN8nApiClient } from '@/n8n-api-client/authenticated-n8n-api-client';
|
||||||
import type { Scenario } from '@/types/scenario';
|
|
||||||
import type { N8nApiClient } from '@/n8n-api-client/n8n-api-client';
|
import type { N8nApiClient } from '@/n8n-api-client/n8n-api-client';
|
||||||
import type { ScenarioDataFileLoader } from '@/scenario/scenario-data-loader';
|
import type { ScenarioDataFileLoader } from '@/scenario/scenario-data-loader';
|
||||||
import { ScenarioDataImporter } from '@/test-execution/scenario-data-importer';
|
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
|
* Runs scenarios
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
|
|
||||||
import type { Scenario } from '@/types/scenario';
|
import type { Scenario } from '@/types/scenario';
|
||||||
|
|
||||||
export type K6Tag = {
|
export type K6Tag = {
|
||||||
|
|
|
@ -12,7 +12,8 @@
|
||||||
"typecheck": "vue-tsc --noEmit",
|
"typecheck": "vue-tsc --noEmit",
|
||||||
"lint": "eslint . --ext .js,.ts,.vue --quiet",
|
"lint": "eslint . --ext .js,.ts,.vue --quiet",
|
||||||
"lintfix": "eslint . --ext .js,.ts,.vue --fix",
|
"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",
|
"storybook": "storybook dev -p 6006 --no-open",
|
||||||
"build:storybook": "storybook build"
|
"build:storybook": "storybook build"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted } from 'vue';
|
|
||||||
import hljs from 'highlight.js/lib/core';
|
import hljs from 'highlight.js/lib/core';
|
||||||
import hljsXML from 'highlight.js/lib/languages/xml';
|
|
||||||
import hljsJavascript from 'highlight.js/lib/languages/javascript';
|
import hljsJavascript from 'highlight.js/lib/languages/javascript';
|
||||||
|
import hljsXML from 'highlight.js/lib/languages/xml';
|
||||||
|
import { computed, onMounted } from 'vue';
|
||||||
|
|
||||||
import { Chat, ChatWindow } from '@n8n/chat/components';
|
import { Chat, ChatWindow } from '@n8n/chat/components';
|
||||||
import { useOptions } from '@n8n/chat/composables';
|
import { useOptions } from '@n8n/chat/composables';
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
import type { StoryObj } from '@storybook/vue3';
|
import type { StoryObj } from '@storybook/vue3';
|
||||||
import { onMounted } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
import type { ChatOptions } from '@n8n/chat/types';
|
|
||||||
import { createChat } from '@n8n/chat/index';
|
import { createChat } from '@n8n/chat/index';
|
||||||
|
import type { ChatOptions } from '@n8n/chat/types';
|
||||||
|
|
||||||
const webhookUrl = 'http://localhost:5678/webhook/f406671e-c954-4691-b39a-66c90aa2f103/chat';
|
const webhookUrl = 'http://localhost:5678/webhook/f406671e-c954-4691-b39a-66c90aa2f103/chat';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { fireEvent, waitFor } from '@testing-library/vue';
|
import { fireEvent, waitFor } from '@testing-library/vue';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createFetchResponse,
|
createFetchResponse,
|
||||||
createGetLatestMessagesResponse,
|
createGetLatestMessagesResponse,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { screen } from '@testing-library/vue';
|
import { screen } from '@testing-library/vue';
|
||||||
|
|
||||||
import { defaultMountingTarget } from '@n8n/chat/constants';
|
import { defaultMountingTarget } from '@n8n/chat/constants';
|
||||||
|
|
||||||
export function getMountingTarget(target = defaultMountingTarget) {
|
export function getMountingTarget(target = defaultMountingTarget) {
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Close from 'virtual:icons/mdi/close';
|
import Close from 'virtual:icons/mdi/close';
|
||||||
import { computed, nextTick, onMounted } from 'vue';
|
import { computed, nextTick, onMounted } from 'vue';
|
||||||
import Layout from '@n8n/chat/components/Layout.vue';
|
|
||||||
import GetStarted from '@n8n/chat/components/GetStarted.vue';
|
import GetStarted from '@n8n/chat/components/GetStarted.vue';
|
||||||
import GetStartedFooter from '@n8n/chat/components/GetStartedFooter.vue';
|
import GetStartedFooter from '@n8n/chat/components/GetStartedFooter.vue';
|
||||||
import MessagesList from '@n8n/chat/components/MessagesList.vue';
|
|
||||||
import Input from '@n8n/chat/components/Input.vue';
|
import Input from '@n8n/chat/components/Input.vue';
|
||||||
|
import Layout from '@n8n/chat/components/Layout.vue';
|
||||||
|
import MessagesList from '@n8n/chat/components/MessagesList.vue';
|
||||||
import { useI18n, useChat, useOptions } from '@n8n/chat/composables';
|
import { useI18n, useChat, useOptions } from '@n8n/chat/composables';
|
||||||
import { chatEventBus } from '@n8n/chat/event-buses';
|
import { chatEventBus } from '@n8n/chat/event-buses';
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import IconFileText from 'virtual:icons/mdi/fileText';
|
|
||||||
import IconFileMusic from 'virtual:icons/mdi/fileMusic';
|
|
||||||
import IconFileImage from 'virtual:icons/mdi/fileImage';
|
|
||||||
import IconFileVideo from 'virtual:icons/mdi/fileVideo';
|
|
||||||
import IconDelete from 'virtual:icons/mdi/closeThick';
|
import IconDelete from 'virtual:icons/mdi/closeThick';
|
||||||
|
import IconFileImage from 'virtual:icons/mdi/fileImage';
|
||||||
|
import IconFileMusic from 'virtual:icons/mdi/fileMusic';
|
||||||
|
import IconFileText from 'virtual:icons/mdi/fileText';
|
||||||
|
import IconFileVideo from 'virtual:icons/mdi/fileVideo';
|
||||||
import IconPreview from 'virtual:icons/mdi/openInNew';
|
import IconPreview from 'virtual:icons/mdi/openInNew';
|
||||||
|
|
||||||
import { computed, type FunctionalComponent } from 'vue';
|
import { computed, type FunctionalComponent } from 'vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import IconChat from 'virtual:icons/mdi/chat';
|
import IconChat from 'virtual:icons/mdi/chat';
|
||||||
import IconChevronDown from 'virtual:icons/mdi/chevron-down';
|
import IconChevronDown from 'virtual:icons/mdi/chevron-down';
|
||||||
import { nextTick, ref } from 'vue';
|
import { nextTick, ref } from 'vue';
|
||||||
|
|
||||||
import Chat from '@n8n/chat/components/Chat.vue';
|
import Chat from '@n8n/chat/components/Chat.vue';
|
||||||
import { chatEventBus } from '@n8n/chat/event-buses';
|
import { chatEventBus } from '@n8n/chat/event-buses';
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from '@n8n/chat/composables';
|
|
||||||
import PoweredBy from '@n8n/chat/components/PoweredBy.vue';
|
import PoweredBy from '@n8n/chat/components/PoweredBy.vue';
|
||||||
|
import { useI18n } from '@n8n/chat/composables';
|
||||||
|
|
||||||
const { t, te } = useI18n();
|
const { t, te } = useI18n();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import IconSend from 'virtual:icons/mdi/send';
|
|
||||||
import IconFilePlus from 'virtual:icons/mdi/filePlus';
|
|
||||||
import { computed, onMounted, onUnmounted, ref, unref } from 'vue';
|
|
||||||
import { useFileDialog } from '@vueuse/core';
|
import { useFileDialog } from '@vueuse/core';
|
||||||
import ChatFile from './ChatFile.vue';
|
import IconFilePlus from 'virtual:icons/mdi/filePlus';
|
||||||
|
import IconSend from 'virtual:icons/mdi/send';
|
||||||
|
import { computed, onMounted, onUnmounted, ref, unref } from 'vue';
|
||||||
|
|
||||||
import { useI18n, useChat, useOptions } from '@n8n/chat/composables';
|
import { useI18n, useChat, useOptions } from '@n8n/chat/composables';
|
||||||
import { chatEventBus } from '@n8n/chat/event-buses';
|
import { chatEventBus } from '@n8n/chat/event-buses';
|
||||||
|
|
||||||
|
import ChatFile from './ChatFile.vue';
|
||||||
|
|
||||||
export interface ArrowKeyDownPayload {
|
export interface ArrowKeyDownPayload {
|
||||||
key: 'ArrowUp' | 'ArrowDown';
|
key: 'ArrowUp' | 'ArrowDown';
|
||||||
currentInputValue: string;
|
currentInputValue: string;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||||
|
|
||||||
import { chatEventBus } from '@n8n/chat/event-buses';
|
import { chatEventBus } from '@n8n/chat/event-buses';
|
||||||
|
|
||||||
const chatBodyRef = ref<HTMLElement | null>(null);
|
const chatBodyRef = ref<HTMLElement | null>(null);
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
|
import hljs from 'highlight.js/lib/core';
|
||||||
|
import bash from 'highlight.js/lib/languages/bash';
|
||||||
|
import javascript from 'highlight.js/lib/languages/javascript';
|
||||||
|
import python from 'highlight.js/lib/languages/python';
|
||||||
|
import typescript from 'highlight.js/lib/languages/typescript';
|
||||||
|
import xml from 'highlight.js/lib/languages/xml';
|
||||||
|
import type MarkdownIt from 'markdown-it';
|
||||||
|
import markdownLink from 'markdown-it-link-attributes';
|
||||||
import { computed, ref, toRefs, onMounted } from 'vue';
|
import { computed, ref, toRefs, onMounted } from 'vue';
|
||||||
import VueMarkdown from 'vue-markdown-render';
|
import VueMarkdown from 'vue-markdown-render';
|
||||||
import hljs from 'highlight.js/lib/core';
|
|
||||||
import javascript from 'highlight.js/lib/languages/javascript';
|
|
||||||
import typescript from 'highlight.js/lib/languages/typescript';
|
|
||||||
import python from 'highlight.js/lib/languages/python';
|
|
||||||
import xml from 'highlight.js/lib/languages/xml';
|
|
||||||
import bash from 'highlight.js/lib/languages/bash';
|
|
||||||
import markdownLink from 'markdown-it-link-attributes';
|
|
||||||
import type MarkdownIt from 'markdown-it';
|
|
||||||
import ChatFile from './ChatFile.vue';
|
|
||||||
import type { ChatMessage, ChatMessageText } from '@n8n/chat/types';
|
|
||||||
import { useOptions } from '@n8n/chat/composables';
|
import { useOptions } from '@n8n/chat/composables';
|
||||||
|
import type { ChatMessage, ChatMessageText } from '@n8n/chat/types';
|
||||||
|
|
||||||
|
import ChatFile from './ChatFile.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
message: ChatMessage;
|
message: ChatMessage;
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import { Message } from './index';
|
|
||||||
import type { ChatMessage } from '@n8n/chat/types';
|
import type { ChatMessage } from '@n8n/chat/types';
|
||||||
|
|
||||||
|
import { Message } from './index';
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
animation?: 'bouncing' | 'scaling';
|
animation?: 'bouncing' | 'scaling';
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
|
|
||||||
import Message from '@n8n/chat/components/Message.vue';
|
import Message from '@n8n/chat/components/Message.vue';
|
||||||
import type { ChatMessage } from '@n8n/chat/types';
|
|
||||||
import MessageTyping from '@n8n/chat/components/MessageTyping.vue';
|
import MessageTyping from '@n8n/chat/components/MessageTyping.vue';
|
||||||
import { useChat } from '@n8n/chat/composables';
|
import { useChat } from '@n8n/chat/composables';
|
||||||
|
import type { ChatMessage } from '@n8n/chat/types';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
messages: ChatMessage[];
|
messages: ChatMessage[];
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { inject } from 'vue';
|
import { inject } from 'vue';
|
||||||
|
|
||||||
import { ChatSymbol } from '@n8n/chat/constants';
|
import { ChatSymbol } from '@n8n/chat/constants';
|
||||||
import type { Chat } from '@n8n/chat/types';
|
import type { Chat } from '@n8n/chat/types';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { isRef } from 'vue';
|
import { isRef } from 'vue';
|
||||||
|
|
||||||
import { useOptions } from '@n8n/chat/composables/useOptions';
|
import { useOptions } from '@n8n/chat/composables/useOptions';
|
||||||
|
|
||||||
export function useI18n() {
|
export function useI18n() {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { inject } from 'vue';
|
import { inject } from 'vue';
|
||||||
|
|
||||||
import { ChatOptionsSymbol } from '@n8n/chat/constants';
|
import { ChatOptionsSymbol } from '@n8n/chat/constants';
|
||||||
import type { ChatOptions } from '@n8n/chat/types';
|
import type { ChatOptions } from '@n8n/chat/types';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import type { InjectionKey } from 'vue';
|
import type { InjectionKey } from 'vue';
|
||||||
|
|
||||||
import type { Chat, ChatOptions } from '@n8n/chat/types';
|
import type { Chat, ChatOptions } from '@n8n/chat/types';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
|
|
@ -45,8 +45,6 @@
|
||||||
vertical-align: inherit; /* 2 */
|
vertical-align: inherit; /* 2 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Remove inconsistent and unnecessary margins
|
Remove inconsistent and unnecessary margins
|
||||||
*/
|
*/
|
||||||
|
@ -63,7 +61,8 @@
|
||||||
button, /* (Safari) 3 lines */
|
button, /* (Safari) 3 lines */
|
||||||
input,
|
input,
|
||||||
select,
|
select,
|
||||||
textarea { /* (Firefox, Safari) */
|
textarea {
|
||||||
|
/* (Firefox, Safari) */
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,11 +79,13 @@
|
||||||
Add correct display
|
Add correct display
|
||||||
*/
|
*/
|
||||||
main, /* (IE11) */
|
main, /* (IE11) */
|
||||||
details { /* (Edge 18-, IE) */
|
details {
|
||||||
|
/* (Edge 18-, IE) */
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
summary { /* (all) */
|
summary {
|
||||||
|
/* (all) */
|
||||||
display: list-item;
|
display: list-item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,18 +107,19 @@
|
||||||
kbd,
|
kbd,
|
||||||
samp {
|
samp {
|
||||||
font-family:
|
font-family:
|
||||||
/* macOS 10.10+ */ "Menlo",
|
/* macOS 10.10+ */
|
||||||
/* Windows 6+ */ "Consolas",
|
'Menlo',
|
||||||
/* Android 4+ */ "Roboto Mono",
|
/* Windows 6+ */ 'Consolas',
|
||||||
/* Ubuntu 10.10+ */ "Ubuntu Monospace",
|
/* Android 4+ */ 'Roboto Mono',
|
||||||
/* KDE Plasma 5+ */ "Noto Mono",
|
/* Ubuntu 10.10+ */ 'Ubuntu Monospace',
|
||||||
/* KDE Plasma 4+ */ "Oxygen Mono",
|
/* KDE Plasma 5+ */ 'Noto Mono',
|
||||||
/* Linux/OpenOffice fallback */ "Liberation Mono",
|
/* KDE Plasma 4+ */ 'Oxygen Mono',
|
||||||
|
/* Linux/OpenOffice fallback */ 'Liberation Mono',
|
||||||
/* fallback */ monospace,
|
/* fallback */ monospace,
|
||||||
/* macOS emoji */ "Apple Color Emoji",
|
/* macOS emoji */ 'Apple Color Emoji',
|
||||||
/* Windows emoji */ "Segoe UI Emoji",
|
/* Windows emoji */ 'Segoe UI Emoji',
|
||||||
/* Windows emoji */ "Segoe UI Symbol",
|
/* Windows emoji */ 'Segoe UI Symbol',
|
||||||
/* Linux emoji */ "Noto Color Emoji"; /* 1 */
|
/* Linux emoji */ 'Noto Color Emoji'; /* 1 */
|
||||||
|
|
||||||
font-size: 1em; /* 2 */
|
font-size: 1em; /* 2 */
|
||||||
}
|
}
|
||||||
|
@ -201,9 +203,9 @@
|
||||||
Correct inability to style buttons (iOS, Safari)
|
Correct inability to style buttons (iOS, Safari)
|
||||||
*/
|
*/
|
||||||
button,
|
button,
|
||||||
[type="button"],
|
[type='button'],
|
||||||
[type="reset"],
|
[type='reset'],
|
||||||
[type="submit"] {
|
[type='submit'] {
|
||||||
-webkit-appearance: button;
|
-webkit-appearance: button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,7 +251,7 @@
|
||||||
1. Correct outline style (Safari)
|
1. Correct outline style (Safari)
|
||||||
2. Correct odd appearance (Chrome, Edge, Safari)
|
2. Correct odd appearance (Chrome, Edge, Safari)
|
||||||
*/
|
*/
|
||||||
[type="search"] {
|
[type='search'] {
|
||||||
outline-offset: -2px; /* 1 */
|
outline-offset: -2px; /* 1 */
|
||||||
-webkit-appearance: textfield; /* 2 */
|
-webkit-appearance: textfield; /* 2 */
|
||||||
}
|
}
|
||||||
|
@ -311,7 +313,7 @@
|
||||||
/*
|
/*
|
||||||
Change cursor on busy elements (all)
|
Change cursor on busy elements (all)
|
||||||
*/
|
*/
|
||||||
[aria-busy="true"] {
|
[aria-busy='true'] {
|
||||||
cursor: progress;
|
cursor: progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,7 +327,7 @@
|
||||||
/*
|
/*
|
||||||
Change cursor on disabled, non-editable, or inoperable elements (all)
|
Change cursor on disabled, non-editable, or inoperable elements (all)
|
||||||
*/
|
*/
|
||||||
[aria-disabled="true"],
|
[aria-disabled='true'],
|
||||||
[disabled] {
|
[disabled] {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
@ -333,12 +335,12 @@
|
||||||
/*
|
/*
|
||||||
Change display on visually hidden accessible elements (all)
|
Change display on visually hidden accessible elements (all)
|
||||||
*/
|
*/
|
||||||
[aria-hidden="false"][hidden] {
|
[aria-hidden='false'][hidden] {
|
||||||
display: inline;
|
display: inline;
|
||||||
display: initial;
|
display: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
[aria-hidden="false"][hidden]:not(:focus) {
|
[aria-hidden='false'][hidden]:not(:focus) {
|
||||||
clip: rect(0, 0, 0, 0);
|
clip: rect(0, 0, 0, 0);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
@ -347,8 +349,8 @@
|
||||||
Print out URLs after links (all)
|
Print out URLs after links (all)
|
||||||
*/
|
*/
|
||||||
@media print {
|
@media print {
|
||||||
a[href^="http"]::after {
|
a[href^='http']::after {
|
||||||
content: " (" attr(href) ")";
|
content: ' (' attr(href) ')';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* ----- Variables ----- */
|
/* ----- Variables ----- */
|
||||||
|
@ -586,7 +588,7 @@
|
||||||
/* ----- Forms ----- */
|
/* ----- Forms ----- */
|
||||||
/* ----- Misc ----- */
|
/* ----- Misc ----- */
|
||||||
|
|
||||||
[tabindex="-1"]:focus {
|
[tabindex='-1']:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import './main.scss';
|
import './main.scss';
|
||||||
|
|
||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue';
|
||||||
import App from './App.vue';
|
|
||||||
import type { ChatOptions } from '@n8n/chat/types';
|
|
||||||
import { defaultMountingTarget, defaultOptions } from '@n8n/chat/constants';
|
import { defaultMountingTarget, defaultOptions } from '@n8n/chat/constants';
|
||||||
import { createDefaultMountingTarget } from '@n8n/chat/utils';
|
|
||||||
import { ChatPlugin } from '@n8n/chat/plugins';
|
import { ChatPlugin } from '@n8n/chat/plugins';
|
||||||
|
import type { ChatOptions } from '@n8n/chat/types';
|
||||||
|
import { createDefaultMountingTarget } from '@n8n/chat/utils';
|
||||||
|
|
||||||
|
import App from './App.vue';
|
||||||
|
|
||||||
export function createChat(options?: Partial<ChatOptions>) {
|
export function createChat(options?: Partial<ChatOptions>) {
|
||||||
const resolvedOptions: ChatOptions = {
|
const resolvedOptions: ChatOptions = {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue