mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-17 05:29:44 +02:00
refactor(core): Extract Workflow Builder session persistence into a backend module (#41075)
This commit is contained in:
parent
e45a65a9bf
commit
cdeb405948
2
OWNERS
2
OWNERS
|
|
@ -118,7 +118,7 @@ packages/cli/src/modules/data-table/ @n8n-io/knowledge
|
|||
packages/cli/src/evaluation.ee/ @n8n-io/ai-trust
|
||||
packages/cli/src/chat/ @n8n-io/ai
|
||||
packages/cli/src/tool-generation/ @n8n-io/ai
|
||||
packages/cli/src/modules/workflow-builder/ @n8n-io/ai-assistant
|
||||
packages/modules/workflow-builder/ @n8n-io/ai-assistant
|
||||
packages/cli/src/modules/mcp/ @n8n-io/context
|
||||
packages/modules/quick-connect/ @n8n-io/ai
|
||||
packages/cli/src/modules/chat-hub/ @n8n-io/ai-assistant
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@
|
|||
"@n8n/backend-common": "workspace:*",
|
||||
"@n8n/backend-module-insights": "workspace:*",
|
||||
"@n8n/backend-module-quick-connect": "workspace:*",
|
||||
"@n8n/backend-module-workflow-builder": "workspace:*",
|
||||
"@n8n/backend-network": "workspace:*",
|
||||
"@n8n/backend-services": "workspace:*",
|
||||
"@n8n/blob-storage": "workspace:*",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import { WorkflowBuilderSession } from '@n8n/backend-module-workflow-builder';
|
||||
import { ModuleMetadata } from '@n8n/decorators';
|
||||
import { Container } from '@n8n/di';
|
||||
|
||||
import { packagedModules } from '../modules.manifest';
|
||||
|
||||
describe('packaged Workflow Builder', () => {
|
||||
it('loads the package entrypoint through the CLI manifest', async () => {
|
||||
const metadata = Container.get(ModuleMetadata);
|
||||
expect(metadata.get('workflow-builder')).toBeUndefined();
|
||||
|
||||
const load = packagedModules['workflow-builder'];
|
||||
expect(load).toBeTypeOf('function');
|
||||
await load?.();
|
||||
|
||||
const entry = metadata.get('workflow-builder');
|
||||
expect(entry?.instanceTypes).toEqual(['main']);
|
||||
const module = entry && Container.get(entry.class);
|
||||
expect(await module?.entities?.()).toEqual([WorkflowBuilderSession]);
|
||||
});
|
||||
});
|
||||
|
|
@ -7,4 +7,5 @@ import type { PackagedModules } from '@n8n/backend-common';
|
|||
export const packagedModules: PackagedModules = {
|
||||
insights: async () => await import('@n8n/backend-module-insights/module'),
|
||||
'quick-connect': async () => await import('@n8n/backend-module-quick-connect/module'),
|
||||
'workflow-builder': async () => await import('@n8n/backend-module-workflow-builder/module'),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import type { Mock, MockedClass } from 'vitest';
|
||||
import { AiWorkflowBuilderService } from '@n8n/ai-workflow-builder';
|
||||
import type { Logger } from '@n8n/backend-common';
|
||||
import { WorkflowBuilderSessionRepository } from '@n8n/backend-module-workflow-builder';
|
||||
import type { HttpTransport, OutboundHttp, SsrfProtectionService } from '@n8n/backend-network';
|
||||
import type { GlobalConfig, SsrfProtectionConfig } from '@n8n/config';
|
||||
import { AiAssistantClient } from '@n8n_io/ai-assistant-sdk';
|
||||
|
|
@ -14,7 +15,6 @@ import { join } from 'node:path';
|
|||
|
||||
import type { License } from '@/license';
|
||||
import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials';
|
||||
import type { WorkflowBuilderSessionRepository } from '@/modules/workflow-builder';
|
||||
import type { Push } from '@/push';
|
||||
import { WorkflowBuilderService } from '@/services/ai-workflow-builder.service';
|
||||
import type { DynamicNodeParametersService } from '@/services/dynamic-node-parameters.service';
|
||||
|
|
@ -138,6 +138,14 @@ describe('WorkflowBuilderService', () => {
|
|||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('uses the public repository constructor for dependency injection', () => {
|
||||
const dependencies: unknown[] = Reflect.getMetadata(
|
||||
'design:paramtypes',
|
||||
WorkflowBuilderService,
|
||||
);
|
||||
expect(dependencies).toContain(WorkflowBuilderSessionRepository);
|
||||
});
|
||||
|
||||
it('should initialize without creating the service immediately', () => {
|
||||
expect(MockedAiWorkflowBuilderService).not.toHaveBeenCalled();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
ChatPayload,
|
||||
} from '@n8n/ai-workflow-builder';
|
||||
import { Logger } from '@n8n/backend-common';
|
||||
import { WorkflowBuilderSessionRepository } from '@n8n/backend-module-workflow-builder';
|
||||
import { OutboundHttp, SsrfProtectionService } from '@n8n/backend-network';
|
||||
import { GlobalConfig, SsrfProtectionConfig } from '@n8n/config';
|
||||
import { BUILTIN_NODES_PACKAGES } from '@n8n/constants';
|
||||
|
|
@ -24,7 +25,6 @@ import * as path from 'path';
|
|||
import { N8N_VERSION } from '@/constants';
|
||||
import { License } from '@/license';
|
||||
import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials';
|
||||
import { WorkflowBuilderSessionRepository } from '@/modules/workflow-builder';
|
||||
import { Push } from '@/push';
|
||||
import { DynamicNodeParametersService } from '@/services/dynamic-node-parameters.service';
|
||||
import { UrlService } from '@n8n/backend-services';
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
{ "path": "../@n8n/backend-services/tsconfig.build.json" },
|
||||
{ "path": "../modules/insights/backend/tsconfig.build.json" },
|
||||
{ "path": "../modules/quick-connect/backend/tsconfig.build.json" },
|
||||
{ "path": "../modules/workflow-builder/backend/tsconfig.build.json" },
|
||||
{ "path": "../@n8n/backend-test-utils/tsconfig.build.json" },
|
||||
{ "path": "../@n8n/blob-storage/tsconfig.build.json" },
|
||||
{ "path": "../@n8n/di/tsconfig.build.json" },
|
||||
|
|
|
|||
|
|
@ -30,6 +30,14 @@ const alias = {
|
|||
__dirname,
|
||||
'../modules/insights/backend/src/index.ts',
|
||||
),
|
||||
'@n8n/backend-module-workflow-builder/module': path.resolve(
|
||||
__dirname,
|
||||
'../modules/workflow-builder/backend/src/workflow-builder.module.ts',
|
||||
),
|
||||
'@n8n/backend-module-workflow-builder': path.resolve(
|
||||
__dirname,
|
||||
'../modules/workflow-builder/backend/src/index.ts',
|
||||
),
|
||||
'@n8n/mcp-apps/server': path.resolve(__dirname, '../@n8n/mcp-apps/src/server/index.ts'),
|
||||
'@n8n/telemetry': path.resolve(__dirname, '../@n8n/telemetry/src'),
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
|
|
|
|||
17
packages/modules/workflow-builder/backend/README.md
Normal file
17
packages/modules/workflow-builder/backend/README.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Workflow Builder backend
|
||||
|
||||
This package stores Workflow Builder sessions. It owns the session entity and repository.
|
||||
|
||||
Import `WorkflowBuilderSession`, `IWorkflowBuilderSession`, and
|
||||
`WorkflowBuilderSessionRepository` from `@n8n/backend-module-workflow-builder`.
|
||||
The public API does not register the backend module.
|
||||
|
||||
The CLI manifest loads `@n8n/backend-module-workflow-builder/module` on demand.
|
||||
The module registers its entity before the database starts. Migrations stay in `@n8n/db`.
|
||||
|
||||
Run `pnpm build > build.log 2>&1` before you use the package from the CLI. The build writes JavaScript and
|
||||
declarations to `dist`. It excludes tests.
|
||||
|
||||
Run `pnpm test --maxWorkers=1` for unit tests and SQLite persistence tests.
|
||||
Run `pnpm test:postgres --maxWorkers=1` for persistence tests with the shared PostgreSQL harness.
|
||||
The PostgreSQL harness needs Docker.
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import { backendConfig } from '@n8n/oxlint-config/backend';
|
||||
import { defineConfig } from 'oxlint';
|
||||
|
||||
export default defineConfig({
|
||||
extends: [backendConfig],
|
||||
options: { typeAware: true },
|
||||
ignorePatterns: ['coverage/**', 'dist/**'],
|
||||
});
|
||||
71
packages/modules/workflow-builder/backend/package.json
Normal file
71
packages/modules/workflow-builder/backend/package.json
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
{
|
||||
"name": "@n8n/backend-module-workflow-builder",
|
||||
"version": "0.1.0",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.js"
|
||||
},
|
||||
"./module": {
|
||||
"types": "./dist/workflow-builder.module.d.ts",
|
||||
"import": "./dist/workflow-builder.module.js",
|
||||
"require": "./dist/workflow-builder.module.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "rimraf dist .turbo",
|
||||
"dev": "pnpm watch",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"build": "tsc -p tsconfig.build.json",
|
||||
"build:unchecked": "tsc -p tsconfig.build.json --noCheck",
|
||||
"format": "biome format --write .",
|
||||
"format:check": "biome ci .",
|
||||
"lint": "oxlint --type-aware . --quiet",
|
||||
"lint:fix": "oxlint --type-aware . --fix",
|
||||
"watch": "tsc -p tsconfig.build.json --watch",
|
||||
"test": "vitest run",
|
||||
"test:unit": "vitest run",
|
||||
"test:dev": "vitest --silent=false",
|
||||
"test:postgres": "N8N_LOG_LEVEL=silent vitest run --config vitest.config.postgres.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@n8n/ai-workflow-builder": "workspace:*",
|
||||
"@n8n/db": "workspace:*",
|
||||
"@n8n/decorators": "workspace:*",
|
||||
"@n8n/di": "workspace:*",
|
||||
"@n8n/typeorm": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@langchain/core": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@langchain/core": "catalog:",
|
||||
"@n8n/backend-common": "workspace:*",
|
||||
"@n8n/backend-test-utils": "workspace:*",
|
||||
"@n8n/oxlint-config": "workspace:*",
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"n8n-containers": "workspace:*",
|
||||
"oxlint": "catalog:",
|
||||
"oxlint-tsgolint": "catalog:",
|
||||
"typescript": "catalog:typescript",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:"
|
||||
},
|
||||
"license": "LicenseRef-n8n-sustainable-use",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"module": [
|
||||
"dist/workflow-builder.module.d.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
import { AIMessage, HumanMessage } from '@langchain/core/messages';
|
||||
import { ModuleRegistry } from '@n8n/backend-common';
|
||||
import { createWorkflow, testDb, testModules } from '@n8n/backend-test-utils';
|
||||
import { DbConnection, User, UserRepository, WorkflowEntity, WorkflowRepository } from '@n8n/db';
|
||||
import { Container } from '@n8n/di';
|
||||
import { DataSource } from '@n8n/typeorm';
|
||||
|
||||
import { WorkflowBuilderSession, WorkflowBuilderSessionRepository } from '../index';
|
||||
|
||||
let repository: WorkflowBuilderSessionRepository;
|
||||
let dataSource: DataSource;
|
||||
|
||||
beforeAll(async () => {
|
||||
await testModules.loadModules(['workflow-builder'], {
|
||||
'workflow-builder': async () => await import('../workflow-builder.module.js'),
|
||||
});
|
||||
expect(Container.get(ModuleRegistry).entities).toContain(WorkflowBuilderSession);
|
||||
expect(Container.get(DbConnection).connectionState.connected).toBe(false);
|
||||
await testDb.init();
|
||||
dataSource = Container.get(DataSource);
|
||||
repository = Container.get(WorkflowBuilderSessionRepository);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await repository.delete({});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await testDb.terminate();
|
||||
});
|
||||
|
||||
async function createSessionOwner() {
|
||||
const user = await Container.get(UserRepository).save({ role: { slug: 'global:owner' } });
|
||||
const workflow = await createWorkflow();
|
||||
return { user, workflow, threadId: `workflow-${workflow.id}-user-${user.id}` };
|
||||
}
|
||||
|
||||
describe('packaged session persistence', () => {
|
||||
it('resolves relations to the shared database entities', () => {
|
||||
const metadata = dataSource.getMetadata(WorkflowBuilderSession);
|
||||
expect(metadata.tableNameWithoutPrefix).toBe('workflow_builder_session');
|
||||
expect(metadata.findRelationWithPropertyPath('workflow')?.inverseEntityMetadata.target).toBe(
|
||||
WorkflowEntity,
|
||||
);
|
||||
expect(metadata.findRelationWithPropertyPath('user')?.inverseEntityMetadata.target).toBe(User);
|
||||
});
|
||||
|
||||
it('saves, restores, and updates one session for both thread formats', async () => {
|
||||
const { user, workflow, threadId } = await createSessionOwner();
|
||||
const messages = [new HumanMessage('Build a workflow'), new AIMessage('Workflow ready')];
|
||||
await repository.saveSession(threadId, {
|
||||
messages,
|
||||
previousSummary: 'Workflow summary',
|
||||
updatedAt: new Date(),
|
||||
activeVersionCardId: 'version-card',
|
||||
resumeAfterRestoreMessageId: 'resume-message',
|
||||
});
|
||||
|
||||
expect(await repository.getSession(`${threadId}-code`)).toEqual({
|
||||
messages,
|
||||
previousSummary: 'Workflow summary',
|
||||
updatedAt: expect.any(Date),
|
||||
activeVersionCardId: 'version-card',
|
||||
resumeAfterRestoreMessageId: 'resume-message',
|
||||
});
|
||||
const original = await repository.findOneByOrFail({ workflowId: workflow.id, userId: user.id });
|
||||
expect(original.id).toMatch(/^[0-9a-f-]{36}$/);
|
||||
expect(original.createdAt).toBeInstanceOf(Date);
|
||||
expect(original.updatedAt).toBeInstanceOf(Date);
|
||||
expect(original.messages.map((message) => message.type)).toEqual(['human', 'ai']);
|
||||
|
||||
await repository.saveSession(`${threadId}-code`, {
|
||||
messages: [new HumanMessage('Update the workflow')],
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
expect(await repository.count()).toBe(1);
|
||||
expect(await repository.findOneByOrFail({ id: original.id })).toMatchObject({
|
||||
createdAt: original.createdAt,
|
||||
previousSummary: null,
|
||||
activeVersionCardId: null,
|
||||
resumeAfterRestoreMessageId: null,
|
||||
});
|
||||
expect(await repository.getSession(threadId)).toMatchObject({
|
||||
messages: [new HumanMessage('Update the workflow')],
|
||||
previousSummary: undefined,
|
||||
});
|
||||
|
||||
await repository.deleteSession(`${threadId}-code`);
|
||||
expect(await repository.getSession(threadId)).toBeNull();
|
||||
});
|
||||
|
||||
it('generates an ID and enforces the workflow and user uniqueness rule', async () => {
|
||||
const { user, workflow } = await createSessionOwner();
|
||||
const values = { workflowId: workflow.id, userId: user.id, messages: [] };
|
||||
const entity = await repository.save(repository.create(values));
|
||||
expect(entity.id).toMatch(/^[0-9a-f-]{36}$/);
|
||||
await expect(repository.save(repository.create(values))).rejects.toThrow(/unique/i);
|
||||
});
|
||||
|
||||
it.each(['workflow', 'user'] as const)(
|
||||
'removes sessions when the %s is removed',
|
||||
async (owner) => {
|
||||
const { user, workflow, threadId } = await createSessionOwner();
|
||||
await repository.saveSession(threadId, { messages: [], updatedAt: new Date() });
|
||||
if (owner === 'workflow') {
|
||||
await Container.get(WorkflowRepository).delete(workflow.id);
|
||||
} else {
|
||||
await Container.get(UserRepository).delete(user.id);
|
||||
}
|
||||
expect(await repository.getSession(threadId)).toBeNull();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { AIMessage, HumanMessage } from '@langchain/core/messages';
|
||||
import { mockClear } from 'vitest-mock-extended';
|
||||
|
||||
import { mockEntityManager } from '@test/mocking';
|
||||
import { mockInstance } from '@n8n/backend-test-utils';
|
||||
import { DataSource, EntityManager, type EntityMetadata } from '@n8n/typeorm';
|
||||
import { mock, mockClear } from 'vitest-mock-extended';
|
||||
|
||||
import { WorkflowBuilderSession } from '../workflow-builder-session.entity';
|
||||
|
||||
|
|
@ -25,7 +25,12 @@ vi.mock('@n8n/ai-workflow-builder', () => ({
|
|||
import { WorkflowBuilderSessionRepository } from '../workflow-builder-session.repository';
|
||||
|
||||
describe('WorkflowBuilderSessionRepository', () => {
|
||||
const entityManager = mockEntityManager(WorkflowBuilderSession);
|
||||
const entityManager = mockInstance(EntityManager);
|
||||
const dataSource = mockInstance(DataSource, {
|
||||
manager: entityManager,
|
||||
getMetadata: () => mock<EntityMetadata>({ target: WorkflowBuilderSession }),
|
||||
});
|
||||
Object.assign(entityManager, { connection: dataSource });
|
||||
let repository: WorkflowBuilderSessionRepository;
|
||||
|
||||
const mockExecute = vi.fn().mockResolvedValue(undefined);
|
||||
|
|
@ -46,11 +51,7 @@ describe('WorkflowBuilderSessionRepository', () => {
|
|||
mockQueryBuilder.values.mockClear().mockReturnThis();
|
||||
mockQueryBuilder.orUpdate.mockClear().mockReturnThis();
|
||||
|
||||
// Create repository with mocked data source
|
||||
const mockDataSource = {
|
||||
manager: entityManager,
|
||||
};
|
||||
repository = new WorkflowBuilderSessionRepository(mockDataSource as never);
|
||||
repository = new WorkflowBuilderSessionRepository(dataSource);
|
||||
vi.spyOn(repository, 'createQueryBuilder').mockReturnValue(mockQueryBuilder as never);
|
||||
});
|
||||
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import { ModuleMetadata } from '@n8n/decorators';
|
||||
import { Container } from '@n8n/di';
|
||||
|
||||
import { WorkflowBuilderSession } from '../index';
|
||||
import { WorkflowBuilderModule } from '../workflow-builder.module';
|
||||
|
||||
describe('WorkflowBuilderModule', () => {
|
||||
it('registers the main instance entrypoint with the public entity', async () => {
|
||||
expect(Container.get(ModuleMetadata).get('workflow-builder')).toMatchObject({
|
||||
class: WorkflowBuilderModule,
|
||||
instanceTypes: ['main'],
|
||||
});
|
||||
expect(await new WorkflowBuilderModule().entities()).toEqual([WorkflowBuilderSession]);
|
||||
});
|
||||
});
|
||||
17
packages/modules/workflow-builder/backend/src/index.test.ts
Normal file
17
packages/modules/workflow-builder/backend/src/index.test.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { ModuleMetadata } from '@n8n/decorators';
|
||||
import { Container } from '@n8n/di';
|
||||
|
||||
describe('public API', () => {
|
||||
it('does not register the module entrypoint', async () => {
|
||||
const metadata = Container.get(ModuleMetadata);
|
||||
expect(metadata.get('workflow-builder')).toBeUndefined();
|
||||
|
||||
const publicApi = await import('./index.js');
|
||||
|
||||
expect(metadata.get('workflow-builder')).toBeUndefined();
|
||||
expect(Object.keys(publicApi).sort()).toEqual([
|
||||
'WorkflowBuilderSession',
|
||||
'WorkflowBuilderSessionRepository',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
@ -3,4 +3,3 @@ export {
|
|||
type IWorkflowBuilderSession,
|
||||
} from './workflow-builder-session.entity';
|
||||
export { WorkflowBuilderSessionRepository } from './workflow-builder-session.repository';
|
||||
export { WorkflowBuilderModule } from './workflow-builder.module';
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import './setup-test-folder';
|
||||
|
||||
import { createTestDatabaseGlobalSetup } from 'n8n-containers/test-db-global-setup';
|
||||
|
||||
// Run migrations after the suite registers this package's entity.
|
||||
export const { setup, teardown } = createTestDatabaseGlobalSetup();
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import { mkdirSync, mkdtempSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
|
||||
process.env.N8N_ENCRYPTION_KEY = 'test_key';
|
||||
|
||||
const baseDir = join(tmpdir(), 'n8n-workflow-builder-tests');
|
||||
mkdirSync(baseDir, { recursive: true });
|
||||
|
||||
const testDir = mkdtempSync(join(baseDir, 'run-'));
|
||||
const settingsDir = join(testDir, '.n8n');
|
||||
mkdirSync(settingsDir);
|
||||
process.env.N8N_USER_FOLDER = testDir;
|
||||
process.env.N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS = 'true';
|
||||
|
||||
writeFileSync(join(settingsDir, 'config'), JSON.stringify({ encryptionKey: 'test_key' }), {
|
||||
encoding: 'utf-8',
|
||||
mode: 0o600,
|
||||
});
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"extends": ["./tsconfig.json", "@n8n/typescript-config/tsconfig.build.go.json"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"rootDir": "src",
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["src/**/__tests__/**", "src/**/*.test.ts"]
|
||||
}
|
||||
17
packages/modules/workflow-builder/backend/tsconfig.json
Normal file
17
packages/modules/workflow-builder/backend/tsconfig.json
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"extends": [
|
||||
"@n8n/typescript-config/tsconfig.common.go.json",
|
||||
"@n8n/typescript-config/tsconfig.backend.go.json"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"lib": ["es2023", "dom"],
|
||||
"types": ["node", "vitest/globals"],
|
||||
"strict": true,
|
||||
"strictFunctionTypes": false,
|
||||
"strictPropertyInitialization": false,
|
||||
"useUnknownInCatchVariables": false
|
||||
},
|
||||
"include": ["src/**/*.ts", "test/**/*.ts", "vitest.config*.ts"]
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import baseConfig from './vitest.config';
|
||||
|
||||
export default {
|
||||
...baseConfig,
|
||||
test: {
|
||||
...baseConfig.test,
|
||||
include: ['src/**/*.integration.test.ts'],
|
||||
testTimeout: 30_000,
|
||||
hookTimeout: 30_000,
|
||||
globalSetup: ['./test/setup-postgres.ts'],
|
||||
},
|
||||
};
|
||||
19
packages/modules/workflow-builder/backend/vitest.config.ts
Normal file
19
packages/modules/workflow-builder/backend/vitest.config.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { createVitestConfigWithDecorators } from '@n8n/vitest-config/node-decorators';
|
||||
import { tscDecoratorTransform } from '@n8n/vitest-config/tsc-decorator-transform';
|
||||
import { mergeConfig } from 'vite';
|
||||
import { configDefaults } from 'vitest/config';
|
||||
|
||||
export default mergeConfig(
|
||||
createVitestConfigWithDecorators({}, { pinCjs: ['@n8n/backend-common', '@n8n/decorators'] }),
|
||||
{
|
||||
plugins: [
|
||||
tscDecoratorTransform({ filePredicate: (fileName) => /\.entity\.ts$/.test(fileName) }),
|
||||
],
|
||||
test: {
|
||||
pool: 'forks',
|
||||
setupFiles: ['./test/setup-test-folder.ts'],
|
||||
exclude: [...configDefaults.exclude, '**/dist/**'],
|
||||
hookTimeout: 30_000,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
|
@ -4835,6 +4835,9 @@ importers:
|
|||
'@n8n/backend-module-quick-connect':
|
||||
specifier: workspace:*
|
||||
version: link:../modules/quick-connect/backend
|
||||
'@n8n/backend-module-workflow-builder':
|
||||
specifier: workspace:*
|
||||
version: link:../modules/workflow-builder/backend
|
||||
'@n8n/backend-network':
|
||||
specifier: workspace:*
|
||||
version: link:../@n8n/backend-network
|
||||
|
|
@ -7719,6 +7722,64 @@ importers:
|
|||
specifier: ^2.2.8
|
||||
version: 2.2.8(patch_hash=e2aee939ccac8a57fe449bfd92bedd8117841579526217bc39aca26c6b8c317f)(typescript@6.0.2)
|
||||
|
||||
packages/modules/workflow-builder/backend:
|
||||
dependencies:
|
||||
'@n8n/ai-workflow-builder':
|
||||
specifier: workspace:*
|
||||
version: link:../../../@n8n/ai-workflow-builder.ee
|
||||
'@n8n/db':
|
||||
specifier: workspace:*
|
||||
version: link:../../../@n8n/db
|
||||
'@n8n/decorators':
|
||||
specifier: workspace:*
|
||||
version: link:../../../@n8n/decorators
|
||||
'@n8n/di':
|
||||
specifier: workspace:*
|
||||
version: link:../../../@n8n/di
|
||||
'@n8n/typeorm':
|
||||
specifier: workspace:*
|
||||
version: link:../../../@n8n/typeorm
|
||||
devDependencies:
|
||||
'@langchain/core':
|
||||
specifier: 'catalog:'
|
||||
version: 1.2.8(@opentelemetry/api@1.9.1)(@opentelemetry/exporter-trace-otlp-proto@0.221.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))(openai@6.46.0(@aws-sdk/credential-provider-node@3.972.80)(@smithy/signature-v4@5.7.2)(ws@8.21.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.21.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))
|
||||
'@n8n/backend-common':
|
||||
specifier: workspace:*
|
||||
version: link:../../../@n8n/backend-common
|
||||
'@n8n/backend-test-utils':
|
||||
specifier: workspace:*
|
||||
version: link:../../../@n8n/backend-test-utils
|
||||
'@n8n/oxlint-config':
|
||||
specifier: workspace:*
|
||||
version: link:../../../@n8n/oxlint-config
|
||||
'@n8n/typescript-config':
|
||||
specifier: workspace:*
|
||||
version: link:../../../@n8n/typescript-config
|
||||
'@n8n/vitest-config':
|
||||
specifier: workspace:*
|
||||
version: link:../../../@n8n/vitest-config
|
||||
n8n-containers:
|
||||
specifier: workspace:*
|
||||
version: link:../../../quality/environments/containers
|
||||
oxlint:
|
||||
specifier: 'catalog:'
|
||||
version: 1.78.0(oxlint-tsgolint@0.21.1)
|
||||
oxlint-tsgolint:
|
||||
specifier: 'catalog:'
|
||||
version: 0.21.1
|
||||
typescript:
|
||||
specifier: catalog:typescript
|
||||
version: 7.0.2
|
||||
vite:
|
||||
specifier: 'catalog:'
|
||||
version: 8.0.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@20.19.41)(esbuild@0.28.1)(jiti@2.6.1)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.16.1)(tsx@4.19.3)(yaml@2.8.3)
|
||||
vitest:
|
||||
specifier: 'catalog:'
|
||||
version: 5.0.1(@opentelemetry/api@1.9.1)(@types/node@20.19.41)(@vitest/browser-playwright@5.0.1)(@vitest/coverage-istanbul@5.0.1)(@vitest/coverage-v8@5.0.1)(@vitest/ui@5.0.1)(jsdom@23.0.1(bufferutil@4.0.9)(supports-color@8.1.1)(utf-8-validate@5.0.10))(vite@8.0.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@20.19.41)(esbuild@0.28.1)(jiti@2.6.1)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.16.1)(tsx@4.19.3)(yaml@2.8.3))
|
||||
vitest-mock-extended:
|
||||
specifier: 'catalog:'
|
||||
version: 3.1.0(typescript@7.0.2)(vitest@5.0.1)
|
||||
|
||||
packages/node-dev:
|
||||
dependencies:
|
||||
'@n8n/di':
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user