feat(editor): Add the type-availability-policies frontend module (no-changelog) (#38634)

This commit is contained in:
Theodore Nastos 2026-09-15 11:17:15 +00:00 committed by GitHub
parent 4514a35d1d
commit f7f51fbcd3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 214 additions and 0 deletions

5
OWNERS
View File

@ -235,6 +235,11 @@ packages/@n8n/client-oauth2/ @n8n-io/iam
packages/extensions/insights/ @n8n-io/ligo
# Governance & Observability
packages/modules/type-availability-policies/ @n8n-io/governance-and-observability
packages/cli/src/modules/type-availability-policies/ @n8n-io/governance-and-observability
# CI/CD
.github/ @n8n-io/qa-dx

View File

@ -44,6 +44,10 @@ export const sourcePackages = [
* The module tsconfig base holds that boundary.
*/
export const modulePackages: Array<{ name: string; dir: string; entry?: boolean }> = [
{
name: '@n8n/frontend-module-type-availability-policies',
dir: 'modules/type-availability-policies/frontend',
},
{ name: '@n8n/frontend-module-insights', dir: 'modules/insights/frontend' },
{ name: '@n8n/frontend-module-instance-registry', dir: 'modules/instance-registry/frontend' },
{ name: '@n8n/frontend-module-otel', dir: 'modules/otel/frontend' },

View File

@ -57,6 +57,7 @@
"@n8n/frontend-module-instance-registry": "workspace:*",
"@n8n/frontend-module-otel": "workspace:*",
"@n8n/frontend-module-sdk": "workspace:*",
"@n8n/frontend-module-type-availability-policies": "workspace:*",
"@n8n/frontend-test-utils": "workspace:*",
"@n8n/frontend-utils": "workspace:*",
"@n8n/frontend-vite-config": "workspace:*",

View File

@ -10,6 +10,7 @@ import { OtelModule } from '@n8n/frontend-module-otel';
import { InsightsModule } from '@n8n/frontend-module-insights/insights.module';
import { PromotionsModule } from '@/features/integrations/promotions.ee/module.descriptor';
import { ContextModule } from '@/features/settings/context/module.descriptor';
import { TypeAvailabilityPoliciesModule } from '@n8n/frontend-module-type-availability-policies';
/**
* The static list is the design, not a placeholder (design §9). n8n self-hosted
@ -39,4 +40,5 @@ export const modules: FrontendModuleDescription[] = [
InsightsModule,
PromotionsModule,
ContextModule,
TypeAvailabilityPoliciesModule,
];

View File

@ -25,6 +25,12 @@
"@n8n/composables*": ["../@n8n/composables/src*"],
"@n8n/constants*": ["../../@n8n/constants/src*"],
"@n8n/frontend-module-sdk": ["../@n8n/frontend-module-sdk/src/index.ts"],
"@n8n/frontend-module-type-availability-policies": [
"../../modules/type-availability-policies/frontend/src/index.ts"
],
"@n8n/frontend-module-type-availability-policies/*": [
"../../modules/type-availability-policies/frontend/src/*"
],
"@n8n/frontend-module-insights": ["../../modules/insights/frontend/src/index.ts"],
"@n8n/frontend-module-insights/*": ["../../modules/insights/frontend/src/*"],
"@n8n/frontend-module-instance-registry": [

View File

@ -0,0 +1,21 @@
# @n8n/frontend-module-type-availability-policies
Frontend module for node type availability policies. The editor-ui shell consumes it from
source through `src/app/modules.manifest.ts`, so there is no build step and no `dist`.
The descriptor `id` must match the backend module id
(`packages/cli/src/modules/type-availability-policies`). Both `settings.activeModules` and
`/rest/module-settings` are keyed by that string, and a mismatch fails silently.
The module is license-gated on `feat:nodeTypePolicies` and is not a default module. Enable it
in a dev instance with `N8N_ENABLED_MODULES=type-availability-policies`.
```bash
pnpm turbo typecheck lint test --filter=@n8n/frontend-module-type-availability-policies
```
Use turbo and not `pnpm --filter`: this package reads its platform dependencies from source,
and on a cold tree turbo builds them first.
`packages/@n8n/module-cli/frontend-module-guide.md` holds the descriptor contract, the
registration points, the import boundaries and the setup a module needs to render UI.

View File

@ -0,0 +1,4 @@
{
"$schema": "../../../../node_modules/@biomejs/biome/configuration_schema.json",
"extends": ["../../../../biome.jsonc"]
}

View File

@ -0,0 +1,4 @@
import { defineConfig } from 'eslint/config';
import { frontendConfig } from '@n8n/eslint-config/frontend';
export default defineConfig(frontendConfig);

View File

@ -0,0 +1,41 @@
{
"name": "@n8n/frontend-module-type-availability-policies",
"version": "0.1.0",
"type": "module",
"main": "src/index.ts",
"exports": {
".": "./src/index.ts"
},
"scripts": {
"clean": "rimraf .turbo",
"typecheck": "vue-tsc --noEmit",
"test": "vitest run",
"test:changed": "janitor test-scoped",
"test:dev": "vitest",
"lint": "eslint src --quiet",
"lint:fix": "eslint src --fix",
"format": "biome format --write . && prettier --write . --ignore-path ../../../../.prettierignore",
"format:check": "biome ci . && prettier --check . --ignore-path ../../../../.prettierignore"
},
"dependencies": {
"@n8n/frontend-module-sdk": "workspace:*"
},
"devDependencies": {
"@n8n/eslint-config": "workspace:*",
"@n8n/frontend-test-utils": "workspace:*",
"@n8n/frontend-vite-config": "workspace:*",
"@n8n/playwright-janitor": "workspace:*",
"@n8n/typescript-config": "workspace:*",
"@n8n/vitest-config": "workspace:*",
"@testing-library/jest-dom": "catalog:frontend",
"@testing-library/vue": "catalog:frontend",
"@vitejs/plugin-vue": "catalog:frontend",
"eslint": "catalog:",
"typescript": "catalog:",
"unplugin-icons": "catalog:frontend",
"vite": "catalog:",
"vitest": "catalog:",
"vue-tsc": "catalog:frontend"
},
"license": "LicenseRef-n8n-sustainable-use"
}

View File

@ -0,0 +1,4 @@
// The shared frontend test harness: the jsdom environment, english messages, and a fresh pinia
// per test. It lives in `@n8n/frontend-test-utils` rather than in `@n8n/vitest-config`, because
// `@n8n/i18n` devDepends on the latter and importing i18n there closes a cycle in the turbo graph.
import '@n8n/frontend-test-utils/setup';

View File

@ -0,0 +1 @@
export { TypeAvailabilityPoliciesModule } from './type-availability-policies.module';

View File

@ -0,0 +1,2 @@
/** Must stay the same as the backend module id: `settings.activeModules` is keyed by it. */
export const TYPE_AVAILABILITY_POLICIES_MODULE_ID = 'type-availability-policies';

View File

@ -0,0 +1,9 @@
import { TypeAvailabilityPoliciesModule } from './type-availability-policies.module';
describe('TypeAvailabilityPoliciesModule', () => {
it('keeps the id the backend module publishes in `activeModules`', () => {
// A mismatch is silent: `isModuleActive` compares strings, so the module would
// never report active. The twin is `packages/cli/src/modules/type-availability-policies`.
expect(TypeAvailabilityPoliciesModule.id).toBe('type-availability-policies');
});
});

View File

@ -0,0 +1,11 @@
import { defineFrontendModule } from '@n8n/frontend-module-sdk';
import { TYPE_AVAILABILITY_POLICIES_MODULE_ID } from './type-availability-policies.constants';
/** Keep this file import-light: the shell runs every descriptor body before Pinia starts. */
export const TypeAvailabilityPoliciesModule = defineFrontendModule({
id: TYPE_AVAILABILITY_POLICIES_MODULE_ID,
name: 'Type Availability Policies',
description: 'Reports which node types a project can use, and why a type is unavailable',
icon: 'shield',
});

View File

@ -0,0 +1,21 @@
{
"extends": "@n8n/typescript-config/tsconfig.frontend-module.json",
"compilerOptions": {
// `rootDirs`, `types` and `include` cannot be inherited from the base: relative entries in
// them resolve against this file, so a copy in the base would point at the base's directory.
// (`paths` is the exception and does come from the base.)
"rootDirs": [".", "../../../frontend/@n8n/design-system/src"],
// The two `.d.ts` entries are ambient declarations this package never imports, so nothing
// pulls them into the program: `~icons/*` and `markdown-it-task-lists` for design-system's
// source, `window.BASE_PATH` for `@n8n/stores`'s. Consuming those packages from source is
// what makes them the consumer's problem — a built `dist` would have carried them.
"types": [
"vite/client",
"vitest/globals",
"unplugin-icons/types/vue",
"../../../frontend/@n8n/design-system/src/shims-modules.d.ts",
"../../../frontend/@n8n/stores/src/shims.d.ts"
]
},
"include": ["src/**/*.ts", "src/**/*.vue", "vite.config.ts"]
}

View File

@ -0,0 +1,23 @@
import vue from '@vitejs/plugin-vue';
import { fileURLToPath } from 'node:url';
import { resolve } from 'node:path';
import { defineConfig, mergeConfig } from 'vite';
import { frontendAliases } from '@n8n/frontend-vite-config';
import { vitestConfig } from '@n8n/vitest-config/frontend';
const packageDir = fileURLToPath(new URL('.', import.meta.url));
const packagesDir = resolve(packageDir, '..', '..', '..');
export default mergeConfig(
defineConfig({
plugins: [vue()],
resolve: {
// The same platform mapping the editor-ui dev server uses, so a test resolves
// `@n8n/stores/...` from source rather than from a stale `dist` — the two disagreeing is
// what put 1,111 specifiers on the wrong side of the src/dist line. Sibling modules are
// deliberately absent: nothing here should make a cross-module import resolve.
alias: frontendAliases(packagesDir),
},
}),
vitestConfig,
);

View File

@ -6047,6 +6047,9 @@ importers:
'@n8n/frontend-module-sdk':
specifier: workspace:*
version: link:../@n8n/frontend-module-sdk
'@n8n/frontend-module-type-availability-policies':
specifier: workspace:*
version: link:../../modules/type-availability-policies/frontend
'@n8n/frontend-test-utils':
specifier: workspace:*
version: link:../@n8n/frontend-test-utils
@ -6726,6 +6729,58 @@ importers:
specifier: ^2.2.8
version: 2.2.8(patch_hash=e2aee939ccac8a57fe449bfd92bedd8117841579526217bc39aca26c6b8c317f)(typescript@6.0.2)
packages/modules/type-availability-policies/frontend:
dependencies:
'@n8n/frontend-module-sdk':
specifier: workspace:*
version: link:../../../frontend/@n8n/frontend-module-sdk
devDependencies:
'@n8n/eslint-config':
specifier: workspace:*
version: link:../../../@n8n/eslint-config
'@n8n/frontend-test-utils':
specifier: workspace:*
version: link:../../../frontend/@n8n/frontend-test-utils
'@n8n/frontend-vite-config':
specifier: workspace:*
version: link:../../../frontend/@n8n/frontend-vite-config
'@n8n/playwright-janitor':
specifier: workspace:*
version: link:../../../testing/janitor
'@n8n/typescript-config':
specifier: workspace:*
version: link:../../../@n8n/typescript-config
'@n8n/vitest-config':
specifier: workspace:*
version: link:../../../@n8n/vitest-config
'@testing-library/jest-dom':
specifier: catalog:frontend
version: 6.9.1
'@testing-library/vue':
specifier: catalog:frontend
version: 8.1.0(@vue/compiler-sfc@3.5.26)(vue@3.5.26(typescript@6.0.2))
'@vitejs/plugin-vue':
specifier: catalog:frontend
version: 6.0.8(vite@8.0.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@20.19.41)(esbuild@0.28.1)(jiti@2.6.1)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.16.1)(tsx@4.19.3)(yaml@2.8.3))(vue@3.5.26(typescript@6.0.2))
eslint:
specifier: 'catalog:'
version: 9.29.0(jiti@2.6.1)(supports-color@8.1.1)
typescript:
specifier: 'catalog:'
version: 6.0.2
unplugin-icons:
specifier: catalog:frontend
version: 23.0.1(@vue/compiler-sfc@3.5.26)
vite:
specifier: 'catalog:'
version: 8.0.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@20.19.41)(esbuild@0.28.1)(jiti@2.6.1)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.16.1)(tsx@4.19.3)(yaml@2.8.3)
vitest:
specifier: 'catalog:'
version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@20.19.41)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(jsdom@23.0.1(bufferutil@4.0.9)(supports-color@8.1.1)(utf-8-validate@5.0.10))(vite@8.0.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@20.19.41)(esbuild@0.28.1)(jiti@2.6.1)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.16.1)(tsx@4.19.3)(yaml@2.8.3))
vue-tsc:
specifier: ^2.2.8
version: 2.2.8(patch_hash=e2aee939ccac8a57fe449bfd92bedd8117841579526217bc39aca26c6b8c317f)(typescript@6.0.2)
packages/node-dev:
dependencies:
'@n8n/di':