refactor(core): Replace typedi with our custom DI system (no-changelog) (#12389)
Some checks failed
Test Master / install-and-build (push) Waiting to run
Test Master / Unit tests (18.x) (push) Blocked by required conditions
Test Master / Unit tests (20.x) (push) Blocked by required conditions
Test Master / Unit tests (22.4) (push) Blocked by required conditions
Test Master / Lint (push) Blocked by required conditions
Test Master / Notify Slack on failure (push) Blocked by required conditions
Benchmark Docker Image CI / build (push) Has been cancelled

Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2025-01-06 10:21:24 +01:00 committed by GitHub
parent 8053a4a176
commit 39d5e0ff87
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
413 changed files with 979 additions and 452 deletions

View file

@ -84,7 +84,6 @@
"ws": ">=8.17.1"
},
"patchedDependencies": {
"typedi@0.10.0": "patches/typedi@0.10.0.patch",
"pkce-challenge@3.0.0": "patches/pkce-challenge@3.0.0.patch",
"pyodide@0.23.4": "patches/pyodide@0.23.4.patch",
"@types/express-serve-static-core@4.17.43": "patches/@types__express-serve-static-core@4.17.43.patch",

View file

@ -21,7 +21,7 @@
"dist/**/*"
],
"dependencies": {
"reflect-metadata": "0.2.2",
"typedi": "catalog:"
"@n8n/di": "workspace:*",
"reflect-metadata": "catalog:"
}
}

View file

