From c4d08ecb69cf53a9cffc421f8b70bbf708505f54 Mon Sep 17 00:00:00 2001 From: Charlie Kolb Date: Fri, 10 Jul 2026 13:40:58 +0200 Subject: [PATCH] chore(eslint-config): Apply module boundary tags to all packages (spike, no-changelog) Stamps boundaries: { type, scope } into all 71 workspace package.json files per the assessment table, extends the rule's type set (runtime/nodes/sdk/tooling) to cover the whole repo, and adds a validation harness that checks the real workspace dependency graph against the matrices. Harness output: 19 type + 21 scope violations, clustering on known smells (n8n-workflow is types+runtime in one; design-system/workflow-sdk want 'shared' scope; core->platform layering inversion). See scripts/spike-apply-boundaries.mjs. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/@n8n/agents/package.json | 1 + packages/@n8n/ai-node-sdk/package.json | 1 + packages/@n8n/ai-utilities/package.json | 1 + .../@n8n/ai-workflow-builder.ee/package.json | 1 + packages/@n8n/api-types/package.json | 1 + packages/@n8n/backend-common/package.json | 1 + packages/@n8n/backend-network/package.json | 1 + packages/@n8n/backend-test-utils/package.json | 1 + packages/@n8n/benchmark/package.json | 1 + packages/@n8n/chat-hub/package.json | 1 + packages/@n8n/cli/package.json | 1 + packages/@n8n/client-oauth2/package.json | 1 + .../@n8n/codemirror-lang-html/package.json | 1 + .../@n8n/codemirror-lang-sql/package.json | 1 + packages/@n8n/codemirror-lang/package.json | 1 + packages/@n8n/computer-use/package.json | 1 + packages/@n8n/config/package.json | 1 + packages/@n8n/constants/package.json | 1 + packages/@n8n/crdt/package.json | 1 + packages/@n8n/create-node/package.json | 1 + packages/@n8n/db/package.json | 1 + packages/@n8n/decorators/package.json | 1 + packages/@n8n/di/package.json | 1 + packages/@n8n/engine/package.json | 1 + packages/@n8n/errors/package.json | 1 + packages/@n8n/eslint-config/package.json | 1 + .../scripts/spike-apply-boundaries.mjs | 185 ++++++++++++++++++ .../src/rules/enforce-module-boundaries.ts | 34 +++- .../package.json | 1 + packages/@n8n/expression-runtime/package.json | 1 + packages/@n8n/extension-sdk/package.json | 1 + packages/@n8n/imap/package.json | 1 + packages/@n8n/instance-ai/package.json | 1 + packages/@n8n/json-schema-to-zod/package.json | 1 + packages/@n8n/local-gateway/package.json | 1 + packages/@n8n/mcp-apps/package.json | 1 + .../@n8n/mcp-browser-extension/package.json | 1 + packages/@n8n/mcp-browser/package.json | 1 + packages/@n8n/node-cli/package.json | 1 + packages/@n8n/nodes-langchain/package.json | 1 + packages/@n8n/permissions/package.json | 1 + .../@n8n/scan-community-package/package.json | 1 + packages/@n8n/scheduler/package.json | 1 + packages/@n8n/stylelint-config/package.json | 1 + packages/@n8n/syslog-client/package.json | 1 + packages/@n8n/task-runner/package.json | 1 + packages/@n8n/tournament/package.json | 1 + packages/@n8n/typeorm/package.json | 1 + packages/@n8n/typescript-config/package.json | 1 + packages/@n8n/utils/package.json | 1 + packages/@n8n/vitest-config/package.json | 1 + packages/@n8n/workflow-sdk/package.json | 1 + packages/cli/package.json | 1 + packages/core/package.json | 1 + packages/extensions/insights/package.json | 1 + packages/frontend/@n8n/chat/package.json | 1 + .../frontend/@n8n/composables/package.json | 1 + .../frontend/@n8n/design-system/package.json | 1 + packages/frontend/@n8n/i18n/package.json | 1 + .../@n8n/rest-api-client/package.json | 1 + packages/frontend/@n8n/stores/package.json | 1 + packages/frontend/@n8n/storybook/package.json | 1 + packages/frontend/editor-ui/package.json | 1 + packages/node-dev/package.json | 1 + packages/nodes-base/package.json | 1 + packages/testing/code-health/package.json | 1 + packages/testing/containers/package.json | 1 + packages/testing/janitor/package.json | 1 + packages/testing/performance/package.json | 1 + packages/testing/playwright/package.json | 1 + packages/testing/rules-engine/package.json | 1 + packages/testing/test-impact/package.json | 1 + packages/workflow/package.json | 1 + 73 files changed, 287 insertions(+), 3 deletions(-) create mode 100644 packages/@n8n/eslint-config/scripts/spike-apply-boundaries.mjs diff --git a/packages/@n8n/agents/package.json b/packages/@n8n/agents/package.json index 99c33d7f198..bf4a09242ce 100644 --- a/packages/@n8n/agents/package.json +++ b/packages/@n8n/agents/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "sdk", "scope": "ai" }, "name": "@n8n/agents", "version": "0.15.0", "description": "AI agent SDK for n8n's code-first execution engine", diff --git a/packages/@n8n/ai-node-sdk/package.json b/packages/@n8n/ai-node-sdk/package.json index 1c386e76a7a..21e8df6f64f 100644 --- a/packages/@n8n/ai-node-sdk/package.json +++ b/packages/@n8n/ai-node-sdk/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "sdk", "scope": "nodes" }, "name": "@n8n/ai-node-sdk", "version": "0.20.0", "description": "SDK for building AI nodes in n8n", diff --git a/packages/@n8n/ai-utilities/package.json b/packages/@n8n/ai-utilities/package.json index 54f3d23cb21..a75844b0bbc 100644 --- a/packages/@n8n/ai-utilities/package.json +++ b/packages/@n8n/ai-utilities/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "nodes" }, "name": "@n8n/ai-utilities", "version": "0.23.0", "description": "Utilities for building AI nodes in n8n", diff --git a/packages/@n8n/ai-workflow-builder.ee/package.json b/packages/@n8n/ai-workflow-builder.ee/package.json index d626b5a0ecd..1b743cfbc15 100644 --- a/packages/@n8n/ai-workflow-builder.ee/package.json +++ b/packages/@n8n/ai-workflow-builder.ee/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "feature", "scope": "ai" }, "name": "@n8n/ai-workflow-builder", "version": "1.30.0", "scripts": { diff --git a/packages/@n8n/api-types/package.json b/packages/@n8n/api-types/package.json index 306685ff3f7..4b22098a613 100644 --- a/packages/@n8n/api-types/package.json +++ b/packages/@n8n/api-types/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "data-access", "scope": "shared" }, "name": "@n8n/api-types", "version": "1.30.0", "scripts": { diff --git a/packages/@n8n/backend-common/package.json b/packages/@n8n/backend-common/package.json index 3610c56a5ac..df387d652d6 100644 --- a/packages/@n8n/backend-common/package.json +++ b/packages/@n8n/backend-common/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "platform" }, "name": "@n8n/backend-common", "version": "1.30.0", "scripts": { diff --git a/packages/@n8n/backend-network/package.json b/packages/@n8n/backend-network/package.json index 93a0647b91b..d6937c49d57 100644 --- a/packages/@n8n/backend-network/package.json +++ b/packages/@n8n/backend-network/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "data-access", "scope": "platform" }, "name": "@n8n/backend-network", "version": "1.4.0", "scripts": { diff --git a/packages/@n8n/backend-test-utils/package.json b/packages/@n8n/backend-test-utils/package.json index 5c8a07ba2b6..58d7be17605 100644 --- a/packages/@n8n/backend-test-utils/package.json +++ b/packages/@n8n/backend-test-utils/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "tooling" }, "name": "@n8n/backend-test-utils", "version": "1.30.0", "scripts": { diff --git a/packages/@n8n/benchmark/package.json b/packages/@n8n/benchmark/package.json index 94fff0f2f1a..fa53fd7e083 100644 --- a/packages/@n8n/benchmark/package.json +++ b/packages/@n8n/benchmark/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "tooling" }, "name": "@n8n/n8n-benchmark", "version": "2.15.0", "description": "Cli for running benchmark tests for n8n", diff --git a/packages/@n8n/chat-hub/package.json b/packages/@n8n/chat-hub/package.json index 69c27f81dec..a342ced1997 100644 --- a/packages/@n8n/chat-hub/package.json +++ b/packages/@n8n/chat-hub/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "feature", "scope": "ai" }, "name": "@n8n/chat-hub", "version": "1.23.0", "scripts": { diff --git a/packages/@n8n/cli/package.json b/packages/@n8n/cli/package.json index 91393e48f8e..7a9561379d3 100644 --- a/packages/@n8n/cli/package.json +++ b/packages/@n8n/cli/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "app", "scope": "platform" }, "name": "@n8n/cli", "version": "0.10.0", "description": "[beta] Client CLI for n8n — manage workflows, executions, and credentials from the terminal", diff --git a/packages/@n8n/client-oauth2/package.json b/packages/@n8n/client-oauth2/package.json index 6aa2159a2f0..6cb871623d7 100644 --- a/packages/@n8n/client-oauth2/package.json +++ b/packages/@n8n/client-oauth2/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "platform" }, "name": "@n8n/client-oauth2", "version": "1.12.0", "scripts": { diff --git a/packages/@n8n/codemirror-lang-html/package.json b/packages/@n8n/codemirror-lang-html/package.json index 315738eb90a..e674d512943 100644 --- a/packages/@n8n/codemirror-lang-html/package.json +++ b/packages/@n8n/codemirror-lang-html/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "editor" }, "name": "@n8n/codemirror-lang-html", "version": "1.6.0", "description": "HTML + n8n expression language support for CodeMirror 6", diff --git a/packages/@n8n/codemirror-lang-sql/package.json b/packages/@n8n/codemirror-lang-sql/package.json index 885001339c7..23a5279d645 100644 --- a/packages/@n8n/codemirror-lang-sql/package.json +++ b/packages/@n8n/codemirror-lang-sql/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "editor" }, "name": "@n8n/codemirror-lang-sql", "version": "1.11.0", "description": "SQL + n8n expression language support for CodeMirror 6", diff --git a/packages/@n8n/codemirror-lang/package.json b/packages/@n8n/codemirror-lang/package.json index 7c295d4abed..5ccf117954a 100644 --- a/packages/@n8n/codemirror-lang/package.json +++ b/packages/@n8n/codemirror-lang/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "editor" }, "name": "@n8n/codemirror-lang", "version": "0.10.0", "description": "Language support package for CodeMirror 6 in n8n", diff --git a/packages/@n8n/computer-use/package.json b/packages/@n8n/computer-use/package.json index 440b9daf507..e4ff1caf1db 100644 --- a/packages/@n8n/computer-use/package.json +++ b/packages/@n8n/computer-use/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "app", "scope": "ai" }, "name": "@n8n/computer-use", "version": "0.14.0", "description": "Local AI gateway for n8n AI Assistant — filesystem, shell, screenshots, mouse/keyboard, and browser automation", diff --git a/packages/@n8n/config/package.json b/packages/@n8n/config/package.json index 9aa18982c3d..6ff05d7ccea 100644 --- a/packages/@n8n/config/package.json +++ b/packages/@n8n/config/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "shared" }, "name": "@n8n/config", "version": "2.28.0", "scripts": { diff --git a/packages/@n8n/constants/package.json b/packages/@n8n/constants/package.json index f9bf06d0ca8..1591002c8de 100644 --- a/packages/@n8n/constants/package.json +++ b/packages/@n8n/constants/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "shared" }, "name": "@n8n/constants", "version": "0.30.0", "scripts": { diff --git a/packages/@n8n/crdt/package.json b/packages/@n8n/crdt/package.json index c37b2fc4b46..b623672dda7 100644 --- a/packages/@n8n/crdt/package.json +++ b/packages/@n8n/crdt/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "persistence" }, "name": "@n8n/crdt", "version": "0.8.0", "description": "CRDT abstraction layer for n8n collaborative editing", diff --git a/packages/@n8n/create-node/package.json b/packages/@n8n/create-node/package.json index e1fc475d474..0058377135e 100644 --- a/packages/@n8n/create-node/package.json +++ b/packages/@n8n/create-node/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "nodes" }, "name": "@n8n/create-node", "version": "0.38.0", "description": "Official CLI to create new community nodes for n8n", diff --git a/packages/@n8n/db/package.json b/packages/@n8n/db/package.json index c8851f8f3d3..f0a4b947bec 100644 --- a/packages/@n8n/db/package.json +++ b/packages/@n8n/db/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "data-access", "scope": "persistence" }, "name": "@n8n/db", "version": "1.30.0", "scripts": { diff --git a/packages/@n8n/decorators/package.json b/packages/@n8n/decorators/package.json index ff887b22a80..f2f0687ca66 100644 --- a/packages/@n8n/decorators/package.json +++ b/packages/@n8n/decorators/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "shared" }, "name": "@n8n/decorators", "version": "1.30.0", "scripts": { diff --git a/packages/@n8n/di/package.json b/packages/@n8n/di/package.json index bbc7785b399..236a874ad9d 100644 --- a/packages/@n8n/di/package.json +++ b/packages/@n8n/di/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "shared" }, "name": "@n8n/di", "version": "0.14.0", "scripts": { diff --git a/packages/@n8n/engine/package.json b/packages/@n8n/engine/package.json index c9dfb4b9a54..c31454c4c32 100644 --- a/packages/@n8n/engine/package.json +++ b/packages/@n8n/engine/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "runtime", "scope": "workflow-engine" }, "name": "@n8n/engine", "version": "0.10.0", "description": "n8n workflow execution engine (v2)", diff --git a/packages/@n8n/errors/package.json b/packages/@n8n/errors/package.json index 86d4b66c9c3..dbf6c88989f 100644 --- a/packages/@n8n/errors/package.json +++ b/packages/@n8n/errors/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "shared" }, "name": "@n8n/errors", "version": "0.11.0", "scripts": { diff --git a/packages/@n8n/eslint-config/package.json b/packages/@n8n/eslint-config/package.json index 5f54c0bf9f7..ba5cdb4d931 100644 --- a/packages/@n8n/eslint-config/package.json +++ b/packages/@n8n/eslint-config/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "tooling" }, "private": true, "name": "@n8n/eslint-config", "type": "module", diff --git a/packages/@n8n/eslint-config/scripts/spike-apply-boundaries.mjs b/packages/@n8n/eslint-config/scripts/spike-apply-boundaries.mjs new file mode 100644 index 00000000000..d30879d48f5 --- /dev/null +++ b/packages/@n8n/eslint-config/scripts/spike-apply-boundaries.mjs @@ -0,0 +1,185 @@ +#!/usr/bin/env node +/** + * SPIKE: stamp `boundaries: { type, scope }` into every workspace package.json, + * then validate the real workspace dependency graph against the module-boundary + * matrices. Run from the repo root: `node packages/@n8n/eslint-config/scripts/spike-apply-boundaries.mjs` + * + * Not production code — this is the throwaway harness that answers "what breaks + * if we adopt the tags in the table". The real tag source would be a generator + * wired into the eslint config; here we embed the mapping so it stays reviewable. + * + * NOTE: uses key `boundaries`, NOT `n8n` — the `n8n` key is already taken by + * node/credential registration in nodes-base et al. + */ +import { readFileSync, writeFileSync, readdirSync, existsSync } from 'node:fs'; +import { join } from 'node:path'; + +// name -> [type, scope]. Mirrors the assessment table (extended type set). +const TAGS = { + // shared + '@n8n/utils': ['util', 'shared'], + '@n8n/constants': ['util', 'shared'], + '@n8n/errors': ['util', 'shared'], + '@n8n/di': ['util', 'shared'], + '@n8n/decorators': ['util', 'shared'], + '@n8n/config': ['util', 'shared'], + '@n8n/permissions': ['util', 'shared'], + '@n8n/i18n': ['util', 'shared'], + '@n8n/json-schema-to-zod': ['util', 'shared'], + '@n8n/api-types': ['data-access', 'shared'], + // workflow-engine + 'n8n-workflow': ['runtime', 'workflow-engine'], + 'n8n-core': ['runtime', 'workflow-engine'], + '@n8n/engine': ['runtime', 'workflow-engine'], + '@n8n/expression-runtime': ['runtime', 'workflow-engine'], + '@n8n/scheduler': ['util', 'workflow-engine'], + '@n8n/tournament': ['util', 'workflow-engine'], + '@n8n/task-runner': ['app', 'workflow-engine'], + '@n8n/workflow-sdk': ['sdk', 'workflow-engine'], + // nodes + 'n8n-nodes-base': ['nodes', 'nodes'], + '@n8n/n8n-nodes-langchain': ['nodes', 'nodes'], + '@n8n/ai-node-sdk': ['sdk', 'nodes'], + '@n8n/ai-utilities': ['util', 'nodes'], + '@n8n/extension-sdk': ['sdk', 'nodes'], + '@n8n/create-node': ['tooling', 'nodes'], + '@n8n/node-cli': ['tooling', 'nodes'], + 'n8n-node-dev': ['tooling', 'nodes'], + '@n8n/scan-community-package': ['tooling', 'nodes'], + '@n8n/eslint-plugin-community-nodes': ['tooling', 'nodes'], + // ai + '@n8n/instance-ai': ['feature', 'ai'], + '@n8n/ai-workflow-builder': ['feature', 'ai'], + '@n8n/agents': ['sdk', 'ai'], + '@n8n/computer-use': ['app', 'ai'], + '@n8n/mcp-apps': ['feature', 'ai'], + '@n8n/mcp-browser': ['util', 'ai'], + '@n8n/mcp-browser-extension': ['app', 'ai'], + '@n8n/chat-hub': ['feature', 'ai'], + '@n8n/chat': ['feature', 'ai'], + // editor (frontend) + 'n8n-editor-ui': ['app', 'editor'], + '@n8n/design-system': ['ui', 'editor'], + '@n8n/stores': ['data-access', 'editor'], + '@n8n/rest-api-client': ['data-access', 'editor'], + '@n8n/composables': ['util', 'editor'], + '@n8n/codemirror-lang': ['util', 'editor'], + '@n8n/codemirror-lang-html': ['util', 'editor'], + '@n8n/codemirror-lang-sql': ['util', 'editor'], + // persistence + '@n8n/db': ['data-access', 'persistence'], + '@n8n/typeorm': ['util', 'persistence'], + '@n8n/crdt': ['util', 'persistence'], + // platform (backend server) + n8n: ['app', 'platform'], + '@n8n/cli': ['app', 'platform'], + '@n8n/backend-common': ['util', 'platform'], + '@n8n/backend-network': ['data-access', 'platform'], + '@n8n/local-gateway': ['app', 'platform'], + '@n8n/syslog-client': ['util', 'platform'], + '@n8n/imap': ['util', 'platform'], + '@n8n/client-oauth2': ['util', 'platform'], + // insights + '@n8n/n8n-extension-insights': ['feature', 'insights'], + // tooling + '@n8n/eslint-config': ['tooling', 'tooling'], + '@n8n/typescript-config': ['tooling', 'tooling'], + '@n8n/vitest-config': ['tooling', 'tooling'], + '@n8n/stylelint-config': ['tooling', 'tooling'], + '@n8n/storybook': ['tooling', 'tooling'], + '@n8n/code-health': ['tooling', 'tooling'], + '@n8n/test-impact': ['tooling', 'tooling'], + '@n8n/playwright-janitor': ['tooling', 'tooling'], + 'n8n-playwright': ['tooling', 'tooling'], + 'n8n-containers': ['tooling', 'tooling'], + '@n8n/performance': ['tooling', 'tooling'], + '@n8n/n8n-benchmark': ['tooling', 'tooling'], + '@n8n/backend-test-utils': ['tooling', 'tooling'], + '@n8n/rules-engine': ['tooling', 'tooling'], +}; + +const ALL = [ + 'app', 'feature', 'runtime', 'nodes', 'sdk', 'data-access', 'domain-ui', 'ui', 'util', 'tooling', +]; +const ALLOWED_TYPES = { + app: ALL, + tooling: ALL, + feature: ['data-access', 'domain-ui', 'ui', 'util', 'sdk'], + runtime: ['runtime', 'data-access', 'util'], + nodes: ['nodes', 'runtime', 'data-access', 'sdk', 'util'], + sdk: ['sdk', 'runtime', 'data-access', 'util'], + 'data-access': ['data-access', 'util'], + 'domain-ui': ['domain-ui', 'ui', 'util'], + ui: ['ui', 'util'], + util: ['util'], +}; +const SCOPE_EXEMPT_TYPES = new Set(['app', 'tooling']); +const SHARED_SCOPES = new Set(['shared', 'tooling']); + +// --- discover package.json files --- +const roots = [ + 'packages', + 'packages/@n8n', + 'packages/frontend', + 'packages/frontend/@n8n', + 'packages/extensions', + 'packages/testing', +]; +const pkgFiles = []; +for (const root of roots) { + if (!existsSync(root)) continue; + for (const entry of readdirSync(root, { withFileTypes: true })) { + if (!entry.isDirectory()) continue; + const pj = join(root, entry.name, 'package.json'); + if (existsSync(pj)) pkgFiles.push(pj); + } +} + +// --- stamp tags (textual insert to keep diffs to one line) --- +const byName = {}; +let stamped = 0; +const untagged = []; +for (const pj of pkgFiles) { + const raw = readFileSync(pj, 'utf8'); + const pkg = JSON.parse(raw); + byName[pkg.name] = { pj, pkg }; + const tag = TAGS[pkg.name]; + if (!tag) { untagged.push(pkg.name); continue; } + const [type, scope] = tag; + if (raw.includes('"boundaries"')) { stamped++; continue; } + const insert = ` "boundaries": { "type": "${type}", "scope": "${scope}" },\n`; + writeFileSync(pj, raw.replace(/^{\n/, `{\n${insert}`)); + stamped++; +} + +// --- validate the runtime dependency graph --- +const names = new Set(Object.keys(byName)); +const typeViol = []; +const scopeViol = []; +for (const [name, { pkg }] of Object.entries(byName)) { + const tag = TAGS[name]; + if (!tag) continue; + const [fromType, fromScope] = tag; + for (const dep of Object.keys(pkg.dependencies ?? {})) { + if (!names.has(dep) || dep === name) continue; + const toTag = TAGS[dep]; + if (!toTag) continue; + const [toType, toScope] = toTag; + if (!ALLOWED_TYPES[fromType].includes(toType)) { + typeViol.push(`${name} (${fromType}) -> ${dep} (${toType})`); + continue; + } + const scopeOk = + SCOPE_EXEMPT_TYPES.has(fromType) || + fromScope === toScope || + SHARED_SCOPES.has(toScope); + if (!scopeOk) scopeViol.push(`${name} [${fromScope}] -> ${dep} [${toScope}]`); + } +} + +console.log(`\n=== stamped ${stamped}/${pkgFiles.length} package.json files ===`); +if (untagged.length) console.log(`untagged (no mapping): ${untagged.join(', ')}`); +console.log(`\n=== TYPE violations (${typeViol.length}) ===`); +typeViol.sort().forEach((v) => console.log(' ' + v)); +console.log(`\n=== SCOPE violations (${scopeViol.length}) — need explicit opt-in ===`); +scopeViol.sort().forEach((v) => console.log(' ' + v)); diff --git a/packages/@n8n/eslint-config/src/rules/enforce-module-boundaries.ts b/packages/@n8n/eslint-config/src/rules/enforce-module-boundaries.ts index 7ba6abec0fa..2ce6b218e8f 100644 --- a/packages/@n8n/eslint-config/src/rules/enforce-module-boundaries.ts +++ b/packages/@n8n/eslint-config/src/rules/enforce-module-boundaries.ts @@ -19,7 +19,17 @@ import { ESLintUtils } from '@typescript-eslint/utils'; * `n8n.tags` field at config-build time (one fs scan, cached). Add when adopted. */ -export type BoundaryType = 'app' | 'feature' | 'data-access' | 'domain-ui' | 'ui' | 'util'; +export type BoundaryType = + | 'app' + | 'feature' + | 'runtime' + | 'nodes' + | 'sdk' + | 'data-access' + | 'domain-ui' + | 'ui' + | 'util' + | 'tooling'; export interface PackageTag { name: string; @@ -40,9 +50,26 @@ export interface Options { // Konstantin's hierarchy: apps glue everything, features slice vertically and // must not import each other, data-access stays UI-free, utils are universal. +const ALL_TYPES: BoundaryType[] = [ + 'app', + 'feature', + 'runtime', + 'nodes', + 'sdk', + 'data-access', + 'domain-ui', + 'ui', + 'util', + 'tooling', +]; + const DEFAULT_ALLOWED_TYPES: Record = { - app: ['app', 'feature', 'data-access', 'domain-ui', 'ui', 'util'], - feature: ['data-access', 'domain-ui', 'ui', 'util'], + app: ALL_TYPES, // apps glue everything together + tooling: ALL_TYPES, // dev/CI only, not in any runtime bundle + feature: ['data-access', 'domain-ui', 'ui', 'util', 'sdk'], + runtime: ['runtime', 'data-access', 'util'], + nodes: ['nodes', 'runtime', 'data-access', 'sdk', 'util'], + sdk: ['sdk', 'runtime', 'data-access', 'util'], 'data-access': ['data-access', 'util'], 'domain-ui': ['domain-ui', 'ui', 'util'], ui: ['ui', 'util'], @@ -112,6 +139,7 @@ export const EnforceModuleBoundariesRule = ESLintUtils.RuleCreator.withoutDocs({ const scopeAllowed = fromPkg.type === 'app' || // apps glue every scope together + fromPkg.type === 'tooling' || // dev/CI crosses every domain fromPkg.scope === toPkg.scope || sharedScopes.includes(toPkg.scope) || (allowedScopeDependencies[fromPkg.scope] ?? []).includes(toPkg.scope); diff --git a/packages/@n8n/eslint-plugin-community-nodes/package.json b/packages/@n8n/eslint-plugin-community-nodes/package.json index 29aeec16539..d76689605fc 100644 --- a/packages/@n8n/eslint-plugin-community-nodes/package.json +++ b/packages/@n8n/eslint-plugin-community-nodes/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "nodes" }, "name": "@n8n/eslint-plugin-community-nodes", "type": "module", "version": "0.24.0", diff --git a/packages/@n8n/expression-runtime/package.json b/packages/@n8n/expression-runtime/package.json index 97d2609e4f7..a93cf0fafcd 100644 --- a/packages/@n8n/expression-runtime/package.json +++ b/packages/@n8n/expression-runtime/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "runtime", "scope": "workflow-engine" }, "name": "@n8n/expression-runtime", "version": "0.21.0", "description": "Secure, isolated expression evaluation runtime for n8n", diff --git a/packages/@n8n/extension-sdk/package.json b/packages/@n8n/extension-sdk/package.json index 2262e1c1f41..5275f63a614 100644 --- a/packages/@n8n/extension-sdk/package.json +++ b/packages/@n8n/extension-sdk/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "sdk", "scope": "nodes" }, "name": "@n8n/extension-sdk", "version": "0.14.0", "type": "module", diff --git a/packages/@n8n/imap/package.json b/packages/@n8n/imap/package.json index 234ce20a1af..09f0a1a276c 100644 --- a/packages/@n8n/imap/package.json +++ b/packages/@n8n/imap/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "platform" }, "name": "@n8n/imap", "version": "0.23.0", "scripts": { diff --git a/packages/@n8n/instance-ai/package.json b/packages/@n8n/instance-ai/package.json index 14a143b9cb9..35c07c96a46 100644 --- a/packages/@n8n/instance-ai/package.json +++ b/packages/@n8n/instance-ai/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "feature", "scope": "ai" }, "name": "@n8n/instance-ai", "version": "1.15.0", "scripts": { diff --git a/packages/@n8n/json-schema-to-zod/package.json b/packages/@n8n/json-schema-to-zod/package.json index 3c846596d92..5533f3506cb 100644 --- a/packages/@n8n/json-schema-to-zod/package.json +++ b/packages/@n8n/json-schema-to-zod/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "shared" }, "name": "@n8n/json-schema-to-zod", "version": "1.13.0", "description": "Converts JSON schema objects into Zod schemas", diff --git a/packages/@n8n/local-gateway/package.json b/packages/@n8n/local-gateway/package.json index dee789ad907..15fb368cd01 100644 --- a/packages/@n8n/local-gateway/package.json +++ b/packages/@n8n/local-gateway/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "app", "scope": "platform" }, "name": "@n8n/local-gateway", "version": "0.1.0", "description": "n8n Local Gateway", diff --git a/packages/@n8n/mcp-apps/package.json b/packages/@n8n/mcp-apps/package.json index 780726b9411..97eb9a50f6e 100644 --- a/packages/@n8n/mcp-apps/package.json +++ b/packages/@n8n/mcp-apps/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "feature", "scope": "ai" }, "name": "@n8n/mcp-apps", "version": "0.7.0", "description": "MCP Apps UI resources and server helpers for n8n", diff --git a/packages/@n8n/mcp-browser-extension/package.json b/packages/@n8n/mcp-browser-extension/package.json index fb47785e736..5889a21fe2e 100644 --- a/packages/@n8n/mcp-browser-extension/package.json +++ b/packages/@n8n/mcp-browser-extension/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "app", "scope": "ai" }, "name": "@n8n/mcp-browser-extension", "version": "0.0.4", "private": true, diff --git a/packages/@n8n/mcp-browser/package.json b/packages/@n8n/mcp-browser/package.json index e6ddc862dfb..72d00f808c2 100644 --- a/packages/@n8n/mcp-browser/package.json +++ b/packages/@n8n/mcp-browser/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "ai" }, "name": "@n8n/mcp-browser", "version": "0.13.0", "description": "Browser automation MCP tools built on Playwright, WebDriver BiDi, and safaridriver", diff --git a/packages/@n8n/node-cli/package.json b/packages/@n8n/node-cli/package.json index 25c4f4d0a88..e4cb996d0f7 100644 --- a/packages/@n8n/node-cli/package.json +++ b/packages/@n8n/node-cli/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "nodes" }, "name": "@n8n/node-cli", "version": "0.39.0", "description": "Official CLI for developing community nodes for n8n", diff --git a/packages/@n8n/nodes-langchain/package.json b/packages/@n8n/nodes-langchain/package.json index 61f5af501e3..851eb415ead 100644 --- a/packages/@n8n/nodes-langchain/package.json +++ b/packages/@n8n/nodes-langchain/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "nodes", "scope": "nodes" }, "name": "@n8n/n8n-nodes-langchain", "version": "2.30.0", "description": "", diff --git a/packages/@n8n/permissions/package.json b/packages/@n8n/permissions/package.json index 4d458ce52ac..cdea8f4a031 100644 --- a/packages/@n8n/permissions/package.json +++ b/packages/@n8n/permissions/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "shared" }, "name": "@n8n/permissions", "version": "0.67.0", "scripts": { diff --git a/packages/@n8n/scan-community-package/package.json b/packages/@n8n/scan-community-package/package.json index a5ffe2fdb35..fd769e69226 100644 --- a/packages/@n8n/scan-community-package/package.json +++ b/packages/@n8n/scan-community-package/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "nodes" }, "name": "@n8n/scan-community-package", "version": "0.26.0", "description": "Static code analyser for n8n community packages", diff --git a/packages/@n8n/scheduler/package.json b/packages/@n8n/scheduler/package.json index c6088eab243..297b09d6d4e 100644 --- a/packages/@n8n/scheduler/package.json +++ b/packages/@n8n/scheduler/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "workflow-engine" }, "name": "@n8n/scheduler", "version": "0.2.0", "scripts": { diff --git a/packages/@n8n/stylelint-config/package.json b/packages/@n8n/stylelint-config/package.json index e20e2befcdd..2217cb7ece0 100644 --- a/packages/@n8n/stylelint-config/package.json +++ b/packages/@n8n/stylelint-config/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "tooling" }, "private": true, "name": "@n8n/stylelint-config", "type": "module", diff --git a/packages/@n8n/syslog-client/package.json b/packages/@n8n/syslog-client/package.json index 73043d55283..9080b454a11 100644 --- a/packages/@n8n/syslog-client/package.json +++ b/packages/@n8n/syslog-client/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "platform" }, "name": "@n8n/syslog-client", "version": "1.7.0", "scripts": { diff --git a/packages/@n8n/task-runner/package.json b/packages/@n8n/task-runner/package.json index a6b4ca19d9b..42b6caa994d 100644 --- a/packages/@n8n/task-runner/package.json +++ b/packages/@n8n/task-runner/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "app", "scope": "workflow-engine" }, "name": "@n8n/task-runner", "version": "2.30.0", "scripts": { diff --git a/packages/@n8n/tournament/package.json b/packages/@n8n/tournament/package.json index c0d9a5f4756..d18e0570f37 100644 --- a/packages/@n8n/tournament/package.json +++ b/packages/@n8n/tournament/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "workflow-engine" }, "name": "@n8n/tournament", "version": "1.6.0", "description": "Output compatible rewrite of riot tmpl", diff --git a/packages/@n8n/typeorm/package.json b/packages/@n8n/typeorm/package.json index c1b88c182cb..d735e1e32a1 100644 --- a/packages/@n8n/typeorm/package.json +++ b/packages/@n8n/typeorm/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "persistence" }, "name": "@n8n/typeorm", "version": "0.4.0", "description": "Data-Mapper ORM for TypeScript, ES7, ES6, ES5. Supports PostgreSQL and SQLite databases.", diff --git a/packages/@n8n/typescript-config/package.json b/packages/@n8n/typescript-config/package.json index 21d6e0b1918..fbaf72a4640 100644 --- a/packages/@n8n/typescript-config/package.json +++ b/packages/@n8n/typescript-config/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "tooling" }, "name": "@n8n/typescript-config", "version": "1.8.0", "type": "module", diff --git a/packages/@n8n/utils/package.json b/packages/@n8n/utils/package.json index fe21afd1f02..269e886e689 100644 --- a/packages/@n8n/utils/package.json +++ b/packages/@n8n/utils/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "shared" }, "name": "@n8n/utils", "type": "module", "version": "1.38.0", diff --git a/packages/@n8n/vitest-config/package.json b/packages/@n8n/vitest-config/package.json index 08214e5dba1..5387d4dc546 100644 --- a/packages/@n8n/vitest-config/package.json +++ b/packages/@n8n/vitest-config/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "tooling" }, "name": "@n8n/vitest-config", "version": "1.17.0", "type": "module", diff --git a/packages/@n8n/workflow-sdk/package.json b/packages/@n8n/workflow-sdk/package.json index f7134750a25..b16dd21ab46 100644 --- a/packages/@n8n/workflow-sdk/package.json +++ b/packages/@n8n/workflow-sdk/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "sdk", "scope": "workflow-engine" }, "name": "@n8n/workflow-sdk", "version": "0.23.0", "description": "TypeScript SDK for programmatically creating n8n workflows", diff --git a/packages/cli/package.json b/packages/cli/package.json index 9e46057ce05..2c94be1a91e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "app", "scope": "platform" }, "name": "n8n", "version": "2.30.0", "description": "n8n Workflow Automation Tool", diff --git a/packages/core/package.json b/packages/core/package.json index ec79ee5f64b..0a3ec868c7e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "runtime", "scope": "workflow-engine" }, "name": "n8n-core", "version": "2.30.0", "description": "Core functionality of n8n", diff --git a/packages/extensions/insights/package.json b/packages/extensions/insights/package.json index 2e5115df4ae..2181f9bc51f 100644 --- a/packages/extensions/insights/package.json +++ b/packages/extensions/insights/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "feature", "scope": "insights" }, "name": "@n8n/n8n-extension-insights", "version": "0.17.0", "type": "module", diff --git a/packages/frontend/@n8n/chat/package.json b/packages/frontend/@n8n/chat/package.json index 9bde7ede8c3..b8aacce5d4b 100644 --- a/packages/frontend/@n8n/chat/package.json +++ b/packages/frontend/@n8n/chat/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "feature", "scope": "ai" }, "name": "@n8n/chat", "version": "1.29.0", "scripts": { diff --git a/packages/frontend/@n8n/composables/package.json b/packages/frontend/@n8n/composables/package.json index e277aad827c..36d20275ec9 100644 --- a/packages/frontend/@n8n/composables/package.json +++ b/packages/frontend/@n8n/composables/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "editor" }, "name": "@n8n/composables", "type": "module", "version": "1.20.0", diff --git a/packages/frontend/@n8n/design-system/package.json b/packages/frontend/@n8n/design-system/package.json index 514a23c079f..124a6acfe99 100644 --- a/packages/frontend/@n8n/design-system/package.json +++ b/packages/frontend/@n8n/design-system/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "ui", "scope": "editor" }, "type": "module", "name": "@n8n/design-system", "version": "2.29.0", diff --git a/packages/frontend/@n8n/i18n/package.json b/packages/frontend/@n8n/i18n/package.json index 1fd7212c16d..1b9e3928c9d 100644 --- a/packages/frontend/@n8n/i18n/package.json +++ b/packages/frontend/@n8n/i18n/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "util", "scope": "shared" }, "name": "@n8n/i18n", "type": "module", "version": "2.30.0", diff --git a/packages/frontend/@n8n/rest-api-client/package.json b/packages/frontend/@n8n/rest-api-client/package.json index 0fc64afa12e..82576fb91e6 100644 --- a/packages/frontend/@n8n/rest-api-client/package.json +++ b/packages/frontend/@n8n/rest-api-client/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "data-access", "scope": "editor" }, "name": "@n8n/rest-api-client", "type": "module", "version": "2.30.0", diff --git a/packages/frontend/@n8n/stores/package.json b/packages/frontend/@n8n/stores/package.json index 6f25ccf5f77..7ceb4cb41b4 100644 --- a/packages/frontend/@n8n/stores/package.json +++ b/packages/frontend/@n8n/stores/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "data-access", "scope": "editor" }, "name": "@n8n/stores", "type": "module", "version": "2.30.0", diff --git a/packages/frontend/@n8n/storybook/package.json b/packages/frontend/@n8n/storybook/package.json index 2fe28a9c757..8ff4d584a3d 100644 --- a/packages/frontend/@n8n/storybook/package.json +++ b/packages/frontend/@n8n/storybook/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "tooling" }, "name": "@n8n/storybook", "private": true, "version": "0.0.0", diff --git a/packages/frontend/editor-ui/package.json b/packages/frontend/editor-ui/package.json index 4326d595277..0465c3cfa19 100644 --- a/packages/frontend/editor-ui/package.json +++ b/packages/frontend/editor-ui/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "app", "scope": "editor" }, "name": "n8n-editor-ui", "version": "2.30.0", "description": "Workflow Editor UI for n8n", diff --git a/packages/node-dev/package.json b/packages/node-dev/package.json index 57eea1f6f09..64dc82e6d4d 100644 --- a/packages/node-dev/package.json +++ b/packages/node-dev/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "nodes" }, "name": "n8n-node-dev", "version": "2.9.0", "description": "CLI to simplify n8n credentials/node development", diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index e3b21eb817f..1b2225ae967 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "nodes", "scope": "nodes" }, "name": "n8n-nodes-base", "version": "2.30.0", "description": "Base nodes of n8n", diff --git a/packages/testing/code-health/package.json b/packages/testing/code-health/package.json index 41b4afbf1b6..2cc90ec6af4 100644 --- a/packages/testing/code-health/package.json +++ b/packages/testing/code-health/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "tooling" }, "name": "@n8n/code-health", "private": true, "version": "0.1.0", diff --git a/packages/testing/containers/package.json b/packages/testing/containers/package.json index c9236a6e6cd..babb5f1cbca 100644 --- a/packages/testing/containers/package.json +++ b/packages/testing/containers/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "tooling" }, "name": "n8n-containers", "private": true, "version": "1.0.0", diff --git a/packages/testing/janitor/package.json b/packages/testing/janitor/package.json index 93cd5763cca..2cbe81ddf1e 100644 --- a/packages/testing/janitor/package.json +++ b/packages/testing/janitor/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "tooling" }, "name": "@n8n/playwright-janitor", "private": true, "version": "0.1.0", diff --git a/packages/testing/performance/package.json b/packages/testing/performance/package.json index 246dfc6d095..098197698a6 100644 --- a/packages/testing/performance/package.json +++ b/packages/testing/performance/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "tooling" }, "name": "@n8n/performance", "version": "1.0.0", "private": true, diff --git a/packages/testing/playwright/package.json b/packages/testing/playwright/package.json index 908efef11d6..90e7e89ae34 100644 --- a/packages/testing/playwright/package.json +++ b/packages/testing/playwright/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "tooling" }, "name": "n8n-playwright", "private": true, "scripts": { diff --git a/packages/testing/rules-engine/package.json b/packages/testing/rules-engine/package.json index 03668e75675..059039b52a3 100644 --- a/packages/testing/rules-engine/package.json +++ b/packages/testing/rules-engine/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "tooling" }, "name": "@n8n/rules-engine", "private": true, "version": "0.1.0", diff --git a/packages/testing/test-impact/package.json b/packages/testing/test-impact/package.json index f70cc2a5e8c..5542c1f31a0 100644 --- a/packages/testing/test-impact/package.json +++ b/packages/testing/test-impact/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "tooling", "scope": "tooling" }, "name": "@n8n/test-impact", "private": true, "version": "0.1.0", diff --git a/packages/workflow/package.json b/packages/workflow/package.json index 5eed2c7936d..13b790bb011 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -1,4 +1,5 @@ { + "boundaries": { "type": "runtime", "scope": "workflow-engine" }, "name": "n8n-workflow", "version": "2.30.0", "description": "Workflow base code of n8n",