refactor(core): Introduce @n8n/constants package (#14825)

This commit is contained in:
Iván Ovejero
2025-04-23 12:25:39 +02:00
committed by GitHub
parent 14d8ae1c1a
commit 9243e18de6
26 changed files with 146 additions and 86 deletions

View File

@@ -37,6 +37,7 @@ component_management:
- packages/@n8n/api-types/**
- packages/@n8n/config/**
- packages/@n8n/client-oauth2/**
- packages/@n8n/constants/**
- packages/@n8n/di/**
- packages/@n8n/imap/**
- packages/@n8n/permissions/**

View File

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

View File

@@ -0,0 +1,24 @@
{
"name": "@n8n/constants",
"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"
},
"main": "dist/index.js",
"module": "src/index.ts",
"types": "dist/index.d.ts",
"files": [
"dist/**/*"
],
"devDependencies": {
"@n8n/typescript-config": "workspace:*"
}
}

View File

@@ -0,0 +1,47 @@
export const LICENSE_FEATURES = {
SHARING: 'feat:sharing',
LDAP: 'feat:ldap',
SAML: 'feat:saml',
LOG_STREAMING: 'feat:logStreaming',
ADVANCED_EXECUTION_FILTERS: 'feat:advancedExecutionFilters',
VARIABLES: 'feat:variables',
SOURCE_CONTROL: 'feat:sourceControl',
API_DISABLED: 'feat:apiDisabled',
EXTERNAL_SECRETS: 'feat:externalSecrets',
SHOW_NON_PROD_BANNER: 'feat:showNonProdBanner',
WORKFLOW_HISTORY: 'feat:workflowHistory',
DEBUG_IN_EDITOR: 'feat:debugInEditor',
BINARY_DATA_S3: 'feat:binaryDataS3',
MULTIPLE_MAIN_INSTANCES: 'feat:multipleMainInstances',
WORKER_VIEW: 'feat:workerView',
ADVANCED_PERMISSIONS: 'feat:advancedPermissions',
PROJECT_ROLE_ADMIN: 'feat:projectRole:admin',
PROJECT_ROLE_EDITOR: 'feat:projectRole:editor',
PROJECT_ROLE_VIEWER: 'feat:projectRole:viewer',
AI_ASSISTANT: 'feat:aiAssistant',
ASK_AI: 'feat:askAi',
COMMUNITY_NODES_CUSTOM_REGISTRY: 'feat:communityNodes:customRegistry',
AI_CREDITS: 'feat:aiCredits',
FOLDERS: 'feat:folders',
INSIGHTS_VIEW_SUMMARY: 'feat:insights:viewSummary',
INSIGHTS_VIEW_DASHBOARD: 'feat:insights:viewDashboard',
INSIGHTS_VIEW_HOURLY_DATA: 'feat:insights:viewHourlyData',
API_KEY_SCOPES: 'feat:apiKeyScopes',
} as const;
export const LICENSE_QUOTAS = {
TRIGGER_LIMIT: 'quota:activeWorkflows',
VARIABLES_LIMIT: 'quota:maxVariables',
USERS_LIMIT: 'quota:users',
WORKFLOW_HISTORY_PRUNE_LIMIT: 'quota:workflowHistoryPrune',
TEAM_PROJECT_LIMIT: 'quota:maxTeamProjects',
AI_CREDITS: 'quota:aiCredits',
INSIGHTS_MAX_HISTORY_DAYS: 'quota:insights:maxHistoryDays',
INSIGHTS_RETENTION_MAX_AGE_DAYS: 'quota:insights:retention:maxAgeDays',
INSIGHTS_RETENTION_PRUNE_INTERVAL_DAYS: 'quota:insights:retention:pruneIntervalDays',
} as const;
export const UNLIMITED_LICENSE_QUOTA = -1;
export type BooleanLicenseFeature = (typeof LICENSE_FEATURES)[keyof typeof LICENSE_FEATURES];
export type NumericLicenseFeature = (typeof LICENSE_QUOTAS)[keyof typeof LICENSE_QUOTAS];

View File

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

View File

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

View File

@@ -92,6 +92,7 @@
"@n8n/api-types": "workspace:*",
"@n8n/client-oauth2": "workspace:*",
"@n8n/config": "workspace:*",
"@n8n/constants": "workspace:^",
"@n8n/di": "workspace:*",
"@n8n/localtunnel": "3.0.0",
"@n8n/n8n-nodes-langchain": "workspace:*",