@ -1,6 +1,6 @@
import 'reflect-metadata';
import { Container, Service } from '@n8n/di';
import { readFileSync } from 'fs';
import { Container, Service } from 'typedi';
// eslint-disable-next-line @typescript-eslint/ban-types
type Class = Function;
@ -35,7 +35,7 @@ export const Config: ClassDecorator = (ConfigClass: Class) => {
for (const [key, { type, envName }] of classMetadata) {
if (typeof type === 'function' && globalMetadata.has(type)) {
config[key] = Container.get(type);
config[key] = Container.get(type as Constructable);
} else if (envName) {
const value = readEnv(envName);
if (value === undefined) continue;

View file

@ -1,6 +1,6 @@
import { Container } from '@n8n/di';
import fs from 'fs';
import { mock } from 'jest-mock-extended';
import { Container } from 'typedi';
import { GlobalConfig } from '../src/index';

View file

@ -1,4 +1,4 @@
import { Container } from 'typedi';
import { Container } from '@n8n/di';
import { Config, Env } from '../src/decorators';

View file

@ -9,5 +9,6 @@
"baseUrl": "src",
"tsBuildInfoFile": "dist/typecheck.tsbuildinfo"
},
"include": ["src/**/*.ts", "test/**/*.ts"]
"include": ["src/**/*.ts", "test/**/*.ts"],
"references": [{ "path": "../di/tsconfig.build.json" }]
}

View file

@ -0,0 +1,7 @@
const sharedOptions = require('@n8n_io/eslint-config/shared');
/** @type {import('@types/eslint').ESLint.ConfigData} */
module.exports = {
extends: ['@n8n_io/eslint-config/base'],
...sharedOptions(__dirname),
};

View file

@ -0,0 +1,52 @@
## @n8n/di
`@n8n/di` is a dependency injection (DI) container library, based on [`typedi`](https://github.com/typestack/typedi).
n8n no longer uses `typedi` because:
- `typedi` is no longer officially maintained
- Need for future-proofing, e.g. stage-3 decorators
- Small enough that it is worth the maintenance burden
- Easier to customize, e.g. to simplify unit tests
### Usage
```typescript
// from https://github.com/typestack/typedi/blob/develop/README.md
import { Container, Service } from 'typedi';
@Service()
class ExampleInjectedService {
printMessage() {
console.log('I am alive!');
}
}
@Service()
class ExampleService {
constructor(
// because we annotated ExampleInjectedService with the @Service()
// decorator TypeDI will automatically inject an instance of
// ExampleInjectedService here when the ExampleService class is requested
// from TypeDI.
public injectedService: ExampleInjectedService
) {}
}
const serviceInstance = Container.get(ExampleService);
// we request an instance of ExampleService from TypeDI
serviceInstance.injectedService.printMessage();
// logs "I am alive!" to the console
```
Requires enabling these flags in `tsconfig.json`:
```json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
```

View file

@ -0,0 +1,2 @@
/** @type {import('jest').Config} */
module.exports = require('../../../jest.config');

View file

@ -0,0 +1,26 @@
{
"name": "@n8n/di",
"version": "0.1.0",
"scripts": {
"clean": "rimraf dist .turbo",
"dev": "pnpm watch",
"typecheck": "tsc --noEmit",
"build": "tsc -p tsconfig.build.json",
"format": "biome format --write .",
"format:check": "biome ci .",
"lint": "eslint .",
"lintfix": "eslint . --fix",
"watch": "tsc -p tsconfig.build.json --watch",
"test": "jest",
"test:dev": "jest --watch"
},
"main": "dist/di.js",
"module": "src/di.ts",
"types": "dist/di.d.ts",
"files": [
"dist/**/*"
],
"dependencies": {
"reflect-metadata": "catalog:"
}
}

View file

@ -0,0 +1,287 @@
import { Container, Service } from '../di';
@Service()
class SimpleService {
getValue() {
return 'simple';
}
}
@Service()
class DependentService {
constructor(readonly simple: SimpleService) {}
getValue() {
return this.simple.getValue() + '-dependent';
}
}
class CustomFactory {
getValue() {
return 'factory-made';
}
}
@Service({ factory: () => new CustomFactory() })
class FactoryService {
getValue() {
return 'should-not-be-called';
}
}
abstract class AbstractService {
abstract getValue(): string;
}
@Service()
class ConcreteService extends AbstractService {
getValue(): string {
return 'concrete';
}
}
describe('DI Container', () => {
beforeEach(() => {
jest.clearAllMocks();
Container.reset();
});
describe('basic functionality', () => {
it('should create a simple instance', () => {
const instance = Container.get(SimpleService);
expect(instance).toBeInstanceOf(SimpleService);
expect(instance.getValue()).toBe('simple');
});
it('should return same instance on multiple gets', () => {
const instance1 = Container.get(SimpleService);
const instance2 = Container.get(SimpleService);
expect(instance1).toBe(instance2);
});
it('should handle classes with no dependencies (empty constructor)', () => {
@Service()
class EmptyConstructorService {}
const instance = Container.get(EmptyConstructorService);
expect(instance).toBeInstanceOf(EmptyConstructorService);
});
it('should throw when trying to resolve an undecorated class', () => {
class UnDecoratedService {}
expect(() => Container.get(UnDecoratedService)).toThrow();
});
});
describe('dependency injection', () => {
it('should inject dependencies correctly', () => {
const dependent = Container.get(DependentService);
expect(dependent).toBeInstanceOf(DependentService);
expect(dependent.getValue()).toBe('simple-dependent');
expect(dependent.simple).toBeInstanceOf(SimpleService);
});
it('should handle deep dependency chains', () => {
@Service()
class ServiceC {
getValue() {
return 'C';
}
}
@Service()
class ServiceB {
constructor(private c: ServiceC) {}
getValue() {
return this.c.getValue() + 'B';
}
}
@Service()
class ServiceA {
constructor(private b: ServiceB) {}
getValue() {
return this.b.getValue() + 'A';
}
}
const instance = Container.get(ServiceA);
expect(instance.getValue()).toBe('CBA');
});
it('should return undefined for non-decorated dependencies in resolution chain', () => {
class NonDecoratedDep {}
@Service()
class ServiceWithNonDecoratedDep {
constructor(readonly dep: NonDecoratedDep) {}
}
const instance = Container.get(ServiceWithNonDecoratedDep);
expect(instance).toBeInstanceOf(ServiceWithNonDecoratedDep);
expect(instance.dep).toBeUndefined();
});
});
describe('factory handling', () => {
it('should use factory when provided', () => {
const instance = Container.get(FactoryService);
expect(instance).toBeInstanceOf(CustomFactory);
expect(instance.getValue()).toBe('factory-made');
});
it('should preserve factory metadata when setting instance', () => {
const customInstance = new CustomFactory();
Container.set(FactoryService, customInstance);
const instance = Container.get(FactoryService);
expect(instance).toBe(customInstance);
});
it('should preserve factory when resetting container', () => {
const factoryInstance1 = Container.get(FactoryService);
Container.reset();
const factoryInstance2 = Container.get(FactoryService);
expect(factoryInstance1).not.toBe(factoryInstance2);
expect(factoryInstance2.getValue()).toBe('factory-made');
});
it('should throw error when factory throws', () => {
@Service({
factory: () => {
throw new Error('Factory error');
},
})
class ErrorFactoryService {}
expect(() => Container.get(ErrorFactoryService)).toThrow('Factory error');
});
});
describe('instance management', () => {
it('should allow manual instance setting', () => {
const customInstance = new SimpleService();
Container.set(SimpleService, customInstance);
const instance = Container.get(SimpleService);
expect(instance).toBe(customInstance);
});
});
describe('abstract classes', () => {
it('should throw when trying to instantiate an abstract class directly', () => {
@Service()
abstract class TestAbstractClass {
abstract doSomething(): void;
// Add a concrete method to make the class truly abstract at runtime
constructor() {
if (this.constructor === TestAbstractClass) {
throw new TypeError('Abstract class "TestAbstractClass" cannot be instantiated');
}
}
}
expect(() => Container.get(TestAbstractClass)).toThrow(
'[DI] TestAbstractClass is an abstract class, and cannot be instantiated',
);
});
it('should allow setting an implementation for an abstract class', () => {
const concrete = new ConcreteService();
Container.set(AbstractService, concrete);
const instance = Container.get(AbstractService);
expect(instance).toBe(concrete);
expect(instance.getValue()).toBe('concrete');
});
it('should allow factory for abstract class', () => {
@Service({ factory: () => new ConcreteService() })
abstract class FactoryAbstractService {
abstract getValue(): string;
}
const instance = Container.get(FactoryAbstractService);
expect(instance).toBeInstanceOf(ConcreteService);
expect(instance.getValue()).toBe('concrete');
});
});
describe('inheritance', () => {
it('should handle inheritance in injectable classes', () => {
@Service()
class BaseService {
getValue() {
return 'base';
}
}
@Service()
class DerivedService extends BaseService {
getValue() {
return 'derived-' + super.getValue();
}
}
const instance = Container.get(DerivedService);
expect(instance.getValue()).toBe('derived-base');
});
it('should maintain separate instances for base and derived classes', () => {
@Service()
class BaseService {
getValue() {
return 'base';
}
}
@Service()
class DerivedService extends BaseService {}
const baseInstance = Container.get(BaseService);
const derivedInstance = Container.get(DerivedService);
expect(baseInstance).not.toBe(derivedInstance);
expect(baseInstance).toBeInstanceOf(BaseService);
expect(derivedInstance).toBeInstanceOf(DerivedService);
});
});
describe('type registration checking', () => {
it('should return true for registered classes', () => {
expect(Container.has(SimpleService)).toBe(true);
});
it('should return false for unregistered classes', () => {
class UnregisteredService {}
expect(Container.has(UnregisteredService)).toBe(false);
});
it('should return true for abstract classes with implementations', () => {
const concrete = new ConcreteService();
Container.set(AbstractService, concrete);
expect(Container.has(AbstractService)).toBe(true);
});
it('should return true for factory-provided services before instantiation', () => {
expect(Container.has(FactoryService)).toBe(true);
});
it('should maintain registration after reset', () => {
expect(Container.has(SimpleService)).toBe(true);
Container.reset();
expect(Container.has(SimpleService)).toBe(true);
});
it('should return true after manual instance setting', () => {
class ManualService {}
expect(Container.has(ManualService)).toBe(false);
Container.set(ManualService, new ManualService());
expect(Container.has(ManualService)).toBe(true);
});
});
});

142
packages/@n8n/di/src/di.ts Normal file
View file

@ -0,0 +1,142 @@
import 'reflect-metadata';
/**
* Represents a class constructor type that can be instantiated with 'new'
* @template T The type of instance the constructor creates
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type Constructable<T = unknown> = new (...args: any[]) => T;
type AbstractConstructable<T = unknown> = abstract new (...args: unknown[]) => T;
type ServiceIdentifier<T = unknown> = Constructable<T> | AbstractConstructable<T>;
interface Metadata<T = unknown> {
instance?: T;
factory?: () => T;
}
interface Options<T> {
factory?: () => T;
}
const instances = new Map<ServiceIdentifier, Metadata>();
/**
* Decorator that marks a class as available for dependency injection.
* @param options Configuration options for the injectable class
* @param options.factory Optional factory function to create instances of this class
* @returns A class decorator to be applied to the target class
*/
// eslint-disable-next-line @typescript-eslint/ban-types
export function Service<T = unknown>(): Function;
// eslint-disable-next-line @typescript-eslint/ban-types
export function Service<T = unknown>(options: Options<T>): Function;
export function Service<T>({ factory }: Options<T> = {}) {
return function (target: Constructable<T>) {
instances.set(target, { factory });
return target;
};
}
class DIError extends Error {
constructor(message: string) {
super(`[DI] ${message}`);
}
}
class ContainerClass {
/** Stack to track types being resolved to detect circular dependencies */
private readonly resolutionStack: ServiceIdentifier[] = [];
/**
* Checks if a type is registered in the container
* @template T The type to check for
* @param type The constructor of the type to check
* @returns True if the type is registered (has metadata), false otherwise
*/
has<T>(type: ServiceIdentifier<T>): boolean {
return instances.has(type);
}
/**
* Retrieves or creates an instance of the specified type from the container
* @template T The type of instance to retrieve
* @param type The constructor of the type to retrieve
* @returns An instance of the specified type with all dependencies injected
* @throws {DIError} If circular dependencies are detected or if the type is not injectable
*/
get<T>(type: ServiceIdentifier<T>): T {
const { resolutionStack } = this;
const metadata = instances.get(type) as Metadata<T>;
if (!metadata) {
// Special case: Allow undefined returns for non-decorated constructor params
// when resolving a dependency chain (i.e., resolutionStack not empty)
if (resolutionStack.length) return undefined as T;
throw new DIError(`${type.name} is not decorated with ${Service.name}`);
}
if (metadata?.instance) return metadata.instance as T;
// Check for circular dependencies before proceeding with instantiation
if (resolutionStack.includes(type)) {
throw new DIError(
`Circular dependency detected. ${resolutionStack.map((t) => t.name).join(' -> ')}`,
);
}
// Add current type to resolution stack before resolving dependencies
resolutionStack.push(type);
try {
let instance: T;
if (metadata?.factory) {
instance = metadata.factory();
} else {
const paramTypes = (Reflect.getMetadata('design:paramtypes', type) ??
[]) as Constructable[];
const dependencies = paramTypes.map(<P>(paramType: Constructable<P>) =>
this.get(paramType),
);
// Create new instance with resolved dependencies
instance = new (type as Constructable)(...dependencies) as T;
}
instances.set(type, { ...metadata, instance });
return instance;
} catch (error) {
if (error instanceof TypeError && error.message.toLowerCase().includes('abstract')) {
throw new DIError(`${type.name} is an abstract class, and cannot be instantiated`);
}
throw error;
} finally {
resolutionStack.pop();
}
}
/**
* Manually sets an instance for a specific type in the container
* @template T The type of instance being set
* @param type The constructor of the type to set. This can also be an abstract class
* @param instance The instance to store in the container
*/
set<T>(type: ServiceIdentifier<T>, instance: T): void {
// Preserve any existing metadata (like factory) when setting new instance
const metadata = instances.get(type) ?? {};
instances.set(type, { ...metadata, instance });
}
/** Clears all instantiated instances from the container while preserving type registrations */
reset(): void {
for (const metadata of instances.values()) {
delete metadata.instance;
}
}
}
/**
* Global dependency injection container instance
* Used to retrieve and manage class instances and their dependencies
*/
export const Container = new ContainerClass();

View file

@ -0,0 +1,11 @@
{
"extends": ["./tsconfig.json", "../../../tsconfig.build.json"],
"compilerOptions": {
"composite": true,
"rootDir": "src",
"outDir": "dist",
"tsBuildInfoFile": "dist/build.tsbuildinfo"
},
"include": ["src/**/*.ts"],
"exclude": ["src/**/__tests__/**"]
}

View file

@ -0,0 +1,12 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"rootDir": ".",
"types": ["node", "jest"],
"baseUrl": "src",
"tsBuildInfoFile": "dist/typecheck.tsbuildinfo",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"include": ["src/**/*.ts"]
}

View file

@ -35,6 +35,7 @@
},
"dependencies": {
"@n8n/config": "workspace:*",
"@n8n/di": "workspace:*",
"@sentry/node": "catalog:",
"acorn": "8.14.0",
"acorn-walk": "8.3.4",
@ -42,7 +43,6 @@
"n8n-core": "workspace:*",
"n8n-workflow": "workspace:*",
"nanoid": "catalog:",
"typedi": "catalog:",
"ws": "^8.18.0"
},
"devDependencies": {

View file

@ -1,7 +1,7 @@
import './polyfills';
import { Container } from '@n8n/di';
import type { ErrorReporter } from 'n8n-core';
import { ensureError, setGlobalState } from 'n8n-workflow';
import Container from 'typedi';
import { MainConfig } from './config/main-config';
import type { HealthCheckServer } from './health-check-server';

View file

@ -89,6 +89,7 @@
"@n8n/api-types": "workspace:*",
"@n8n/client-oauth2": "workspace:*",
"@n8n/config": "workspace:*",
"@n8n/di": "workspace:*",
"@n8n/localtunnel": "3.0.0",
"@n8n/n8n-nodes-langchain": "workspace:*",
"@n8n/permissions": "workspace:*",
@ -154,7 +155,7 @@
"prom-client": "13.2.0",
"psl": "1.9.0",
"raw-body": "2.5.1",
"reflect-metadata": "0.2.2",
"reflect-metadata": "catalog:",
"replacestream": "4.0.3",
"samlify": "2.8.9",
"semver": "7.5.4",
@ -165,7 +166,6 @@
"sshpk": "1.17.0",
"swagger-ui-express": "5.0.1",
"syslog-client": "1.1.1",
"typedi": "catalog:",
"uuid": "catalog:",
"validator": "13.7.0",
"ws": "8.17.1",

View file

@ -1,3 +1,4 @@
import { Container } from '@n8n/di';
import { mock } from 'jest-mock-extended';
import type { IWorkflowBase } from 'n8n-workflow';
import type {
@ -8,7 +9,6 @@ import type {
INodeExecutionData,
} from 'n8n-workflow';
import type PCancelable from 'p-cancelable';
import Container from 'typedi';
import { ActiveExecutions } from '@/active-executions';
import { CredentialsHelper } from '@/credentials-helper';

View file

@ -1,3 +1,4 @@
import { Container } from '@n8n/di';
import { mock } from 'jest-mock-extended';
import { DirectedGraph, WorkflowExecute } from 'n8n-core';
import * as core from 'n8n-core';
@ -18,7 +19,6 @@ import {
type IWorkflowExecuteHooks,
} from 'n8n-workflow';
import PCancelable from 'p-cancelable';
import Container from 'typedi';
import { ActiveExecutions } from '@/active-executions';
import config from '@/config';

View file

@ -1,4 +1,5 @@
import { GlobalConfig } from '@n8n/config';
import { Container, Service } from '@n8n/di';
import compression from 'compression';
import express from 'express';
import { engine as expressHandlebars } from 'express-handlebars';
@ -6,7 +7,6 @@ import { readFile } from 'fs/promises';
import type { Server } from 'http';
import isbot from 'isbot';
import { Logger } from 'n8n-core';
import { Container, Service } from 'typedi';
import config from '@/config';
import { N8N_VERSION, TEMPLATES_DIR, inDevelopment, inTest } from '@/constants';

View file

@ -1,4 +1,4 @@
import { Service } from 'typedi';
import { Service } from '@n8n/di';
import { CacheService } from '@/services/cache/cache.service';

View file

@ -1,3 +1,4 @@
import { Service } from '@n8n/di';
import { Logger } from 'n8n-core';
import type {
IDeferredPromise,
@ -9,7 +10,6 @@ import type {
import { createDeferredPromise, ExecutionCancelledError, sleep } from 'n8n-workflow';
import { strict as assert } from 'node:assert';
import type PCancelable from 'p-cancelable';
import { Service } from 'typedi';
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
import { ExecutionNotFoundError } from '@/errors/execution-not-found-error';

View file

@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { Service } from '@n8n/di';
import {
ActiveWorkflows,
ErrorReporter,
@ -28,7 +29,6 @@ import {
WebhookPathTakenError,
ApplicationError,
} from 'n8n-workflow';
import { Service } from 'typedi';
import { ActivationErrorsService } from '@/activation-errors.service';
import { ActiveExecutions } from '@/active-executions';

View file

@ -1,9 +1,9 @@
import { GlobalConfig } from '@n8n/config';
import { Container, Service } from '@n8n/di';
import { createHash } from 'crypto';
import type { NextFunction, Response } from 'express';
import { JsonWebTokenError, TokenExpiredError } from 'jsonwebtoken';
import { Logger } from 'n8n-core';
import Container, { Service } from 'typedi';
import config from '@/config';
import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES, Time } from '@/constants';

View file

@ -1,5 +1,5 @@
import { Container } from '@n8n/di';
import type { Response } from 'express';
import { Container } from 'typedi';
import type { User } from '@/databases/entities/user';

View file

@ -1,4 +1,4 @@
import { Container } from 'typedi';
import { Container } from '@n8n/di';
import type { User } from '@/databases/entities/user';
import { UserRepository } from '@/databases/repositories/user.repository';

View file

@ -1,4 +1,4 @@
import { Container } from 'typedi';
import { Container } from '@n8n/di';
import type { User } from '@/databases/entities/user';
import { EventService } from '@/events/event.service';

View file

@ -1,8 +1,8 @@
import type { PushPayload } from '@n8n/api-types';
import { Service } from '@n8n/di';
import { ErrorReporter } from 'n8n-core';
import type { Workflow } from 'n8n-workflow';
import { ApplicationError } from 'n8n-workflow';
import { Service } from 'typedi';
import { CollaborationState } from '@/collaboration/collaboration.state';
import type { User } from '@/databases/entities/user';

View file

@ -1,6 +1,6 @@
import type { Iso8601DateTimeString } from '@n8n/api-types';
import { Service } from '@n8n/di';
import type { Workflow } from 'n8n-workflow';
import { Service } from 'typedi';
import { Time } from '@/constants';
import type { User } from '@/databases/entities/user';

View file

@ -1,7 +1,7 @@
import { SecurityConfig } from '@n8n/config';
import { Container } from '@n8n/di';
import { Flags } from '@oclif/core';
import { ApplicationError } from 'n8n-workflow';
import { Container } from 'typedi';
import { RISK_CATEGORIES } from '@/security-audit/constants';
import { SecurityAuditService } from '@/security-audit/security-audit.service';

View file

@ -1,5 +1,6 @@
import 'reflect-metadata';
import { GlobalConfig } from '@n8n/config';
import { Container } from '@n8n/di';
import { Command, Errors } from '@oclif/core';
import {
BinaryDataService,
@ -10,7 +11,6 @@ import {
ErrorReporter,
} from 'n8n-core';
import { ApplicationError, ensureError, sleep } from 'n8n-workflow';
import { Container } from 'typedi';
import type { AbstractServer } from '@/abstract-server';
import config from '@/config';

View file

@ -1,10 +1,10 @@
import { Container } from '@n8n/di';
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
import type { DataSourceOptions as ConnectionOptions } from '@n8n/typeorm';
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
import { MigrationExecutor, DataSource as Connection } from '@n8n/typeorm';
import { Command, Flags } from '@oclif/core';
import { Logger } from 'n8n-core';
import { Container } from 'typedi';
import { getConnectionOptions } from '@/databases/config';
import type { Migration } from '@/databases/types';

View file

@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/no-loop-func */
import { Container } from '@n8n/di';
import { Flags } from '@oclif/core';
import fs from 'fs';
import { diff } from 'json-diff';
@ -7,7 +8,6 @@ import type { IRun, ITaskData, IWorkflowExecutionDataProcess } from 'n8n-workflo
import { ApplicationError, jsonParse } from 'n8n-workflow';
import os from 'os';
import { sep } from 'path';
import { Container } from 'typedi';
import { ActiveExecutions } from '@/active-executions';
import type { User } from '@/databases/entities/user';

View file

@ -1,7 +1,7 @@
import { Container } from '@n8n/di';
import { Flags } from '@oclif/core';
import type { IWorkflowBase, IWorkflowExecutionDataProcess } from 'n8n-workflow';
import { ApplicationError, ExecutionBaseError } from 'n8n-workflow';
import { Container } from 'typedi';
import { ActiveExecutions } from '@/active-executions';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';

View file

@ -1,9 +1,9 @@
import { Container } from '@n8n/di';
import { Flags } from '@oclif/core';
import fs from 'fs';
import { Credentials } from 'n8n-core';
import { ApplicationError } from 'n8n-workflow';
import path from 'path';
import Container from 'typedi';
import { CredentialsRepository } from '@/databases/repositories/credentials.repository';
import type { ICredentialsDb, ICredentialsDecryptedDb } from '@/interfaces';

View file

@ -1,8 +1,8 @@
import { Container } from '@n8n/di';
import { Flags } from '@oclif/core';
import fs from 'fs';
import { ApplicationError } from 'n8n-workflow';
import path from 'path';
import Container from 'typedi';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';

View file

@ -1,3 +1,4 @@
import { Container } from '@n8n/di';
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
import type { EntityManager } from '@n8n/typeorm';
import { Flags } from '@oclif/core';
@ -6,7 +7,6 @@ import fs from 'fs';
import { Cipher } from 'n8n-core';
import type { ICredentialsEncrypted } from 'n8n-workflow';
import { ApplicationError, jsonParse } from 'n8n-workflow';
import { Container } from 'typedi';
import { UM_FIX_INSTRUCTION } from '@/constants';
import { CredentialsEntity } from '@/databases/entities/credentials-entity';

View file

@ -1,8 +1,8 @@
import { Container } from '@n8n/di';
import { Flags } from '@oclif/core';
import glob from 'fast-glob';
import fs from 'fs';
import { ApplicationError, jsonParse } from 'n8n-workflow';
import { Container } from 'typedi';
import { UM_FIX_INSTRUCTION } from '@/constants';
import type { WorkflowEntity } from '@/databases/entities/workflow-entity';

View file

@ -1,8 +1,8 @@
import { Container } from '@n8n/di';
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
import { In } from '@n8n/typeorm';
import { Flags } from '@oclif/core';
import { ApplicationError } from 'n8n-workflow';
import Container from 'typedi';
import { UM_FIX_INSTRUCTION } from '@/constants';
import { CredentialsService } from '@/credentials/credentials.service';

View file

@ -1,4 +1,4 @@
import { Container } from 'typedi';
import { Container } from '@n8n/di';
import { SETTINGS_LICENSE_CERT_KEY } from '@/constants';
import { SettingsRepository } from '@/databases/repositories/settings.repository';

View file

@ -1,4 +1,4 @@
import { Container } from 'typedi';
import { Container } from '@n8n/di';
import { License } from '@/license';

View file

@ -1,5 +1,5 @@
import { Container } from '@n8n/di';
import { Flags } from '@oclif/core';
import Container from 'typedi';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';

View file

@ -1,5 +1,5 @@
import { Container } from '@n8n/di';
import { Flags } from '@oclif/core';
import Container from 'typedi';
import { AuthUserRepository } from '@/databases/repositories/auth-user.repository';

View file

@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { GlobalConfig } from '@n8n/config';
import { Container } from '@n8n/di';
import { Flags } from '@oclif/core';
import glob from 'fast-glob';
import { createReadStream, createWriteStream, existsSync } from 'fs';
@ -9,7 +10,6 @@ import { jsonParse, randomString, type IWorkflowExecutionDataProcess } from 'n8n
import path from 'path';
import replaceStream from 'replacestream';
import { pipeline } from 'stream/promises';
import { Container } from 'typedi';
import { ActiveExecutions } from '@/active-executions';
import { ActiveWorkflowManager } from '@/active-workflow-manager';

View file

@ -1,5 +1,5 @@
import { Container } from '@n8n/di';
import { Flags } from '@oclif/core';
import { Container } from 'typedi';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';

View file

@ -1,4 +1,4 @@
import { Container } from 'typedi';
import { Container } from '@n8n/di';
import type { CredentialsEntity } from '@/databases/entities/credentials-entity';
import { User } from '@/databases/entities/user';

View file

@ -1,6 +1,6 @@
import { Container } from '@n8n/di';
import { Flags } from '@oclif/core';
import { ApplicationError } from 'n8n-workflow';
import { Container } from 'typedi';
import { ActiveExecutions } from '@/active-executions';
import config from '@/config';

View file

@ -1,5 +1,5 @@
import { Container } from '@n8n/di';
import { Flags, type Config } from '@oclif/core';
import { Container } from 'typedi';
import config from '@/config';
import { N8N_VERSION, inTest } from '@/constants';

View file

@ -1,6 +1,6 @@
import { Service } from '@n8n/di';
import { Logger } from 'n8n-core';
import type { WorkflowExecuteMode as ExecutionMode } from 'n8n-workflow';
import { Service } from 'typedi';
import config from '@/config';
import { ExecutionRepository } from '@/databases/repositories/execution.repository';

View file

@ -1,4 +1,4 @@
import { Service } from 'typedi';
import { Service } from '@n8n/di';
import { TypedEmitter } from '@/typed-emitter';

View file

@ -1,4 +1,5 @@
import { GlobalConfig } from '@n8n/config';
import { Container } from '@n8n/di';
import convict from 'convict';
import { flatten } from 'flat';
import { readFileSync } from 'fs';
@ -7,7 +8,6 @@ import { Logger } from 'n8n-core';
import { ApplicationError, setGlobalState } from 'n8n-workflow';
import assert from 'node:assert';
import colors from 'picocolors';
import { Container } from 'typedi';
import { inTest, inE2ETests } from '@/constants';

View file

@ -1,8 +1,8 @@
import { GlobalConfig } from '@n8n/config';
import { Container } from '@n8n/di';
import convict from 'convict';
import { InstanceSettings } from 'n8n-core';
import path from 'path';
import { Container } from 'typedi';
import { ensureStringArray } from './utils';

View file

@ -1,5 +1,5 @@
import { Container } from '@n8n/di';
import { mock } from 'jest-mock-extended';
import { Container } from 'typedi';
import type { ApiKey } from '@/databases/entities/api-key';
import type { User } from '@/databases/entities/user';

View file

@ -1,8 +1,8 @@
import { UserUpdateRequestDto } from '@n8n/api-types';
import { Container } from '@n8n/di';
import type { Response } from 'express';
import { mock, anyObject } from 'jest-mock-extended';
import jwt from 'jsonwebtoken';
import { Container } from 'typedi';
import { AUTH_COOKIE_NAME } from '@/constants';
import { MeController } from '@/controllers/me.controller';

View file

@ -1,7 +1,7 @@
import type { PushMessage } from '@n8n/api-types';
import { Container } from '@n8n/di';
import { Request } from 'express';
import { Logger } from 'n8n-core';
import Container from 'typedi';
import { v4 as uuid } from 'uuid';
import { ActiveWorkflowManager } from '@/active-workflow-manager';

View file

@ -1,10 +1,10 @@
import { Container } from '@n8n/di';
import Csrf from 'csrf';
import type { Response } from 'express';
import { mock } from 'jest-mock-extended';
import { Cipher } from 'n8n-core';
import { Logger } from 'n8n-core';
import nock from 'nock';
import Container from 'typedi';
import { Time } from '@/constants';
import { OAuth1CredentialController } from '@/controllers/oauth/oauth1-credential.controller';

View file

@ -1,10 +1,10 @@
import { Container } from '@n8n/di';
import Csrf from 'csrf';
import { type Response } from 'express';
import { mock } from 'jest-mock-extended';
import { Cipher } from 'n8n-core';
import { Logger } from 'n8n-core';
import nock from 'nock';
import Container from 'typedi';
import { Time } from '@/constants';
import { OAuth2CredentialController } from '@/controllers/oauth/oauth2-credential.controller';

View file

@ -1,10 +1,10 @@
import { GlobalConfig } from '@n8n/config';
import { Service } from '@n8n/di';
import Csrf from 'csrf';
import type { Response } from 'express';
import { Credentials, Logger } from 'n8n-core';
import type { ICredentialDataDecryptedObject, IWorkflowExecuteAdditionalData } from 'n8n-workflow';
import { jsonParse, ApplicationError } from 'n8n-workflow';
import { Service } from 'typedi';
import { RESPONSE_ERROR_MESSAGES, Time } from '@/constants';
import { CredentialsHelper } from '@/credentials-helper';

View file

@ -1,9 +1,9 @@
import { Container } from '@n8n/di';
import { existsSync } from 'fs';
import { mkdir, utimes, open, rm } from 'fs/promises';
import { InstanceSettings, Logger } from 'n8n-core';
import { sleep } from 'n8n-workflow';
import { join, dirname } from 'path';
import { Container } from 'typedi';
import { inProduction } from '@/constants';

View file

@ -1,5 +1,5 @@
import { Service } from '@n8n/di';
import type { ICredentialType, ICredentialTypes } from 'n8n-workflow';
import { Service } from 'typedi';
import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials';

View file

@ -2,6 +2,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-return */
import { Service } from '@n8n/di';
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
import { In } from '@n8n/typeorm';
import { Credentials, getAdditionalKeys } from 'n8n-core';
@ -26,7 +27,6 @@ import type {
IDataObject,
} from 'n8n-workflow';
import { ICredentialsHelper, NodeHelpers, Workflow, ApplicationError } from 'n8n-workflow';
import { Service } from 'typedi';
import { CredentialTypes } from '@/credential-types';
import { CredentialsOverwrites } from '@/credentials-overwrites';

View file

@ -1,8 +1,8 @@
import { GlobalConfig } from '@n8n/config';
import { Service } from '@n8n/di';
import { Logger } from 'n8n-core';
import type { ICredentialDataDecryptedObject } from 'n8n-workflow';
import { deepCopy, jsonParse } from 'n8n-workflow';
import { Service } from 'typedi';
import { CredentialTypes } from '@/credential-types';
import type { ICredentialsOverwrite } from '@/interfaces';

View file

@ -1,7 +1,7 @@
import { Service } from '@n8n/di';
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
import { In, type EntityManager } from '@n8n/typeorm';
import type { ICredentialDataDecryptedObject } from 'n8n-workflow';
import { Service } from 'typedi';
import type { CredentialsEntity } from '@/databases/entities/credentials-entity';
import { Project } from '@/databases/entities/project';

View file

@ -1,3 +1,4 @@
import { Service } from '@n8n/di';
import type { Scope } from '@n8n/permissions';
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
import {
@ -14,7 +15,6 @@ import type {
INodeProperties,
} from 'n8n-workflow';
import { ApplicationError, CREDENTIAL_EMPTY_VALUE, deepCopy, NodeHelpers } from 'n8n-workflow';
import { Service } from 'typedi';
import { CREDENTIAL_BLANKING_VALUE } from '@/constants';
import { CredentialTypes } from '@/credential-types';

View file

@ -1,4 +1,5 @@
import { GlobalConfig } from '@n8n/config';
import { Container } from '@n8n/di';
import type { DataSourceOptions, LoggerOptions } from '@n8n/typeorm';
import type { MysqlConnectionOptions } from '@n8n/typeorm/driver/mysql/MysqlConnectionOptions';
import type { PostgresConnectionOptions } from '@n8n/typeorm/driver/postgres/PostgresConnectionOptions';
@ -8,7 +9,6 @@ import { InstanceSettings } from 'n8n-core';
import { ApplicationError } from 'n8n-workflow';
import path from 'path';
import type { TlsOptions } from 'tls';
import { Container } from 'typedi';
import { entities } from './entities';
import { mysqlMigrations } from './migrations/mysqldb';

View file

@ -1,4 +1,5 @@
import { GlobalConfig } from '@n8n/config';
import { Container } from '@n8n/di';
import type { ColumnOptions } from '@n8n/typeorm';
import {
BeforeInsert,
@ -8,7 +9,6 @@ import {
UpdateDateColumn,
} from '@n8n/typeorm';
import type { Class } from 'n8n-core';
import { Container } from 'typedi';
import { generateNanoId } from '../utils/generators';

View file

@ -1,8 +1,8 @@
import { Container } from '@n8n/di';
import { Cipher, InstanceSettings } from 'n8n-core';
import { jsonParse } from 'n8n-workflow';
import { readFile, writeFile, rm } from 'node:fs/promises';
import path from 'node:path';
import Container from 'typedi';
import type { MigrationContext, ReversibleMigration } from '@/databases/types';

View file

@ -1,8 +1,8 @@
import { GlobalConfig } from '@n8n/config';
import { Container } from '@n8n/di';
import { statSync } from 'fs';
import { InstanceSettings } from 'n8n-core';
import path from 'path';
import { Container } from 'typedi';
import type { MigrationContext, IrreversibleMigration } from '@/databases/types';

View file

@ -1,5 +1,5 @@
import { Container } from '@n8n/di';
import { mock } from 'jest-mock-extended';
import { Container } from 'typedi';
import { CredentialsEntity } from '@/databases/entities/credentials-entity';
import { mockEntityManager } from '@test/mocking';

View file

@ -1,10 +1,10 @@
import { GlobalConfig } from '@n8n/config';
import { Container } from '@n8n/di';
import type { SelectQueryBuilder } from '@n8n/typeorm';
import { Not, LessThanOrEqual } from '@n8n/typeorm';
import { mock } from 'jest-mock-extended';
import { BinaryDataService } from 'n8n-core';
import { nanoid } from 'nanoid';
import Container from 'typedi';
import { ExecutionEntity } from '@/databases/entities/execution-entity';
import { ExecutionRepository } from '@/databases/repositories/execution.repository';

View file

@ -1,7 +1,7 @@
import { Container } from '@n8n/di';
import { hasScope } from '@n8n/permissions';
import { In } from '@n8n/typeorm';
import { mock } from 'jest-mock-extended';
import { Container } from 'typedi';
import type { CredentialsEntity } from '@/databases/entities/credentials-entity';
import { SharedCredentials } from '@/databases/entities/shared-credentials';

View file

@ -1,6 +1,6 @@
import { Container } from '@n8n/di';
import { type InsertResult, QueryFailedError } from '@n8n/typeorm';
import { mock, mockClear } from 'jest-mock-extended';
import { Container } from 'typedi';
import { StatisticsNames, WorkflowStatistics } from '@/databases/entities/workflow-statistics';
import { WorkflowStatisticsRepository } from '@/databases/repositories/workflow-statistics.repository';

View file

@ -1,5 +1,5 @@
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { AnnotationTagMapping } from '@/databases/entities/annotation-tag-mapping.ee';

View file

@ -1,5 +1,5 @@
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.ee';

View file

@ -1,5 +1,5 @@
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { ApiKey } from '../entities/api-key';

View file

@ -1,5 +1,5 @@
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { AuthIdentity } from '../entities/auth-identity';

View file

@ -1,5 +1,5 @@
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { AuthProviderSyncHistory } from '../entities/auth-provider-sync-history';

View file

@ -1,5 +1,5 @@
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { AuthUser } from '../entities/auth-user';

View file

@ -1,7 +1,7 @@
import { Service } from '@n8n/di';
import type { Scope } from '@n8n/permissions';
import { DataSource, In, Repository, Like } from '@n8n/typeorm';
import type { FindManyOptions, FindOptionsWhere } from '@n8n/typeorm';
import { Service } from 'typedi';
import type { ListQuery } from '@/requests';
import { RoleService } from '@/services/role.service';

View file

@ -1,5 +1,5 @@
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { EventDestinations } from '../entities/event-destinations';

View file

@ -1,5 +1,5 @@
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { ExecutionAnnotation } from '@/databases/entities/execution-annotation.ee';

View file

@ -1,7 +1,7 @@
import { Service } from '@n8n/di';
import { DataSource, In, Repository } from '@n8n/typeorm';
import type { EntityManager } from '@n8n/typeorm';
import type { QueryDeepPartialEntity } from '@n8n/typeorm/query-builder/QueryPartialEntity';
import { Service } from 'typedi';
import { ExecutionData } from '../entities/execution-data';

View file

@ -1,5 +1,5 @@
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { ExecutionMetadata } from '../entities/execution-metadata';

View file

@ -1,4 +1,5 @@
import { GlobalConfig } from '@n8n/config';
import { Service } from '@n8n/di';
import type {
FindManyOptions,
FindOneOptions,
@ -29,7 +30,6 @@ import type {
ExecutionSummary,
IRunExecutionData,
} from 'n8n-workflow';
import { Service } from 'typedi';
import { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.ee';
import { AnnotationTagMapping } from '@/databases/entities/annotation-tag-mapping.ee';

View file

@ -1,5 +1,5 @@
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { InstalledNodes } from '../entities/installed-nodes';

View file

@ -1,6 +1,6 @@
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import type { PackageDirectoryLoader } from 'n8n-core';
import { Service } from 'typedi';
import { InstalledNodesRepository } from './installed-nodes.repository';
import { InstalledPackages } from '../entities/installed-packages';

View file

@ -1,5 +1,5 @@
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { InvalidAuthToken } from '../entities/invalid-auth-token';

View file

@ -1,6 +1,6 @@
import { GlobalConfig } from '@n8n/config';
import { Service } from '@n8n/di';
import { DataSource, Repository, Entity } from '@n8n/typeorm';
import { Service } from 'typedi';
@Entity()
export class LicenseMetrics {}

View file

@ -1,5 +1,5 @@
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { ProcessedData } from '../entities/processed-data';

View file

@ -1,5 +1,5 @@
import { Service } from '@n8n/di';
import { DataSource, In, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { ProjectRelation, type ProjectRole } from '../entities/project-relation';

View file

@ -1,6 +1,6 @@
import { Service } from '@n8n/di';
import type { EntityManager } from '@n8n/typeorm';
import { DataSource, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { Project } from '../entities/project';

View file

@ -1,6 +1,6 @@
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { ErrorReporter } from 'n8n-core';
import { Service } from 'typedi';
import config from '@/config';
import { EXTERNAL_SECRETS_DB_KEY } from '@/external-secrets.ee/constants';

View file

@ -1,7 +1,7 @@
import { Service } from '@n8n/di';
import type { Scope } from '@n8n/permissions';
import type { EntityManager, FindOptionsRelations, FindOptionsWhere } from '@n8n/typeorm';
import { DataSource, In, Not, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { RoleService } from '@/services/role.service';

View file

@ -1,7 +1,7 @@
import { Service } from '@n8n/di';
import type { Scope } from '@n8n/permissions';
import { DataSource, Repository, In, Not } from '@n8n/typeorm';
import type { EntityManager, FindManyOptions, FindOptionsWhere } from '@n8n/typeorm';
import { Service } from 'typedi';
import { RoleService } from '@/services/role.service';

View file

@ -1,7 +1,7 @@
import { Service } from '@n8n/di';
import type { EntityManager } from '@n8n/typeorm';
import { DataSource, In, Repository } from '@n8n/typeorm';
import intersection from 'lodash/intersection';
import { Service } from 'typedi';
import { TagEntity } from '../entities/tag-entity';
import type { WorkflowEntity } from '../entities/workflow-entity';

View file

@ -1,6 +1,6 @@
import { Service } from '@n8n/di';
import type { FindManyOptions, FindOptionsWhere } from '@n8n/typeorm';
import { DataSource, In, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { TestDefinition } from '@/databases/entities/test-definition.ee';
import { ForbiddenError } from '@/errors/response-errors/forbidden.error';

View file

@ -1,5 +1,5 @@
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { TestMetric } from '@/databases/entities/test-metric.ee';

View file

@ -1,6 +1,6 @@
import { Service } from '@n8n/di';
import type { FindManyOptions } from '@n8n/typeorm';
import { DataSource, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import type { AggregatedTestRunMetrics } from '@/databases/entities/test-run.ee';
import { TestRun } from '@/databases/entities/test-run.ee';

View file

@ -1,6 +1,6 @@
import { Service } from '@n8n/di';
import type { DeepPartial, EntityManager, FindManyOptions } from '@n8n/typeorm';
import { DataSource, In, IsNull, Not, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import type { ListQuery } from '@/requests';

View file

@ -1,5 +1,5 @@
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { Service } from 'typedi';
import { Variables } from '../entities/variables';

Some files were not shown because too many files have changed in this diff Show more