diff --git a/packages/@n8n/backend-common/package.json b/packages/@n8n/backend-common/package.json index 68f51cbd93..609e6bf5a4 100644 --- a/packages/@n8n/backend-common/package.json +++ b/packages/@n8n/backend-common/package.json @@ -23,6 +23,7 @@ "dependencies": { "@n8n/config": "workspace:^", "@n8n/constants": "workspace:^", + "@n8n/decorators": "workspace:^", "@n8n/di": "workspace:^", "callsites": "catalog:", "n8n-workflow": "workspace:^", diff --git a/packages/@n8n/backend-common/src/index.ts b/packages/@n8n/backend-common/src/index.ts index f198301b04..6f3f9092af 100644 --- a/packages/@n8n/backend-common/src/index.ts +++ b/packages/@n8n/backend-common/src/index.ts @@ -4,3 +4,5 @@ export * from './types'; export { inDevelopment, inProduction, inTest } from './environment'; export { isObjectLiteral } from './utils/is-object-literal'; export { Logger } from './logging/logger'; +export { ModuleRegistry } from './modules/module-registry'; +export { ModulesConfig, ModuleName } from './modules/modules.config'; diff --git a/packages/cli/src/modules/__tests__/module-registry.test.ts b/packages/@n8n/backend-common/src/modules/__tests__/module-registry.test.ts similarity index 93% rename from packages/cli/src/modules/__tests__/module-registry.test.ts rename to packages/@n8n/backend-common/src/modules/__tests__/module-registry.test.ts index 5c00316d39..f4109d9027 100644 --- a/packages/cli/src/modules/__tests__/module-registry.test.ts +++ b/packages/@n8n/backend-common/src/modules/__tests__/module-registry.test.ts @@ -1,8 +1,8 @@ -import type { LicenseState } from '@n8n/backend-common'; import type { ModuleInterface, ModuleMetadata } from '@n8n/decorators'; import { Container } from '@n8n/di'; import { mock } from 'jest-mock-extended'; +import type { LicenseState } from '../../license-state'; import { ModuleConfusionError } from '../errors/module-confusion.error'; import { ModuleRegistry } from '../module-registry'; import { MODULE_NAMES } from '../modules.config'; @@ -43,7 +43,7 @@ describe('loadModules', () => { }); Container.get = jest.fn().mockReturnValue(ModuleClass); - const moduleRegistry = new ModuleRegistry(moduleMetadata, mock(), mock(), mock(), mock()); + const moduleRegistry = new ModuleRegistry(moduleMetadata, mock(), mock(), mock()); await moduleRegistry.loadModules([]); @@ -57,7 +57,7 @@ describe('loadModules', () => { }); Container.get = jest.fn().mockReturnValue(ModuleClass); - const moduleRegistry = new ModuleRegistry(moduleMetadata, mock(), mock(), mock(), mock()); + const moduleRegistry = new ModuleRegistry(moduleMetadata, mock(), mock(), mock()); await moduleRegistry.loadModules([]); @@ -75,7 +75,7 @@ describe('initModules', () => { }); Container.get = jest.fn().mockReturnValue(ModuleClass); - const moduleRegistry = new ModuleRegistry(moduleMetadata, mock(), mock(), mock(), mock()); + const moduleRegistry = new ModuleRegistry(moduleMetadata, mock(), mock(), mock()); await moduleRegistry.initModules(); @@ -94,7 +94,7 @@ describe('initModules', () => { const licenseState = mock({ isLicensed: jest.fn().mockReturnValue(true) }); Container.get = jest.fn().mockReturnValue(ModuleClass); - const moduleRegistry = new ModuleRegistry(moduleMetadata, mock(), licenseState, mock(), mock()); + const moduleRegistry = new ModuleRegistry(moduleMetadata, licenseState, mock(), mock()); await moduleRegistry.initModules(); @@ -113,7 +113,7 @@ describe('initModules', () => { const licenseState = mock({ isLicensed: jest.fn().mockReturnValue(false) }); Container.get = jest.fn().mockReturnValue(ModuleClass); - const moduleRegistry = new ModuleRegistry(moduleMetadata, mock(), licenseState, mock(), mock()); + const moduleRegistry = new ModuleRegistry(moduleMetadata, licenseState, mock(), mock()); await moduleRegistry.initModules(); @@ -130,7 +130,7 @@ describe('initModules', () => { Container.get = jest.fn().mockReturnValue(ModuleClass); - const moduleRegistry = new ModuleRegistry(moduleMetadata, mock(), mock(), mock(), mock()); + const moduleRegistry = new ModuleRegistry(moduleMetadata, mock(), mock(), mock()); await moduleRegistry.initModules(); @@ -150,7 +150,7 @@ describe('initModules', () => { }); Container.get = jest.fn().mockReturnValue(ModuleClass); - const moduleRegistry = new ModuleRegistry(moduleMetadata, mock(), mock(), mock(), mock()); + const moduleRegistry = new ModuleRegistry(moduleMetadata, mock(), mock(), mock()); // ACT await moduleRegistry.initModules(); @@ -174,12 +174,13 @@ describe('initModules', () => { }); Container.get = jest.fn().mockReturnValue(ModuleClass); - const moduleRegistry = new ModuleRegistry(moduleMetadata, mock(), mock(), mock(), mock()); + const moduleRegistry = new ModuleRegistry(moduleMetadata, mock(), mock(), mock()); // ACT await moduleRegistry.initModules(); // ASSERT + // eslint-disable-next-line @typescript-eslint/no-explicit-any expect(moduleRegistry.isActive(moduleName as any)).toBe(true); expect(moduleRegistry.getActiveModules()).toEqual([moduleName]); }); diff --git a/packages/cli/src/modules/__tests__/modules.config.test.ts b/packages/@n8n/backend-common/src/modules/__tests__/modules.config.test.ts similarity index 100% rename from packages/cli/src/modules/__tests__/modules.config.test.ts rename to packages/@n8n/backend-common/src/modules/__tests__/modules.config.test.ts diff --git a/packages/cli/src/modules/errors/missing-module.error.ts b/packages/@n8n/backend-common/src/modules/errors/missing-module.error.ts similarity index 100% rename from packages/cli/src/modules/errors/missing-module.error.ts rename to packages/@n8n/backend-common/src/modules/errors/missing-module.error.ts diff --git a/packages/cli/src/modules/errors/module-confusion.error.ts b/packages/@n8n/backend-common/src/modules/errors/module-confusion.error.ts similarity index 100% rename from packages/cli/src/modules/errors/module-confusion.error.ts rename to packages/@n8n/backend-common/src/modules/errors/module-confusion.error.ts diff --git a/packages/cli/src/modules/errors/unknown-module.error.ts b/packages/@n8n/backend-common/src/modules/errors/unknown-module.error.ts similarity index 100% rename from packages/cli/src/modules/errors/unknown-module.error.ts rename to packages/@n8n/backend-common/src/modules/errors/unknown-module.error.ts diff --git a/packages/@n8n/backend-common/src/modules/module-registry.ts b/packages/@n8n/backend-common/src/modules/module-registry.ts new file mode 100644 index 0000000000..bc94552e9b --- /dev/null +++ b/packages/@n8n/backend-common/src/modules/module-registry.ts @@ -0,0 +1,113 @@ +import { ModuleMetadata } from '@n8n/decorators'; +import type { EntityClass, ModuleSettings } from '@n8n/decorators'; +import { Container, Service } from '@n8n/di'; +import path from 'path'; + +import { MissingModuleError } from './errors/missing-module.error'; +import { ModuleConfusionError } from './errors/module-confusion.error'; +import { ModulesConfig } from './modules.config'; +import type { ModuleName } from './modules.config'; +import { LicenseState } from '../license-state'; +import { Logger } from '../logging/logger'; + +@Service() +export class ModuleRegistry { + readonly entities: EntityClass[] = []; + + readonly settings: Map = new Map(); + + constructor( + private readonly moduleMetadata: ModuleMetadata, + private readonly licenseState: LicenseState, + private readonly logger: Logger, + private readonly modulesConfig: ModulesConfig, + ) {} + + private readonly defaultModules: ModuleName[] = ['insights', 'external-secrets']; + + private readonly activeModules: string[] = []; + + get eligibleModules(): ModuleName[] { + const { enabledModules, disabledModules } = this.modulesConfig; + + const doubleListed = enabledModules.filter((m) => disabledModules.includes(m)); + + if (doubleListed.length > 0) throw new ModuleConfusionError(doubleListed); + + const defaultPlusEnabled = [...new Set([...this.defaultModules, ...enabledModules])]; + + return defaultPlusEnabled.filter((m) => !disabledModules.includes(m)); + } + + /** + * Loads [module name].module.ts for each eligible module. + * This only registers the database entities for module and should be done + * before instantiating the datasource. + * + * This will not register routes or do any other kind of module related + * setup. + */ + async loadModules(modules?: ModuleName[]) { + const moduleDir = process.env.NODE_ENV === 'test' ? 'src' : 'dist'; + const modulesDir = path.resolve(__dirname, `../../../../cli/${moduleDir}/modules`); + + for (const moduleName of modules ?? this.eligibleModules) { + try { + await import(`${modulesDir}/${moduleName}/${moduleName}.module`); + } catch { + try { + await import(`${modulesDir}/${moduleName}.ee/${moduleName}.module`); + } catch (error) { + throw new MissingModuleError(moduleName, error instanceof Error ? error.message : ''); + } + } + } + + for (const ModuleClass of this.moduleMetadata.getClasses()) { + const entities = Container.get(ModuleClass).entities?.(); + + if (!entities || entities.length === 0) continue; + + this.entities.push(...entities); + } + } + + /** + * Calls `init` on each eligible module. + * + * This will do things like registering routes, setup timers or other module + * specific setup. + * + * `ModuleRegistry.loadModules` must have been called before. + */ + async initModules() { + for (const [moduleName, moduleEntry] of this.moduleMetadata.getEntries()) { + const { licenseFlag, class: ModuleClass } = moduleEntry; + + if (licenseFlag && !this.licenseState.isLicensed(licenseFlag)) { + this.logger.debug(`Skipped init for unlicensed module "${moduleName}"`); + continue; + } + + await Container.get(ModuleClass).init?.(); + + const moduleSettings = await Container.get(ModuleClass).settings?.(); + + if (!moduleSettings) continue; + + this.settings.set(moduleName, moduleSettings); + + this.logger.debug(`Initialized module "${moduleName}"`); + + this.activeModules.push(moduleName); + } + } + + isActive(moduleName: ModuleName) { + return this.activeModules.includes(moduleName); + } + + getActiveModules() { + return this.activeModules; + } +} diff --git a/packages/cli/src/modules/modules.config.ts b/packages/@n8n/backend-common/src/modules/modules.config.ts similarity index 100% rename from packages/cli/src/modules/modules.config.ts rename to packages/@n8n/backend-common/src/modules/modules.config.ts diff --git a/packages/cli/src/commands/base-command.ts b/packages/cli/src/commands/base-command.ts index 4e24757fbf..4226b75923 100644 --- a/packages/cli/src/commands/base-command.ts +++ b/packages/cli/src/commands/base-command.ts @@ -1,5 +1,12 @@ import 'reflect-metadata'; -import { inDevelopment, inTest, LicenseState, Logger } from '@n8n/backend-common'; +import { + inDevelopment, + inTest, + LicenseState, + Logger, + ModuleRegistry, + ModulesConfig, +} from '@n8n/backend-common'; import { GlobalConfig } from '@n8n/config'; import { LICENSE_FEATURES } from '@n8n/constants'; import { Container } from '@n8n/di'; @@ -27,8 +34,6 @@ import { TelemetryEventRelay } from '@/events/relays/telemetry.event-relay'; import { ExternalHooks } from '@/external-hooks'; import { License } from '@/license'; import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials'; -import { ModuleRegistry } from '@/modules/module-registry'; -import { ModulesConfig } from '@/modules/modules.config'; import { NodeTypes } from '@/node-types'; import { PostHogClient } from '@/posthog'; import { ShutdownService } from '@/shutdown/shutdown.service'; diff --git a/packages/cli/src/databases/__tests__/db-connection-options.test.ts b/packages/cli/src/databases/__tests__/db-connection-options.test.ts index 3e14b302ca..d41b9dbe47 100644 --- a/packages/cli/src/databases/__tests__/db-connection-options.test.ts +++ b/packages/cli/src/databases/__tests__/db-connection-options.test.ts @@ -1,3 +1,4 @@ +import type { ModuleRegistry } from '@n8n/backend-common'; import type { GlobalConfig, InstanceSettingsConfig } from '@n8n/config'; import { mysqlMigrations } from '@n8n/db'; import { postgresMigrations } from '@n8n/db'; @@ -5,8 +6,6 @@ import { sqliteMigrations } from '@n8n/db'; import { mock } from 'jest-mock-extended'; import path from 'path'; -import type { ModuleRegistry } from '@/modules/module-registry'; - import { DbConnectionOptions } from '../db-connection-options'; describe('DbConnectionOptions', () => { diff --git a/packages/cli/src/databases/db-connection-options.ts b/packages/cli/src/databases/db-connection-options.ts index e820ec643e..b9b6aeef01 100644 --- a/packages/cli/src/databases/db-connection-options.ts +++ b/packages/cli/src/databases/db-connection-options.ts @@ -1,3 +1,4 @@ +import { ModuleRegistry } from '@n8n/backend-common'; import { DatabaseConfig, InstanceSettingsConfig } from '@n8n/config'; import { entities, @@ -16,8 +17,6 @@ import { UserError } from 'n8n-workflow'; import path from 'path'; import type { TlsOptions } from 'tls'; -import { ModuleRegistry } from '@/modules/module-registry'; - @Service() export class DbConnectionOptions { constructor( diff --git a/packages/cli/src/execution-lifecycle/execution-lifecycle-hooks.ts b/packages/cli/src/execution-lifecycle/execution-lifecycle-hooks.ts index 76548b48b9..cfff804965 100644 --- a/packages/cli/src/execution-lifecycle/execution-lifecycle-hooks.ts +++ b/packages/cli/src/execution-lifecycle/execution-lifecycle-hooks.ts @@ -1,6 +1,7 @@ import { Logger } from '@n8n/backend-common'; import { ExecutionRepository } from '@n8n/db'; -import { Container } from '@n8n/di'; +import { LifecycleMetadata } from '@n8n/decorators'; +import { Container, Service } from '@n8n/di'; import { stringify } from 'flatted'; import { ErrorReporter, InstanceSettings, ExecutionLifecycleHooks } from 'n8n-core'; import type { @@ -11,7 +12,6 @@ import type { import { EventService } from '@/events/event.service'; import { ExternalHooks } from '@/external-hooks'; -import { ModuleRegistry } from '@/modules/module-registry'; import { Push } from '@/push'; import { WorkflowStatisticsService } from '@/services/workflow-statistics.service'; import { isWorkflowIdValid } from '@/utils'; @@ -28,6 +28,72 @@ import { } from './shared/shared-hook-functions'; import { type ExecutionSaveSettings, toSaveSettings } from './to-save-settings'; +@Service() +class ModulesHooksRegistry { + addHooks(hooks: ExecutionLifecycleHooks) { + const handlers = Container.get(LifecycleMetadata).getHandlers(); + + for (const { handlerClass, methodName, eventName } of handlers) { + const instance = Container.get(handlerClass); + + switch (eventName) { + case 'workflowExecuteAfter': + hooks.addHandler(eventName, async function (runData, newStaticData) { + const context = { + type: 'workflowExecuteAfter', + workflow: this.workflowData, + runData, + newStaticData, + }; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/return-await + return await instance[methodName].call(instance, context); + }); + break; + + case 'nodeExecuteBefore': + hooks.addHandler(eventName, async function (nodeName, taskData) { + const context = { + type: 'nodeExecuteBefore', + workflow: this.workflowData, + nodeName, + taskData, + }; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/return-await + return await instance[methodName].call(instance, context); + }); + break; + + case 'nodeExecuteAfter': + hooks.addHandler(eventName, async function (nodeName, taskData, executionData) { + const context = { + type: 'nodeExecuteAfter', + workflow: this.workflowData, + nodeName, + taskData, + executionData, + }; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/return-await + return await instance[methodName].call(instance, context); + }); + break; + + case 'workflowExecuteBefore': + hooks.addHandler(eventName, async function (workflowInstance, executionData) { + const context = { + type: 'workflowExecuteBefore', + workflow: this.workflowData, + workflowInstance, + executionData, + }; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/return-await + return await instance[methodName].call(instance, context); + }); + break; + } + } + } +} + type HooksSetupParameters = { saveSettings: ExecutionSaveSettings; pushRef?: string; @@ -425,7 +491,7 @@ export function getLifecycleHooksForScalingWorker( hookFunctionsPush(hooks, optionalParameters); } - Container.get(ModuleRegistry).registerLifecycleHooks(hooks); + Container.get(ModulesHooksRegistry).addHooks(hooks); return hooks; } @@ -487,7 +553,7 @@ export function getLifecycleHooksForScalingMain( hooks.handlers.nodeExecuteBefore = []; hooks.handlers.nodeExecuteAfter = []; - Container.get(ModuleRegistry).registerLifecycleHooks(hooks); + Container.get(ModulesHooksRegistry).addHooks(hooks); return hooks; } @@ -511,6 +577,6 @@ export function getLifecycleHooksForRegularMain( hookFunctionsSaveProgress(hooks, optionalParameters); hookFunctionsStatistics(hooks); hookFunctionsExternalHooks(hooks); - Container.get(ModuleRegistry).registerLifecycleHooks(hooks); + Container.get(ModulesHooksRegistry).addHooks(hooks); return hooks; } diff --git a/packages/cli/src/modules/module-registry.ts b/packages/cli/src/modules/module-registry.ts deleted file mode 100644 index e61fceae3e..0000000000 --- a/packages/cli/src/modules/module-registry.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { LicenseState, Logger } from '@n8n/backend-common'; -import { LifecycleMetadata, ModuleMetadata } from '@n8n/decorators'; -import type { LifecycleContext, EntityClass, ModuleSettings } from '@n8n/decorators'; -import { Container, Service } from '@n8n/di'; -import type { ExecutionLifecycleHooks } from 'n8n-core'; -import type { - IDataObject, - IRun, - IRunExecutionData, - ITaskData, - ITaskStartedData, - IWorkflowBase, - Workflow, -} from 'n8n-workflow'; - -import { MissingModuleError } from './errors/missing-module.error'; -import { ModuleConfusionError } from './errors/module-confusion.error'; -import { ModulesConfig } from './modules.config'; -import type { ModuleName } from './modules.config'; - -@Service() -export class ModuleRegistry { - readonly entities: EntityClass[] = []; - - readonly settings: Map = new Map(); - - constructor( - private readonly moduleMetadata: ModuleMetadata, - private readonly lifecycleMetadata: LifecycleMetadata, - private readonly licenseState: LicenseState, - private readonly logger: Logger, - private readonly modulesConfig: ModulesConfig, - ) {} - - private readonly defaultModules: ModuleName[] = ['insights', 'external-secrets']; - - private readonly activeModules: string[] = []; - - get eligibleModules(): ModuleName[] { - const { enabledModules, disabledModules } = this.modulesConfig; - - const doubleListed = enabledModules.filter((m) => disabledModules.includes(m)); - - if (doubleListed.length > 0) throw new ModuleConfusionError(doubleListed); - - const defaultPlusEnabled = [...new Set([...this.defaultModules, ...enabledModules])]; - - return defaultPlusEnabled.filter((m) => !disabledModules.includes(m)); - } - - /** - * Loads [module name].module.ts for each eligible module. - * This only registers the database entities for module and should be done - * before instantiating the datasource. - * - * This will not register routes or do any other kind of module related - * setup. - */ - async loadModules(modules?: ModuleName[]) { - for (const moduleName of modules ?? this.eligibleModules) { - try { - await import(`../modules/${moduleName}/${moduleName}.module`); - } catch { - try { - await import(`../modules/${moduleName}.ee/${moduleName}.module`); - } catch (error) { - throw new MissingModuleError(moduleName, error instanceof Error ? error.message : ''); - } - } - } - - for (const ModuleClass of this.moduleMetadata.getClasses()) { - const entities = Container.get(ModuleClass).entities?.(); - - if (!entities || entities.length === 0) continue; - - this.entities.push(...entities); - } - } - - /** - * Calls `init` on each eligible module. - * - * This will do things like registering routes, setup timers or other module - * specific setup. - * - * `ModuleRegistry.loadModules` must have been called before. - */ - async initModules() { - for (const [moduleName, moduleEntry] of this.moduleMetadata.getEntries()) { - const { licenseFlag, class: ModuleClass } = moduleEntry; - - if (licenseFlag && !this.licenseState.isLicensed(licenseFlag)) { - this.logger.debug(`Skipped init for unlicensed module "${moduleName}"`); - continue; - } - - await Container.get(ModuleClass).init?.(); - - const moduleSettings = await Container.get(ModuleClass).settings?.(); - - if (!moduleSettings) continue; - - this.settings.set(moduleName, moduleSettings); - - this.logger.debug(`Initialized module "${moduleName}"`); - - this.activeModules.push(moduleName); - } - } - - isActive(moduleName: ModuleName) { - return this.activeModules.includes(moduleName); - } - - getActiveModules() { - return this.activeModules; - } - - registerLifecycleHooks(hooks: ExecutionLifecycleHooks) { - const handlers = this.lifecycleMetadata.getHandlers(); - - for (const { handlerClass, methodName, eventName } of handlers) { - const instance = Container.get(handlerClass); - - switch (eventName) { - case 'workflowExecuteAfter': - hooks.addHandler( - eventName, - async function ( - this: { workflowData: IWorkflowBase }, - runData: IRun, - newStaticData: IDataObject, - ) { - const context: LifecycleContext = { - type: 'workflowExecuteAfter', - workflow: this.workflowData, - runData, - newStaticData, - }; - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/return-await - return await instance[methodName].call(instance, context); - }, - ); - break; - - case 'nodeExecuteBefore': - hooks.addHandler( - eventName, - async function ( - this: { workflowData: IWorkflowBase }, - nodeName: string, - taskData: ITaskStartedData, - ) { - const context: LifecycleContext = { - type: 'nodeExecuteBefore', - workflow: this.workflowData, - nodeName, - taskData, - }; - - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/return-await - return await instance[methodName].call(instance, context); - }, - ); - break; - - case 'nodeExecuteAfter': - hooks.addHandler( - eventName, - async function ( - this: { workflowData: IWorkflowBase }, - nodeName: string, - taskData: ITaskData, - executionData: IRunExecutionData, - ) { - const context: LifecycleContext = { - type: 'nodeExecuteAfter', - workflow: this.workflowData, - nodeName, - taskData, - executionData, - }; - - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/return-await - return await instance[methodName].call(instance, context); - }, - ); - break; - - case 'workflowExecuteBefore': - hooks.addHandler( - eventName, - async function ( - this: { workflowData: IWorkflowBase }, - workflowInstance: Workflow, - executionData?: IRunExecutionData, - ) { - const context: LifecycleContext = { - type: 'workflowExecuteBefore', - workflow: this.workflowData, - workflowInstance, - executionData, - }; - - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/return-await - return await instance[methodName].call(instance, context); - }, - ); - break; - } - } - } -} diff --git a/packages/cli/src/services/frontend.service.ts b/packages/cli/src/services/frontend.service.ts index 66dff89a23..31fba140eb 100644 --- a/packages/cli/src/services/frontend.service.ts +++ b/packages/cli/src/services/frontend.service.ts @@ -1,5 +1,5 @@ import type { FrontendSettings, ITelemetrySettings } from '@n8n/api-types'; -import { LicenseState, Logger } from '@n8n/backend-common'; +import { LicenseState, Logger, ModuleRegistry } from '@n8n/backend-common'; import { GlobalConfig, SecurityConfig } from '@n8n/config'; import { LICENSE_FEATURES } from '@n8n/constants'; import { Container, Service } from '@n8n/di'; @@ -17,7 +17,6 @@ import { CredentialsOverwrites } from '@/credentials-overwrites'; import { getLdapLoginLabel } from '@/ldap.ee/helpers.ee'; import { License } from '@/license'; import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials'; -import { ModuleRegistry } from '@/modules/module-registry'; import { isApiEnabled } from '@/public-api'; import { PushConfig } from '@/push/push.config'; import type { CommunityPackagesService } from '@/services/community-packages.service'; diff --git a/packages/cli/test/integration/shared/test-modules.ts b/packages/cli/test/integration/shared/test-modules.ts index 976bd366ca..a473a0cd66 100644 --- a/packages/cli/test/integration/shared/test-modules.ts +++ b/packages/cli/test/integration/shared/test-modules.ts @@ -1,8 +1,7 @@ +import { ModuleRegistry } from '@n8n/backend-common'; +import type { ModuleName } from '@n8n/backend-common'; import { Container } from '@n8n/di'; -import { ModuleRegistry } from '@/modules/module-registry'; -import type { ModuleName } from '@/modules/modules.config'; - export async function loadModules(moduleNames: ModuleName[]) { await Container.get(ModuleRegistry).loadModules(moduleNames); } diff --git a/packages/cli/test/integration/shared/utils/test-server.ts b/packages/cli/test/integration/shared/utils/test-server.ts index 78a1e05bee..1fb30e716f 100644 --- a/packages/cli/test/integration/shared/utils/test-server.ts +++ b/packages/cli/test/integration/shared/utils/test-server.ts @@ -1,4 +1,5 @@ import { LicenseState } from '@n8n/backend-common'; +import { ModuleRegistry } from '@n8n/backend-common'; import { mockLogger } from '@n8n/backend-test-utils'; import type { User } from '@n8n/db'; import { Container } from '@n8n/di'; @@ -14,7 +15,6 @@ import { AUTH_COOKIE_NAME } from '@/constants'; import { ControllerRegistry } from '@/controller.registry'; import { License } from '@/license'; import { rawBodyReader, bodyParser } from '@/middlewares'; -import { ModuleRegistry } from '@/modules/module-registry'; import { PostHogClient } from '@/posthog'; import { Push } from '@/push'; import type { APIRequest } from '@/requests'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 55ce8f007e..6dceb2ac52 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -419,6 +419,9 @@ importers: '@n8n/constants': specifier: workspace:^ version: link:../constants + '@n8n/decorators': + specifier: workspace:^ + version: link:../decorators '@n8n/di': specifier: workspace:^ version: link:../di