View File

@@ -1,5 +1,6 @@
import 'reflect-metadata';
import { GlobalConfig } from '@n8n/config';
import { LICENSE_FEATURES } from '@n8n/constants';
import { Container } from '@n8n/di';
import { Command, Errors } from '@oclif/core';
import {
@@ -15,13 +16,7 @@ import { ensureError, sleep, UserError } from 'n8n-workflow';
import type { AbstractServer } from '@/abstract-server';
import config from '@/config';
import {
LICENSE_FEATURES,
N8N_VERSION,
N8N_RELEASE_DATE,
inDevelopment,
inTest,
} from '@/constants';
import { N8N_VERSION, N8N_RELEASE_DATE, inDevelopment, inTest } from '@/constants';
import * as CrashJournal from '@/crash-journal';
import * as Db from '@/db';
import { ModuleRegistry } from '@/decorators/module';

View File

@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { LICENSE_FEATURES } from '@n8n/constants';
import { Container } from '@n8n/di';
import { Flags } from '@oclif/core';
import glob from 'fast-glob';
@@ -13,7 +14,7 @@ import { pipeline } from 'stream/promises';
import { ActiveExecutions } from '@/active-executions';
import { ActiveWorkflowManager } from '@/active-workflow-manager';
import config from '@/config';
import { EDITOR_UI_DIST_DIR, LICENSE_FEATURES } from '@/constants';
import { EDITOR_UI_DIST_DIR } from '@/constants';
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
import { SettingsRepository } from '@/databases/repositories/settings.repository';
import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error';

View File

@@ -72,50 +72,6 @@ export const WORKFLOW_REACTIVATE_MAX_TIMEOUT = 24 * 60 * 60 * 1000; // 1 day
export const SETTINGS_LICENSE_CERT_KEY = 'license.cert';
export const LICENSE_FEATURES = {
SHARING: 'feat:sharing',
LDAP: 'feat:ldap',
SAML: 'feat:saml',
LOG_STREAMING: 'feat:logStreaming',
ADVANCED_EXECUTION_FILTERS: 'feat:advancedExecutionFilters',
VARIABLES: 'feat:variables',
SOURCE_CONTROL: 'feat:sourceControl',
API_DISABLED: 'feat:apiDisabled',
EXTERNAL_SECRETS: 'feat:externalSecrets',
SHOW_NON_PROD_BANNER: 'feat:showNonProdBanner',
WORKFLOW_HISTORY: 'feat:workflowHistory',
DEBUG_IN_EDITOR: 'feat:debugInEditor',
BINARY_DATA_S3: 'feat:binaryDataS3',
MULTIPLE_MAIN_INSTANCES: 'feat:multipleMainInstances',
WORKER_VIEW: 'feat:workerView',
ADVANCED_PERMISSIONS: 'feat:advancedPermissions',
PROJECT_ROLE_ADMIN: 'feat:projectRole:admin',
PROJECT_ROLE_EDITOR: 'feat:projectRole:editor',
PROJECT_ROLE_VIEWER: 'feat:projectRole:viewer',
AI_ASSISTANT: 'feat:aiAssistant',
ASK_AI: 'feat:askAi',
COMMUNITY_NODES_CUSTOM_REGISTRY: 'feat:communityNodes:customRegistry',
AI_CREDITS: 'feat:aiCredits',
FOLDERS: 'feat:folders',
INSIGHTS_VIEW_SUMMARY: 'feat:insights:viewSummary',
INSIGHTS_VIEW_DASHBOARD: 'feat:insights:viewDashboard',
INSIGHTS_VIEW_HOURLY_DATA: 'feat:insights:viewHourlyData',
API_KEY_SCOPES: 'feat:apiKeyScopes',
} as const;
export const LICENSE_QUOTAS = {
TRIGGER_LIMIT: 'quota:activeWorkflows',
VARIABLES_LIMIT: 'quota:maxVariables',
USERS_LIMIT: 'quota:users',
WORKFLOW_HISTORY_PRUNE_LIMIT: 'quota:workflowHistoryPrune',
TEAM_PROJECT_LIMIT: 'quota:maxTeamProjects',
AI_CREDITS: 'quota:aiCredits',
INSIGHTS_MAX_HISTORY_DAYS: 'quota:insights:maxHistoryDays',
INSIGHTS_RETENTION_MAX_AGE_DAYS: 'quota:insights:retention:maxAgeDays',
INSIGHTS_RETENTION_PRUNE_INTERVAL_DAYS: 'quota:insights:retention:pruneIntervalDays',
} as const;
export const UNLIMITED_LICENSE_QUOTA = -1;
export const CREDENTIAL_BLANKING_VALUE = '__n8n_BLANK_VALUE_e5362baf-c777-4d57-a609-6eaf1f9e87f6';
export const UM_FIX_INSTRUCTION =

View File

@@ -1,4 +1,6 @@
import type { PushMessage } from '@n8n/api-types';
import type { BooleanLicenseFeature, NumericLicenseFeature } from '@n8n/constants';
import { LICENSE_FEATURES, LICENSE_QUOTAS, UNLIMITED_LICENSE_QUOTA } from '@n8n/constants';
import { Container } from '@n8n/di';
import { Request } from 'express';
import { Logger } from 'n8n-core';
@@ -6,13 +8,12 @@ import { v4 as uuid } from 'uuid';
import { ActiveWorkflowManager } from '@/active-workflow-manager';
import config from '@/config';
import { LICENSE_FEATURES, LICENSE_QUOTAS, UNLIMITED_LICENSE_QUOTA, inE2ETests } from '@/constants';
import { inE2ETests } from '@/constants';
import { AuthUserRepository } from '@/databases/repositories/auth-user.repository';
import { SettingsRepository } from '@/databases/repositories/settings.repository';
import { UserRepository } from '@/databases/repositories/user.repository';
import { Patch, Post, RestController } from '@/decorators';
import { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus';
import type { BooleanLicenseFeature, NumericLicenseFeature } from '@/interfaces';
import type { FeatureReturnType } from '@/license';
import { License } from '@/license';
import { MfaService } from '@/mfa/mfa.service';

View File

@@ -1,4 +1,5 @@
import { GlobalConfig } from '@n8n/config';
import type { BooleanLicenseFeature } from '@n8n/constants';
import { Container, Service } from '@n8n/di';
import { Router } from 'express';
import type { Application, Request, Response, RequestHandler } from 'express';
@@ -9,7 +10,6 @@ import type { ZodClass } from 'zod-class';
import { AuthService } from '@/auth/auth.service';
import { inProduction, RESPONSE_ERROR_MESSAGES } from '@/constants';
import { UnauthenticatedError } from '@/errors/response-errors/unauthenticated.error';
import type { BooleanLicenseFeature } from '@/interfaces';
import { License } from '@/license';
import { userHasScopes } from '@/permissions.ee/check-access';
import type { AuthenticatedRequest } from '@/requests';

View File

@@ -1,4 +1,4 @@
import type { BooleanLicenseFeature } from '@/interfaces';
import type { BooleanLicenseFeature } from '@n8n/constants';
import { getRouteMetadata } from './controller.registry';
import type { Controller } from './types';

View File

@@ -1,9 +1,8 @@
import type { BooleanLicenseFeature } from '@n8n/constants';
import type { Constructable } from '@n8n/di';
import type { Scope } from '@n8n/permissions';
import type { RequestHandler } from 'express';
import type { BooleanLicenseFeature } from '@/interfaces';
export type Method = 'get' | 'post' | 'put' | 'patch' | 'delete';
export type Arg = { type: 'body' | 'query' } | { type: 'param'; key: string };

View File

@@ -1,7 +1,6 @@
import type { LICENSE_FEATURES } from '@n8n/constants';
import { UserError } from 'n8n-workflow';
import type { LICENSE_FEATURES } from '@/constants';
export class FeatureNotLicensedError extends UserError {
constructor(feature: (typeof LICENSE_FEATURES)[keyof typeof LICENSE_FEATURES]) {
super(

View File

@@ -30,7 +30,6 @@ import type { SharedCredentials } from '@/databases/entities/shared-credentials'
import type { TagEntity } from '@/databases/entities/tag-entity';
import type { User } from '@/databases/entities/user';
import type { LICENSE_FEATURES, LICENSE_QUOTAS } from './constants';
import type { Folder } from './databases/entities/folder';
import type { ExternalHooks } from './external-hooks';
import type { WorkflowWithSharingsAndCredentials } from './workflows/workflows.types';
@@ -306,11 +305,6 @@ export interface IExecutionTrackProperties extends ITelemetryTrackProperties {
// license
// ----------------------------------
type ValuesOf<T> = T[keyof T];
export type BooleanLicenseFeature = ValuesOf<typeof LICENSE_FEATURES>;
export type NumericLicenseFeature = ValuesOf<typeof LICENSE_QUOTAS>;
export interface ILicenseReadResponse {
usage: {
activeWorkflowTriggers: {

View File

@@ -1,4 +1,11 @@
import { GlobalConfig } from '@n8n/config';
import {
LICENSE_FEATURES,
LICENSE_QUOTAS,
UNLIMITED_LICENSE_QUOTA,
type BooleanLicenseFeature,
type NumericLicenseFeature,
} from '@n8n/constants';
import { Container, Service } from '@n8n/di';
import type { TEntitlement, TFeatures, TLicenseBlock } from '@n8n_io/license-sdk';
import { LicenseManager } from '@n8n_io/license-sdk';
@@ -9,15 +16,7 @@ import { SettingsRepository } from '@/databases/repositories/settings.repository
import { OnShutdown } from '@/decorators/on-shutdown';
import { LicenseMetricsService } from '@/metrics/license-metrics.service';
import {
LICENSE_FEATURES,
LICENSE_QUOTAS,
N8N_VERSION,
SETTINGS_LICENSE_CERT_KEY,
Time,
UNLIMITED_LICENSE_QUOTA,
} from './constants';
import type { BooleanLicenseFeature, NumericLicenseFeature } from './interfaces';
import { N8N_VERSION, SETTINGS_LICENSE_CERT_KEY, Time } from './constants';
const LICENSE_RENEWAL_DISABLED_WARNING =
'Automatic license renewal is disabled. The license will not renew automatically, and access to licensed features may be lost!';

View File

@@ -1,11 +1,11 @@
/* eslint-disable @typescript-eslint/no-invalid-void-type */
import type { BooleanLicenseFeature } from '@n8n/constants';
import { Container } from '@n8n/di';
import type { ApiKeyScope, Scope } from '@n8n/permissions';
import type express from 'express';
import type { NextFunction } from 'express';
import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error';
import type { BooleanLicenseFeature } from '@/interfaces';
import { License } from '@/license';
import { userHasScopes } from '@/permissions.ee/check-access';
import type { AuthenticatedRequest } from '@/requests';

View File

@@ -1,4 +1,5 @@
import { GlobalConfig } from '@n8n/config';
import { LICENSE_FEATURES } from '@n8n/constants';
import { Service } from '@n8n/di';
import axios from 'axios';
import { exec } from 'child_process';
@@ -9,7 +10,6 @@ import { UnexpectedError, UserError, type PublicInstalledPackage } from 'n8n-wor
import { promisify } from 'util';
import {
LICENSE_FEATURES,
NODE_PACKAGE_PREFIX,
NPM_COMMAND_TOKENS,
NPM_PACKAGE_STATUS_GOOD,

View File

@@ -1,5 +1,6 @@
import type { FrontendSettings, ITelemetrySettings } from '@n8n/api-types';
import { GlobalConfig, SecurityConfig } from '@n8n/config';
import { LICENSE_FEATURES } from '@n8n/constants';
import { Container, Service } from '@n8n/di';
import { createWriteStream } from 'fs';
import { mkdir } from 'fs/promises';
@@ -9,7 +10,7 @@ import type { ICredentialType, INodeTypeBaseDescription } from 'n8n-workflow';
import path from 'path';
import config from '@/config';
import { inE2ETests, LICENSE_FEATURES, N8N_VERSION } from '@/constants';
import { inE2ETests, N8N_VERSION } from '@/constants';
import { CredentialTypes } from '@/credential-types';
import { CredentialsOverwrites } from '@/credentials-overwrites';
import { getLdapLoginLabel } from '@/ldap.ee/helpers.ee';

View File

@@ -1,4 +1,5 @@
import type { CreateProjectDto, ProjectRole, ProjectType, UpdateProjectDto } from '@n8n/api-types';
import { UNLIMITED_LICENSE_QUOTA } from '@n8n/constants';
import { Container, Service } from '@n8n/di';
import { type Scope } from '@n8n/permissions';
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
@@ -7,7 +8,6 @@ import type { FindOptionsWhere, EntityManager } from '@n8n/typeorm';
import { In, Not } from '@n8n/typeorm';
import { UserError } from 'n8n-workflow';
import { UNLIMITED_LICENSE_QUOTA } from '@/constants';
import { Project } from '@/databases/entities/project';
import { ProjectRelation } from '@/databases/entities/project-relation';
import type { User } from '@/databases/entities/user';

View File

@@ -1,4 +1,5 @@
import type { BooleanLicenseFeature, NumericLicenseFeature } from '@/interfaces';
import type { BooleanLicenseFeature, NumericLicenseFeature } from '@n8n/constants';
import type { License } from '@/license';
export interface LicenseMockDefaults {

View File

@@ -1,3 +1,4 @@
import type { BooleanLicenseFeature, NumericLicenseFeature } from '@n8n/constants';
import type { Application } from 'express';
import type { Server } from 'http';
import type { ICredentialDataDecryptedObject } from 'n8n-workflow';
@@ -6,7 +7,7 @@ import type TestAgent from 'supertest/lib/agent';
import type { CredentialsEntity } from '@/databases/entities/credentials-entity';
import type { Project } from '@/databases/entities/project';
import type { User } from '@/databases/entities/user';
import type { BooleanLicenseFeature, ICredentialsDb, NumericLicenseFeature } from '@/interfaces';
import type { ICredentialsDb } from '@/interfaces';
import type { LicenseMocker } from './license';

View File

@@ -26,6 +26,7 @@
{ "path": "../@n8n/api-types/tsconfig.build.json" },
{ "path": "../@n8n/client-oauth2/tsconfig.build.json" },
{ "path": "../@n8n/config/tsconfig.build.json" },
{ "path": "../@n8n/constants/tsconfig.build.json" },
{ "path": "../@n8n/di/tsconfig.build.json" },
{ "path": "../@n8n/nodes-langchain/tsconfig.build.json" },
{ "path": "../@n8n/permissions/tsconfig.build.json" }

19
pnpm-lock.yaml generated
View File

@@ -416,6 +416,12 @@ importers:
specifier: workspace:*
version: link:../typescript-config
packages/@n8n/constants:
devDependencies:
'@n8n/typescript-config':
specifier: workspace:*
version: link:../typescript-config
packages/@n8n/di:
dependencies:
reflect-metadata:
@@ -926,6 +932,9 @@ importers:
'@n8n/config':
specifier: workspace:*
version: link:../@n8n/config
'@n8n/constants':
specifier: workspace:^
version: link:../@n8n/constants
'@n8n/di':
specifier: workspace:*
version: link:../@n8n/di
@@ -13552,8 +13561,8 @@ packages:
vue-component-type-helpers@2.1.10:
resolution: {integrity: sha512-lfgdSLQKrUmADiSV6PbBvYgQ33KF3Ztv6gP85MfGaGaSGMTXORVaHT1EHfsqCgzRNBstPKYDmvAV9Do5CmJ07A==}
vue-component-type-helpers@2.2.8:
resolution: {integrity: sha512-4bjIsC284coDO9om4HPA62M7wfsTvcmZyzdfR0aUlFXqq4tXxM1APyXpNVxPC8QazKw9OhmZNHBVDA6ODaZsrA==}
vue-component-type-helpers@2.2.10:
resolution: {integrity: sha512-iDUO7uQK+Sab2tYuiP9D1oLujCWlhHELHMgV/cB13cuGbG4qwkLHvtfWb6FzvxrIOPDnU0oHsz2MlQjhYDeaHA==}
vue-demi@0.14.10:
resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
@@ -18612,7 +18621,7 @@ snapshots:
ts-dedent: 2.2.0
type-fest: 2.19.0
vue: 3.5.13(typescript@5.8.2)
vue-component-type-helpers: 2.2.8
vue-component-type-helpers: 2.2.10
'@supabase/auth-js@2.65.0':
dependencies:
@@ -19661,7 +19670,7 @@ snapshots:
'@vue/test-utils@2.4.6':
dependencies:
js-beautify: 1.14.9
vue-component-type-helpers: 2.2.8
vue-component-type-helpers: 2.2.10
'@vue/tsconfig@0.7.0(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))':
optionalDependencies:
@@ -27990,7 +27999,7 @@ snapshots:
vue-component-type-helpers@2.1.10: {}
vue-component-type-helpers@2.2.8: {}
vue-component-type-helpers@2.2.10: {}
vue-demi@0.14.10(vue@3.5.13(typescript@5.8.2)):
dependencies:

View File

@@ -28,6 +28,7 @@
"dependsOn": [
"@n8n/api-types#lint",
"@n8n/config#lint",
"@n8n/constants#lint",
"@n8n/di#lint",
"@n8n/client-oauth2#lint",
"@n8n/imap#lint",