From 918bcddb4f2144f668f39b912d137ae242375eee Mon Sep 17 00:00:00 2001 From: Charlie Kolb Date: Tue, 29 Sep 2026 06:47:57 +0000 Subject: [PATCH 01/55] fix(core): Build email templates on Windows (no-changelog) (#39823) --- packages/cli/scripts/build.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/scripts/build.mjs b/packages/cli/scripts/build.mjs index f92ee2dfa07..3a532751098 100644 --- a/packages/cli/scripts/build.mjs +++ b/packages/cli/scripts/build.mjs @@ -39,7 +39,7 @@ function generateUserManagementEmailTemplates() { // One mjml process for all templates: each `pnpm mjml` start costs about 0.3s. // With several inputs, mjml writes `.html` into the output directory. const sources = templates.map((template) => `"${path.resolve(sourceDir, template)}"`).join(' '); - const result = shell.exec(`pnpm mjml ${sources} --output "${destinationDir}${path.sep}"`, { + const result = shell.exec(`pnpm mjml ${sources} --output "${destinationDir}"`, { silent: false, }); if (result.code !== 0) { From dfd7a522244720ca536e73b8cb3da7802a850a48 Mon Sep 17 00:00:00 2001 From: Ali Elkhateeb Date: Tue, 29 Sep 2026 06:53:58 +0000 Subject: [PATCH 02/55] fix(API): Return an execution when its stored trace context is incomplete (#39715) Co-authored-by: Cursor --- .../cli/src/events/maps/relay.event-map.ts | 3 +- .../support/otel-integration-utils.ts | 3 +- .../executions.public.controller.ts | 13 +++- .../repositories/execution.repository.test.ts | 4 +- .../integration/public-api/executions.test.ts | 65 ++++++++++++++++++- .../test/integration/shared/db/executions.ts | 2 + 6 files changed, 85 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/events/maps/relay.event-map.ts b/packages/cli/src/events/maps/relay.event-map.ts index 2e37e36efe5..bb26886efb9 100644 --- a/packages/cli/src/events/maps/relay.event-map.ts +++ b/packages/cli/src/events/maps/relay.event-map.ts @@ -30,6 +30,7 @@ import type { PolicyAttachment, PolicyRule, } from '@/modules/type-availability-policies/policy-rule.types'; +import type { TracingContext } from '@/modules/otel/tracing-context'; import type { McpCallerAuth } from '@/services/oauth-token-verifier-proxy.service'; import type { AiEventMap } from './ai.event-map'; @@ -658,7 +659,7 @@ export type RelayEventMap = { stoppedAt: Date; detector: CrashDetector; hostId: string; - tracingContext?: { traceparent: string; tracestate?: string }; + tracingContext?: TracingContext; workflowVersionId?: string; retryOf?: string; workflowCustomTelemetryTags?: IWorkflowSettings['customTelemetryTags']; diff --git a/packages/cli/src/modules/otel/__tests__/support/otel-integration-utils.ts b/packages/cli/src/modules/otel/__tests__/support/otel-integration-utils.ts index dbc1c192a7f..cea55ad8ae5 100644 --- a/packages/cli/src/modules/otel/__tests__/support/otel-integration-utils.ts +++ b/packages/cli/src/modules/otel/__tests__/support/otel-integration-utils.ts @@ -26,6 +26,7 @@ import { TestNodeWithTracing } from './test-node-with-tracing'; import { OtelSettingsService } from '../../otel-settings.service'; import { OtelConfig } from '../../otel.config'; import { OtelService } from '../../otel.service'; +import type { TracingContext } from '../../tracing-context'; const BASE_DIR = path.resolve(__dirname, '../../../../../..'); @@ -117,7 +118,7 @@ export async function executeWorkflow( options: { mode?: 'webhook' | 'trigger' | 'manual' | 'retry'; retryOf?: string; - tracingContext?: { traceparent: string; tracestate?: string }; + tracingContext?: TracingContext; triggerData?: IDataObject; } = {}, ): Promise { diff --git a/packages/cli/src/public-api/v1/controllers/executions.public.controller.ts b/packages/cli/src/public-api/v1/controllers/executions.public.controller.ts index ff45fcbf85e..a0d770ad638 100644 --- a/packages/cli/src/public-api/v1/controllers/executions.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/executions.public.controller.ts @@ -33,6 +33,7 @@ import { Put, Query, } from '@n8n/decorators'; +import { isRecord } from '@n8n/utils/is-record'; import type { Response } from 'express'; import { replaceCircularReferences, WorkflowOperationError } from 'n8n-workflow'; @@ -47,6 +48,7 @@ import { isRedactableExecution } from '@/executions/execution-redaction'; import { ExecutionRedactionServiceProxy } from '@/executions/execution-redaction-proxy.service'; import { ExecutionService } from '@/executions/execution.service'; import type { StopResult } from '@/executions/execution.types'; +import type { TracingContext } from '@/modules/otel/tracing-context'; import { decodeCursor, encodeNextCursor } from '@/public-api/v1/shared/services/pagination.service'; import { WorkflowSharingService } from '@/workflows/workflow-sharing.service'; @@ -434,6 +436,15 @@ export class ExecutionsPublicController { } } +function toPublicTracingContext(tracingContext: unknown): TracingContext | null { + if (!isRecord(tracingContext)) return null; + + const { traceparent, tracestate } = tracingContext; + if (typeof traceparent !== 'string' || traceparent.length === 0) return null; + + return typeof tracestate === 'string' ? { traceparent, tracestate } : { traceparent }; +} + function toBaseFields(execution: PublicExecution) { return { finished: execution.finished, @@ -448,7 +459,7 @@ function toBaseFields(execution: PublicExecution) { workflowId: execution.workflowId, waitTill: execution.waitTill ? execution.waitTill.toISOString() : null, storedAt: execution.storedAt, - tracingContext: execution.tracingContext ?? null, + tracingContext: toPublicTracingContext(execution.tracingContext), deduplicationKey: execution.deduplicationKey ?? null, jsonSizeBytes: execution.jsonSizeBytes ?? 0, binaryDataSizeBytes: execution.binaryDataSizeBytes ?? 0, diff --git a/packages/cli/test/integration/database/repositories/execution.repository.test.ts b/packages/cli/test/integration/database/repositories/execution.repository.test.ts index 58879b1bec3..22cf7d7c149 100644 --- a/packages/cli/test/integration/database/repositories/execution.repository.test.ts +++ b/packages/cli/test/integration/database/repositories/execution.repository.test.ts @@ -6,6 +6,8 @@ import { stringify } from 'flatted'; import type { ExecutionStatus, IRunExecutionData, IRunExecutionDataAll } from 'n8n-workflow'; import { WAIT_FOR_SUB_EXECUTION, WAIT_INDEFINITELY } from 'n8n-workflow'; +import type { TracingContext } from '@/modules/otel/tracing-context'; + describe('ExecutionRepository', () => { beforeAll(async () => { await testDb.init(); @@ -192,7 +194,7 @@ describe('ExecutionRepository', () => { status: ExecutionStatus, extra: { waitTill?: Date; - tracingContext?: { traceparent: string }; + tracingContext?: TracingContext; workflowVersionId?: string; retryOf?: string; } = {}, diff --git a/packages/cli/test/integration/public-api/executions.test.ts b/packages/cli/test/integration/public-api/executions.test.ts index 41dbb7046c6..2197061a847 100644 --- a/packages/cli/test/integration/public-api/executions.test.ts +++ b/packages/cli/test/integration/public-api/executions.test.ts @@ -1,3 +1,4 @@ +import { randomBytes } from 'node:crypto'; import { createManyWorkflows, createTeamProject, @@ -93,6 +94,12 @@ const testWithAPIKey = expect(response.statusCode).toBe(401); }; +function createTraceparent() { + const traceId = randomBytes(16).toString('hex'); + const spanId = randomBytes(8).toString('hex'); + return `00-${traceId}-${spanId}-01`; +} + describe('GET /executions/:id', () => { test('should fail due to missing API Key', testWithAPIKey('get', '/executions/1', null)); @@ -110,8 +117,16 @@ describe('GET /executions/:id', () => { test('owner should be able to get an execution owned by him', async () => { const workflow = await createWorkflow({}, owner); + const traceparent = createTraceparent(); - const execution = await createSuccessfulExecution(workflow); + const execution = await createExecution( + { + finished: true, + status: 'success', + tracingContext: { traceparent }, + }, + workflow, + ); const response = await authOwnerAgent.get(`/executions/${execution.id}`); @@ -127,6 +142,7 @@ describe('GET /executions/:id', () => { stoppedAt, workflowId, waitTill, + tracingContext, } = response.body; expect(id).toBeDefined(); @@ -138,6 +154,53 @@ describe('GET /executions/:id', () => { expect(stoppedAt).not.toBeNull(); expect(workflowId).toBe(execution.workflowId); expect(waitTill).toBeNull(); + expect(tracingContext).toEqual({ traceparent }); + }); + + test('should return a webhook execution when the stored tracestate is null', async () => { + const traceparent = createTraceparent(); + const workflow = await createWorkflow({}, owner); + const execution = await createExecution( + { + finished: true, + status: 'success', + mode: 'webhook', + tracingContext: { + traceparent, + tracestate: null, + } as unknown as ExecutionEntity['tracingContext'], + }, + workflow, + ); + + const withoutData = await authOwnerAgent.get(`/executions/${execution.id}`); + const withData = await authOwnerAgent.get(`/executions/${execution.id}?includeData=true`); + + expect(withoutData.statusCode).toBe(200); + expect(withData.statusCode).toBe(200); + expect(withoutData.body.tracingContext).toEqual({ traceparent }); + expect(withData.body.tracingContext).toEqual({ traceparent }); + }); + + test('should return a webhook execution when the stored traceparent is missing', async () => { + const workflow = await createWorkflow({}, owner); + const execution = await createExecution( + { + finished: true, + status: 'success', + mode: 'webhook', + tracingContext: {} as unknown as ExecutionEntity['tracingContext'], + }, + workflow, + ); + + const withoutData = await authOwnerAgent.get(`/executions/${execution.id}`); + const withData = await authOwnerAgent.get(`/executions/${execution.id}?includeData=true`); + + expect(withoutData.statusCode).toBe(200); + expect(withData.statusCode).toBe(200); + expect(withoutData.body.tracingContext).toBeNull(); + expect(withData.body.tracingContext).toBeNull(); }); test('owner should be able to read executions of other users', async () => { diff --git a/packages/cli/test/integration/shared/db/executions.ts b/packages/cli/test/integration/shared/db/executions.ts index 7c1caa3aba6..7ff30280fe2 100644 --- a/packages/cli/test/integration/shared/db/executions.ts +++ b/packages/cli/test/integration/shared/db/executions.ts @@ -47,6 +47,7 @@ export async function createExecution( jsonSizeBytes, binaryDataSizeBytes, workflowVersionId, + tracingContext, } = attributes; const execution = await Container.get(ExecutionRepository).save({ @@ -62,6 +63,7 @@ export async function createExecution( ...(jsonSizeBytes !== undefined && { jsonSizeBytes }), ...(binaryDataSizeBytes !== undefined && { binaryDataSizeBytes }), ...(workflowVersionId !== undefined && { workflowVersionId }), + ...(tracingContext !== undefined && { tracingContext }), }); if (metadata?.length) { From b613184d64f44ebae3e53f8ef157903423081059 Mon Sep 17 00:00:00 2001 From: Konstantin Tieber <46342664+konstantintieber@users.noreply.github.com> Date: Tue, 29 Sep 2026 06:59:12 +0000 Subject: [PATCH 03/55] feat(core): Backfill historic daily data when instance-reporting is first enabled (no-changelog) (#39567) Co-authored-by: Claude Opus 5.5 (1M context) --- .../insights.service.integration.test.ts | 56 ++++ ...s-by-period.repository.integration.test.ts | 36 +++ .../insights-by-period.repository.ts | 31 ++- .../src/modules/insights/insights.service.ts | 29 +++ .../LOCAL_VERIFICATION.md | 12 +- .../modules/instance-reporting.ee/README.md | 45 +++- .../modules/instance-reporting.ee/RETRIES.md | 43 +++- .../instance-reporting.integration.test.ts | 148 +++++++++-- .../instance-reporting.service.test.ts | 240 ++++++++++++++---- .../instance-reporting.service.ts | 113 +++++---- 10 files changed, 602 insertions(+), 151 deletions(-) diff --git a/packages/cli/src/modules/insights/__tests__/insights.service.integration.test.ts b/packages/cli/src/modules/insights/__tests__/insights.service.integration.test.ts index 8396950161f..4f9a96edc2a 100644 --- a/packages/cli/src/modules/insights/__tests__/insights.service.integration.test.ts +++ b/packages/cli/src/modules/insights/__tests__/insights.service.integration.test.ts @@ -1561,6 +1561,62 @@ describe('InsightsService (Integration)', () => { }); }); + describe('getDailyExecutionTotals', () => { + test('sums succeeded and failed executions of all workflows for each UTC day', async () => { + const insightsService = Container.get(InsightsService); + const workflow1 = await createWorkflow({}, await createTeamProject()); + const workflow2 = await createWorkflow({}, await createTeamProject()); + const at = (iso: string) => DateTime.fromISO(iso, { zone: 'utc' }); + + await createCompactedInsightsEvent(workflow1, { + type: 'success', + value: 3, + periodUnit: 'day', + periodStart: at('2025-06-10'), + }); + await createCompactedInsightsEvent(workflow2, { + type: 'failure', + value: 2, + periodUnit: 'day', + periodStart: at('2025-06-10'), + }); + // Other types on the same day do not count as executions. + for (const type of ['runtime_ms', 'time_saved_min', 'billable'] as const) { + await createCompactedInsightsEvent(workflow1, { + type, + value: 100, + periodUnit: 'day', + periodStart: at('2025-06-10'), + }); + } + // Hourly rows on both sides of the UTC day boundary. + await createCompactedInsightsEvent(workflow1, { + type: 'success', + value: 1, + periodUnit: 'hour', + periodStart: at('2025-06-10T23:00:00'), + }); + await createCompactedInsightsEvent(workflow2, { + type: 'failure', + value: 4, + periodUnit: 'hour', + periodStart: at('2025-06-11T00:00:00'), + }); + + const totals = await insightsService.getDailyExecutionTotals({ + startDate: at('2025-06-10').toJSDate(), + endDate: at('2025-06-11').toJSDate(), + }); + + expect(totals).toEqual( + new Map([ + ['2025-06-10', 6], + ['2025-06-11', 4], + ]), + ); + }); + }); + describe('validateDateFiltersLicense', () => { let licenseStateMock: Mocked; let insightsService: InsightsService; diff --git a/packages/cli/src/modules/insights/database/repositories/__tests__/insights-by-period.repository.integration.test.ts b/packages/cli/src/modules/insights/database/repositories/__tests__/insights-by-period.repository.integration.test.ts index d1256c06cbf..0d87a9b1333 100644 --- a/packages/cli/src/modules/insights/database/repositories/__tests__/insights-by-period.repository.integration.test.ts +++ b/packages/cli/src/modules/insights/database/repositories/__tests__/insights-by-period.repository.integration.test.ts @@ -525,6 +525,42 @@ describe('InsightsByPeriodRepository', () => { ); }); + describe('getEarliestDataDate', () => { + async function truncateInsights(): Promise { + await testDb.truncate(['InsightsByPeriod', 'InsightsMetadata', 'WorkflowEntity', 'Project']); + } + + beforeEach(async () => { + await truncateInsights(); + }); + + // Later blocks query a recent window, so leave none of these rows. + afterAll(async () => { + await truncateInsights(); + }); + + test('returns the oldest period start of any bucket size as a UTC instant', async () => { + const repository = Container.get(InsightsByPeriodRepository); + const workflow = await createWorkflow({}, await createTeamProject()); + for (const [periodUnit, periodStart] of [ + ['hour', '2026-06-01T10:00:00'], + ['day', '2026-04-08'], + ['week', '2026-03-16'], + ] as const) { + await createCompactedInsightsEvent(workflow, { + type: 'success', + value: 1, + periodUnit, + periodStart: DateTime.fromISO(periodStart, { zone: 'utc' }), + }); + } + + await expect(repository.getEarliestDataDate()).resolves.toEqual( + new Date('2026-03-16T00:00:00.000Z'), + ); + }); + }); + describe('access filter', () => { let member: User; let accessibleProject: Project; diff --git a/packages/cli/src/modules/insights/database/repositories/insights-by-period.repository.ts b/packages/cli/src/modules/insights/database/repositories/insights-by-period.repository.ts index e3980e65968..19a5e3d8459 100644 --- a/packages/cli/src/modules/insights/database/repositories/insights-by-period.repository.ts +++ b/packages/cli/src/modules/insights/database/repositories/insights-by-period.repository.ts @@ -65,20 +65,23 @@ const optionalNumberLike = z .optional() .transform((value) => (value !== undefined ? Number(value) : undefined)); +/** A raw `periodStart`: a `Date` on Postgres, a UTC SQL datetime string on SQLite. */ +const periodStartParser = z.union([z.date(), z.string()]).transform((value): string => { + if (value instanceof Date) { + return value.toISOString(); + } + + const parsedDatetime = DateTime.fromSQL(value.toString(), { zone: 'utc' }); + if (parsedDatetime.isValid) { + return parsedDatetime.toISO() ?? new Date(value).toISOString(); + } + + return new Date(value).toISOString(); +}); + const aggregatedInsightsByTimeParser = z .object({ - periodStart: z.union([z.date(), z.string()]).transform((value): string => { - if (value instanceof Date) { - return value.toISOString(); - } - - const parsedDatetime = DateTime.fromSQL(value.toString(), { zone: 'utc' }); - if (parsedDatetime.isValid) { - return parsedDatetime.toISO() ?? new Date(value).toISOString(); - } - - return new Date(value).toISOString(); - }), + periodStart: periodStartParser, runTime: optionalNumberLike, succeeded: optionalNumberLike, failed: optionalNumberLike, @@ -574,6 +577,8 @@ export class InsightsByPeriodRepository extends Repository { const result = await this.createQueryBuilder('ibp') .select('MIN(ibp.periodStart)', 'minDate') .getRawOne<{ minDate: Date | string | null }>(); - return result?.minDate ? new Date(result.minDate) : null; + // SQLite returns a UTC datetime string without a zone, which `new Date()` + // would read as local time. + return result?.minDate ? new Date(periodStartParser.parse(result.minDate)) : null; } } diff --git a/packages/cli/src/modules/insights/insights.service.ts b/packages/cli/src/modules/insights/insights.service.ts index 7a4876428cb..71d051fc275 100644 --- a/packages/cli/src/modules/insights/insights.service.ts +++ b/packages/cli/src/modules/insights/insights.service.ts @@ -305,6 +305,35 @@ export class InsightsService { })); } + /** The start of the oldest insights period of any bucket size, or `null` without data. */ + async getEarliestDataDate() { + return await this.insightsByPeriodRepository.getEarliestDataDate(); + } + + /** + * Succeeded plus failed executions of all workflows for each UTC day from + * `startDate` to `endDate`, both inclusive, keyed by `YYYY-MM-DD`. A day + * without executions has no entry. + * + * Unlike {@link getInsightsByTime}, this buckets by day for any range length. + */ + async getDailyExecutionTotals({ + startDate, + endDate, + }: { startDate: Date; endDate: Date }): Promise> { + const rows = await this.insightsByPeriodRepository.getInsightsByTime({ + periodUnit: 'day', + insightTypes: ['success', 'failure'], + startDate, + endDate, + timeZone: 'UTC', + }); + + return new Map( + rows.map((row) => [row.periodStart.slice(0, 10), (row.succeeded ?? 0) + (row.failed ?? 0)]), + ); + } + private async queryInsightsByTime({ user, startDate, diff --git a/packages/cli/src/modules/instance-reporting.ee/LOCAL_VERIFICATION.md b/packages/cli/src/modules/instance-reporting.ee/LOCAL_VERIFICATION.md index 58160100308..13464d8b83d 100644 --- a/packages/cli/src/modules/instance-reporting.ee/LOCAL_VERIFICATION.md +++ b/packages/cli/src/modules/instance-reporting.ee/LOCAL_VERIFICATION.md @@ -27,9 +27,8 @@ the test. - Keep the receiver's request log visible. You must see the raw body. - `sqlite3` is installed. The dev instance uses SQLite at `~/.n8n/database.sqlite` unless you set another database. -- An n8n owner account exists on the instance (the report reads insights as the - instance owner). Complete the setup screen first if this is a fresh - `~/.n8n`. +- An n8n owner account exists on the instance, so you can build the workflows + in step 2. Complete the setup screen first if this is a fresh `~/.n8n`. ```bash export N8N_DB=~/.n8n/database.sqlite # used by the snippets below @@ -161,6 +160,13 @@ with: Confirm the numbers match what you recorded in step 2, and that `date` is yesterday's UTC date, not today's. +"Exactly two entries" holds when `insights_by_period` has no rows older than +yesterday, as on a fresh `~/.n8n`. The first report backfills the `insights` +history. With an older database, it gives one `daily` point for every day from +the oldest row to yesterday, oldest first, but for at most 89 days (the +hour-to-day compaction threshold minus one). Days without rows inside that +range carry `0`. + **n8n side.** ```bash diff --git a/packages/cli/src/modules/instance-reporting.ee/README.md b/packages/cli/src/modules/instance-reporting.ee/README.md index 18fcc082b98..9d9677f749a 100644 --- a/packages/cli/src/modules/instance-reporting.ee/README.md +++ b/packages/cli/src/modules/instance-reporting.ee/README.md @@ -14,13 +14,41 @@ rather than taking fresh numbers — the cumulative total's day-to-day diff is only meaningful while every sample sits a fixed 24 hours apart. A report that follows downtime carries one `daily` point for each day the -instance missed, up to 30 days, and one `cumulative` point as always. Across a -gap the `daily` series is the authoritative one: the two cumulative samples -around the gap sit more than 24 hours apart, so their difference covers the -whole outage. A missed day with no executions is reported as `0`, so a gap in -the series always means "not reported", never "nothing ran". A gap longer than -30 days is unrecoverable, since `insights` buckets a longer range by week; the -oldest days are dropped and logged. +instance missed, and one `cumulative` point as always. Across a gap the `daily` +series is the authoritative one: the two cumulative samples around the gap sit +more than 24 hours apart, so their difference covers the whole outage. A missed +day with no executions is reported as `0`, so a gap in the series always means +"not reported", never "nothing ran". The one exception: missed days before the +first `insights` data are not reported at all. + +The first report works the same way: it carries a `daily` point for every day +from the first `insights` data to yesterday. It carries no past `cumulative` +values, since those are unknown. Days before the first `insights` data are not +sent. A report reads only hourly `insights` data, because only hourly rows +split exactly into UTC days. `insights` folds hours older than 90 days (by +default) into daily rows, so a report never carries a day older than 89 days. +Inside the sent range, a day without data is `0`. +[RETRIES.md](./RETRIES.md#type-2-missed-day-backfill) gives the exact rules. + +Known limits: + +- If `insights` was disabled for a time between its first data and the first + report, those days are sent as `0`. +- After this instance's database is restored from a backup, the days since the + backup are sent again, as `0` or as a lower value. Consumers of the receiver's + export must use the highest value for each instance, metric and day. + `insights` stores no row for a day without executions, so a day lost in the + restore looks the same as a day when nothing ran. If `insights` stored an + explicit `0` for each day it ran, the lost days could be left out, and only + the backup day would be sent again. +- On Postgres, `insights` compacts in the session's time zone. When that time + zone is not UTC, two cases put executions on the wrong UTC day: + - Daily rows inside the sent range. They occur only if + `N8N_INSIGHTS_COMPACTION_HOURLY_TO_DAILY_THRESHOLD_DAYS` was lower before. + Each daily row holds a local day, and the report puts it on one UTC day. + - A time zone with a half-hour offset, such as `Asia/Kolkata`. Hourly rows + then start at 30 minutes past the UTC hour, so the row that holds UTC + midnight counts on one day only. **A day is reported once, and 201 is what decides it.** The receiver answers 201 only once it has saved the report, so anything else means nothing was saved and @@ -171,6 +199,3 @@ last accepted a report, or `null` when it never did: ``` The route exists whenever the module is loaded, including without a receiver — the client decides whether to ask by reading `enabled` from the client settings. - -See [.agents/specs/central-instance-monitoring.md](../../../../../.agents/specs/central-instance-monitoring.md) -for the full design. diff --git a/packages/cli/src/modules/instance-reporting.ee/RETRIES.md b/packages/cli/src/modules/instance-reporting.ee/RETRIES.md index f405e27a48a..c16cf007d23 100644 --- a/packages/cli/src/modules/instance-reporting.ee/RETRIES.md +++ b/packages/cli/src/modules/instance-reporting.ee/RETRIES.md @@ -76,16 +76,47 @@ Scope: no `pending` row is the newest, but one or more calendar days have no - `InstanceMonitoringReportRepository.findLastCoveredDay()` counts only `delivered` rows. A day whose row was skipped, or a day the instance was down - for, is still owed. -- `InstanceReportingService.missedDays()` walks back from yesterday to the day - after the last delivered day. It stops at `MAX_BACKFILL_DAYS` (30) because - `insights` buckets a longer range by week, which cannot fill a daily point. - Older days are dropped. + for, is still owed. Skipped rows never shorten the window: only a delivered + row moves its start. +- `InstanceReportingService.missedDays()` returns every day from the first + owed day to yesterday. Yesterday is always included. +- The first owed day is the day after the last delivered day, but never + before the first `insights` data (`InsightsService.getEarliestDataDate()`). + Before the first delivered report, it is that first day. + - A report never carries a day older than + `N8N_INSIGHTS_COMPACTION_HOURLY_TO_DAILY_THRESHOLD_DAYS` minus one: 89 + days with the default settings. Compaction folds older hours into daily + rows. On Postgres, a daily row holds a day in the session's time zone, so + it has no exact value for a UTC day. The day of margin covers Postgres, + which also dates the threshold in the session's time zone. + - Days before the first data are not reported, not even as `0`. Inside the + window, a day without data is reported as `0`. `insights` writes rows only + for executions and never stores a `0`, so a day without executions and a + day without collected data look the same. + - Without any data, the report carries only yesterday, so a new instance + still shows up on the receiver. +- The first report is no special case. It backfills the history that `insights` + holds instead of sending yesterday alone. +- With the default settings, a full report of 89 daily points comes to less + than 20 KB, far below the receiver's size limit. If the receiver + still rejects a report with `413`, the row is skipped. The next report + carries the same days, so the receiver can reject it again. +- `collectDataPoints()` reads the whole window from `insights` in one query, + bucketed by UTC day. `InsightsService.getInsightsByTime()` is not used, + because it buckets a range of more than 30 days by week. The read does not + apply the license's `insights` history limit. Only the `insights` dashboard + applies that limit. - The next report creates one new row. It carries one `daily` point for every missed day and one `cumulative` point. The cumulative point is a fresh lifetime total, not one per missed day. - That new row is then subject to type 1 if its own delivery fails. +Example: the module is enabled, but the receiver cannot be reached for three +days. Each day's report fails three times and is skipped. No row is delivered, +so each new report starts again at the first day with `insights` data. When the +receiver is reachable again, the next delivered report holds every day, from +that first day to yesterday. + Type 2 is the catch-up layer. It reacts to type 1 giving up, and to any other gap in delivered rows. @@ -108,7 +139,7 @@ flowchart TD G --> I["Day stays uncovered\n(not delivered)"] subgraph T2["Type 2: missed-day backfill"] - J["Next cycle: newest row not pending"] --> K["missedDays() = every day after\nlast DELIVERED day, up to 30"] + J["Next cycle: newest row not pending"] --> K["missedDays() = every day after\nlast DELIVERED day (or since first\ninsights data), exact days only"] K --> L{"any missed days?"} L -- no --> M["nothing to send"] L -- yes --> N["new row: one daily point per\nmissed day + 1 cumulative point"] diff --git a/packages/cli/src/modules/instance-reporting.ee/__tests__/instance-reporting.integration.test.ts b/packages/cli/src/modules/instance-reporting.ee/__tests__/instance-reporting.integration.test.ts index eddc894cf71..f6b6f381489 100644 --- a/packages/cli/src/modules/instance-reporting.ee/__tests__/instance-reporting.integration.test.ts +++ b/packages/cli/src/modules/instance-reporting.ee/__tests__/instance-reporting.integration.test.ts @@ -17,9 +17,8 @@ import { mock } from 'vitest-mock-extended'; import { EventService } from '@/events/event.service'; import type { License } from '@/license'; import { createCompactedInsightsEvent } from '@/modules/insights/database/entities/__tests__/db-utils'; +import { InsightsConfig } from '@/modules/insights/insights.config'; import { InsightsService } from '@/modules/insights/insights.service'; -import { OwnershipService } from '@/services/ownership.service'; -import { createOwner } from '@test-integration/db/users'; import type { InstanceMonitoringReport, @@ -102,13 +101,11 @@ describe('instance reporting retries', () => { await testDb.truncate([ 'InstanceMonitoringReport', 'Settings', - 'User', 'InsightsByPeriod', 'InsightsMetadata', 'WorkflowEntity', 'Project', ]); - await createOwner(); await Container.get(SettingsRepository).upsertByKey( CENTRAL_INSTANCE_MONITORING_SETTINGS_KEY, JSON.stringify({ reportTime: REPORT_TIME }), @@ -145,8 +142,8 @@ describe('instance reporting retries', () => { Container.get(InstanceReportingConfig), repository, Container.get(InsightsService), + Container.get(InsightsConfig), instanceSettings, - Container.get(OwnershipService), Container.get(LicenseMetricsRepository), mock({ loadCertStr: async () => 'license-cert' }), mockLogger(), @@ -216,13 +213,21 @@ describe('instance reporting retries', () => { } async function seedDailyExecutions(totalsByDay: Record) { + await seedCompactedExecutions('day', totalsByDay); + } + + /** Executions as insights holds them after compaction, keyed by period start. */ + async function seedCompactedExecutions( + periodUnit: 'hour' | 'day' | 'week', + totalsByPeriodStart: Record, + ) { const workflow = await createWorkflow({}, await createTeamProject()); - for (const [day, value] of Object.entries(totalsByDay)) { + for (const [periodStart, value] of Object.entries(totalsByPeriodStart)) { await createCompactedInsightsEvent(workflow, { type: 'success', value, - periodUnit: 'day', - periodStart: DateTime.fromISO(day, { zone: 'utc' }), + periodUnit, + periodStart: DateTime.fromISO(periodStart, { zone: 'utc' }), }); } } @@ -411,9 +416,9 @@ describe('instance reporting retries', () => { await expect(repository.findLastCoveredDay()).resolves.toBe('2026-03-25'); }); - test('caps a long gap at 30 days and still reads every day on its own', async () => { + test('backfills a gap longer than 30 days and still reads every day on its own', async () => { await seedDeliveredReport('2026-01-01'); - await seedDailyExecutions({ '2026-02-24': 6, '2026-03-25': 8 }); + await seedDailyExecutions({ '2026-01-10': 4, '2026-02-24': 6, '2026-03-25': 8 }); const harness = makeHarness([accepted()]); harness.scheduler.start(); @@ -421,11 +426,122 @@ describe('instance reporting retries', () => { expect(harness.httpRequest).toHaveBeenCalledTimes(1); const points = dailyPoints(sentPayload(harness, 0)); - expect(points).toHaveLength(30); - // Exact days, not weekly buckets: a 30-day range still reads as days. - expect(points.at(0)).toEqual({ date: '2026-02-24', value: 6 }); + // Every day from the first data on 01-10 to 03-25. Inside that range, a day + // without data is a real 0. + expect(points).toHaveLength(75); + expect(points.at(0)).toEqual({ date: '2026-01-10', value: 4 }); + // Exact days, not weekly buckets, although the gap is longer than 30 days. + expect(points.find((point) => point.date === '2026-02-24')).toEqual({ + date: '2026-02-24', + value: 6, + }); expect(points.at(-1)).toEqual({ date: '2026-03-25', value: 8 }); - expect(points.slice(1, -1).every((point) => point.value === 0)).toBe(true); + expect(points.filter((point) => point.value !== 0)).toHaveLength(3); + }); + + test('carries the insights history on the first report, but no day older than the hourly compaction threshold', async () => { + // With a threshold of 30 days, compaction folded every hour before 02-24 + // into daily rows, and older days into weekly rows. + const insightsConfig = Container.get(InsightsConfig); + const { compactionHourlyToDailyThresholdDays } = insightsConfig; + insightsConfig.compactionHourlyToDailyThresholdDays = 30; + onTestFinished(() => { + insightsConfig.compactionHourlyToDailyThresholdDays = compactionHourlyToDailyThresholdDays; + }); + + await seedCompactedExecutions('week', { '2026-01-19': 700 }); + await seedCompactedExecutions('day', { '2026-02-20': 5, '2026-02-23': 6 }); + await seedCompactedExecutions('hour', { + '2026-02-24T10:00:00': 7, + '2026-02-25T00:00:00': 8, + '2026-03-10T08:00:00': 2, + '2026-03-10T15:00:00': 3, + '2026-03-25T23:00:00': 4, + }); + + const harness = makeHarness([accepted()]); + harness.scheduler.start(); + await armed(harness, 1); + + const points = dailyPoints(sentPayload(harness, 0)); + // From 29 days back to yesterday. 02-24 falls in the one day of margin. + expect(points).toHaveLength(29); + expect(points.at(0)).toEqual({ date: '2026-02-25', value: 8 }); + expect(points.filter((point) => point.value !== 0)).toEqual([ + { date: '2026-02-25', value: 8 }, + { date: '2026-03-10', value: 5 }, + { date: '2026-03-25', value: 4 }, + ]); + }); + + test('carries every day since the first data once the receiver is reachable after skipped first reports', async () => { + await seedDailyExecutions({ + '2026-03-20': 1, + '2026-03-21': 2, + '2026-03-22': 3, + '2026-03-23': 4, + '2026-03-24': 5, + '2026-03-25': 6, + '2026-03-26': 7, + '2026-03-27': 8, + '2026-03-28': 9, + }); + + // The receiver is unreachable for three days, then accepts. + const harness = makeHarness([ + ...Array.from({ length: 3 * MAX_ATTEMPTS }, unreachable), + accepted(), + ]); + harness.scheduler.start(); + + let passes = 0; + for (const nextSlot of [ + '2026-03-27T07:42:00.000Z', + '2026-03-28T07:42:00.000Z', + '2026-03-29T07:42:00.000Z', + ]) { + await armed(harness, ++passes); + for (let attempt = 1; attempt < MAX_ATTEMPTS; attempt++) { + await vi.advanceTimersByTimeAsync(RETRY_DELAY_MS); + await armed(harness, ++passes); + } + + const [skipped] = await repository.find({ + where: { status: 'skipped_after_max_retries' }, + order: { reportDate: 'DESC' }, + take: 1, + }); + expect(skipped.attempts).toBe(MAX_ATTEMPTS); + + await vi.advanceTimersByTimeAsync(RETRY_DELAY_MS); + await armed(harness, ++passes); + expectArmedFor(harness, nextSlot); + await vi.advanceTimersByTimeAsync(new Date(nextSlot).getTime() - Date.now()); + } + await armed(harness, ++passes); + + expect(harness.httpRequest).toHaveBeenCalledTimes(3 * MAX_ATTEMPTS + 1); + // Every report started at the first day with data, not at its own yesterday. + for (const index of [0, MAX_ATTEMPTS, 2 * MAX_ATTEMPTS]) { + expect(dailyPoints(sentPayload(harness, index)).at(0)?.date).toBe('2026-03-20'); + } + + const delivered = sentPayload(harness, 3 * MAX_ATTEMPTS); + expect(dailyPoints(delivered)).toEqual([ + { date: '2026-03-20', value: 1 }, + { date: '2026-03-21', value: 2 }, + { date: '2026-03-22', value: 3 }, + { date: '2026-03-23', value: 4 }, + { date: '2026-03-24', value: 5 }, + { date: '2026-03-25', value: 6 }, + { date: '2026-03-26', value: 7 }, + { date: '2026-03-27', value: 8 }, + { date: '2026-03-28', value: 9 }, + ]); + await expect(repository.findOneByOrFail({ id: delivered.batchId })).resolves.toMatchObject({ + status: 'delivered', + }); + await expect(repository.findLastCoveredDay()).resolves.toBe('2026-03-28'); }); test('resumes the retry budget and the remaining wait after a restart', async () => { @@ -707,8 +823,8 @@ describe('instance reporting retries', () => { // Another process inserts today's report after this one found nothing pending. const insights = Container.get(InsightsService); - const measure = insights.getInsightsByTime.bind(insights); - vi.spyOn(insights, 'getInsightsByTime').mockImplementation(async (args) => { + const measure = insights.getDailyExecutionTotals.bind(insights); + vi.spyOn(insights, 'getDailyExecutionTotals').mockImplementation(async (args) => { other = await createPendingOn(new Date(), otherPoints); return await measure(args); }); diff --git a/packages/cli/src/modules/instance-reporting.ee/__tests__/instance-reporting.service.test.ts b/packages/cli/src/modules/instance-reporting.ee/__tests__/instance-reporting.service.test.ts index f900357e43b..ad29c3bb800 100644 --- a/packages/cli/src/modules/instance-reporting.ee/__tests__/instance-reporting.service.test.ts +++ b/packages/cli/src/modules/instance-reporting.ee/__tests__/instance-reporting.service.test.ts @@ -1,11 +1,10 @@ -import type { InsightsByTime } from '@n8n/api-types'; import { mockLogger } from '@n8n/backend-test-utils'; import type { HttpRequestClient, HttpRequestClientOptions, OutboundHttp, } from '@n8n/backend-network'; -import type { LicenseMetricsRepository, User } from '@n8n/db'; +import type { LicenseMetricsRepository } from '@n8n/db'; import type { InstanceSettings } from 'n8n-core'; import type { IHttpRequestOptions } from 'n8n-workflow'; import type { Mocked } from 'vitest'; @@ -13,8 +12,8 @@ import { mock } from 'vitest-mock-extended'; import type { EventService } from '@/events/event.service'; import type { License } from '@/license'; +import { InsightsConfig } from '@/modules/insights/insights.config'; import type { InsightsService } from '@/modules/insights/insights.service'; -import type { OwnershipService } from '@/services/ownership.service'; import type { InstanceMonitoringReport } from '../database/entities/instance-monitoring-report'; import type { InstanceMonitoringReportRepository } from '../database/repositories/instance-monitoring-report.repository'; @@ -43,8 +42,6 @@ const BATCH_ID = 'batch-id-1'; /** Opaque to this module: whatever `License.loadCertStr()` returns is sent as is. */ const LICENSE_CERT = 'base64-license-cert'; -const OWNER_MOCK = mock({ id: 'owner-id' }); - const LICENSE_METRICS_MOCK = { enabledUsers: 1, totalUsers: 1, @@ -57,23 +54,11 @@ const LICENSE_METRICS_MOCK = { evaluations: 0, }; -function byTime(totalsByDay: Record): InsightsByTime[] { - return Object.entries(totalsByDay).map(([date, total]) => ({ - date: `${date}T00:00:00.000Z`, - values: { - total, - succeeded: 0, - failed: 0, - failureRate: 0, - averageRunTime: 0, - timeSaved: 0, - }, - })); +/** Daily execution totals as insights returns them, keyed by `YYYY-MM-DD`. */ +function totalsByDay(totals: Record): Map { + return new Map(Object.entries(totals)); } -/** One `getInsightsByTime` row: the reported day held 42 executions. */ -const BY_TIME_MOCK = byTime({ [REPORT_DATE]: 42 }); - function makeConfig(overrides: Partial = {}): InstanceReportingConfig { const config = new InstanceReportingConfig(); config.instanceReportingBaseUrl = 'https://example.com'; @@ -98,6 +83,7 @@ interface Harness { service: InstanceReportingService; reportRepository: Mocked; insightsService: Mocked; + insightsConfig: InsightsConfig; license: Mocked; http: HttpRequestClient; eventService: Mocked; @@ -115,10 +101,12 @@ function makeHarness(config: InstanceReportingConfig = makeConfig()): Harness { ); const insightsService = mock(); - insightsService.getInsightsByTime.mockResolvedValue(BY_TIME_MOCK); + // The reported day held 42 executions. + insightsService.getDailyExecutionTotals.mockResolvedValue(totalsByDay({ [REPORT_DATE]: 42 })); + // No insights data: an instance that has not compacted anything yet. + insightsService.getEarliestDataDate.mockResolvedValue(null); - const ownershipService = mock(); - ownershipService.getInstanceOwner.mockResolvedValue(OWNER_MOCK); + const insightsConfig = new InsightsConfig(); const licenseMetricsRepository = mock(); licenseMetricsRepository.getLicenseRenewalMetrics.mockResolvedValue(LICENSE_METRICS_MOCK); @@ -143,8 +131,8 @@ function makeHarness(config: InstanceReportingConfig = makeConfig()): Harness { config, reportRepository, insightsService, + insightsConfig, mock({ instanceId: 'abc123' }), - ownershipService, licenseMetricsRepository, license, mockLogger(), @@ -152,7 +140,16 @@ function makeHarness(config: InstanceReportingConfig = makeConfig()): Harness { outboundHttp, ); - return { service, reportRepository, insightsService, license, http, eventService, clientOptions }; + return { + service, + reportRepository, + insightsService, + insightsConfig, + license, + http, + eventService, + clientOptions, + }; } describe('InstanceReportingService', () => { @@ -282,7 +279,7 @@ describe('InstanceReportingService', () => { await expect(service.sendReport()).resolves.toBeUndefined(); expect(http.request).not.toHaveBeenCalled(); - expect(insightsService.getInsightsByTime).not.toHaveBeenCalled(); + expect(insightsService.getDailyExecutionTotals).not.toHaveBeenCalled(); expect(reportRepository.createPending).not.toHaveBeenCalled(); }); @@ -315,16 +312,14 @@ describe('InstanceReportingService', () => { }); }); - test('queries the instance owner insights for the reported UTC day', async () => { + test('reads the daily execution totals for the reported UTC day', async () => { const { service, insightsService } = makeHarness(); await service.sendReport(); - expect(insightsService.getInsightsByTime).toHaveBeenCalledWith({ - user: OWNER_MOCK, + expect(insightsService.getDailyExecutionTotals).toHaveBeenCalledWith({ startDate: new Date('2026-03-25T00:00:00.000Z'), - endDate: new Date('2026-03-26T00:00:00.000Z'), - timeZone: 'UTC', + endDate: new Date('2026-03-25T00:00:00.000Z'), }); }); @@ -482,7 +477,7 @@ describe('InstanceReportingService', () => { // Re-measuring would sample the cumulative total at a different point in // the day and stretch its interval past 24 hours. expect(body(http).dataPoints).toEqual(measured); - expect(insightsService.getInsightsByTime).not.toHaveBeenCalled(); + expect(insightsService.getDailyExecutionTotals).not.toHaveBeenCalled(); expect(reportRepository.createPending).not.toHaveBeenCalled(); }); }); @@ -556,8 +551,8 @@ describe('InstanceReportingService', () => { test('carries a daily point for every day since the last delivered report', async () => { const { service, reportRepository, insightsService, http } = makeHarness(); reportRepository.findLastCoveredDay.mockResolvedValue('2026-03-22'); - insightsService.getInsightsByTime.mockResolvedValue( - byTime({ '2026-03-23': 5, '2026-03-24': 7, '2026-03-25': 9 }), + insightsService.getDailyExecutionTotals.mockResolvedValue( + totalsByDay({ '2026-03-23': 5, '2026-03-24': 7, '2026-03-25': 9 }), ); await service.sendReport(); @@ -568,12 +563,10 @@ describe('InstanceReportingService', () => { { value: 9, date: '2026-03-25' }, ]); // One range query covers the gap, and the cumulative point stays single. - expect(insightsService.getInsightsByTime).toHaveBeenCalledWith( - expect.objectContaining({ - startDate: new Date('2026-03-23T00:00:00.000Z'), - endDate: new Date('2026-03-26T00:00:00.000Z'), - }), - ); + expect(insightsService.getDailyExecutionTotals).toHaveBeenCalledWith({ + startDate: new Date('2026-03-23T00:00:00.000Z'), + endDate: new Date('2026-03-25T00:00:00.000Z'), + }); expect(points(http).filter((point) => point.kind === 'cumulative')).toHaveLength(1); }); @@ -581,7 +574,7 @@ describe('InstanceReportingService', () => { const { service, reportRepository, insightsService, http } = makeHarness(); reportRepository.findLastCoveredDay.mockResolvedValue('2026-03-23'); // Insights returns no row for a day that saw nothing. - insightsService.getInsightsByTime.mockResolvedValue(byTime({ '2026-03-25': 9 })); + insightsService.getDailyExecutionTotals.mockResolvedValue(totalsByDay({ '2026-03-25': 9 })); await service.sendReport(); @@ -591,29 +584,164 @@ describe('InstanceReportingService', () => { ]); }); - test('reports yesterday alone on the first ever report, importing no history', async () => { - const { service, reportRepository, insightsService, http } = makeHarness(); - reportRepository.findLastCoveredDay.mockResolvedValue(null); + describe('on the first report', () => { + test('carries every day of insights history, oldest first, and one cumulative point', async () => { + const { service, reportRepository, insightsService, http } = makeHarness(); + reportRepository.findLastCoveredDay.mockResolvedValue(null); + insightsService.getEarliestDataDate.mockResolvedValue(new Date('2026-01-01T00:00:00.000Z')); - await service.sendReport(); + await service.sendReport(); - expect(dailyPoints(http)).toEqual([{ value: 42, date: REPORT_DATE }]); - expect(insightsService.getInsightsByTime).toHaveBeenCalledWith( - expect.objectContaining({ startDate: new Date('2026-03-25T00:00:00.000Z') }), - ); + const daily = dailyPoints(http); + expect(daily).toHaveLength(84); + expect(daily.at(0)?.date).toBe('2026-01-01'); + expect(daily.at(-1)).toEqual({ value: 42, date: REPORT_DATE }); + expect(daily.map(({ date }) => date)).toEqual(daily.map(({ date }) => date).sort()); + expect(points(http).filter((point) => point.kind === 'cumulative')).toEqual([ + { kind: 'cumulative', name: 'billableExecutions', value: 815 }, + ]); + }); + + test('starts at the first day with data and sends no zeros before it', async () => { + const { service, reportRepository, insightsService, http } = makeHarness(); + reportRepository.findLastCoveredDay.mockResolvedValue(null); + insightsService.getEarliestDataDate.mockResolvedValue(new Date('2026-03-20T00:00:00.000Z')); + insightsService.getDailyExecutionTotals.mockResolvedValue( + totalsByDay({ '2026-03-20': 4, [REPORT_DATE]: 42 }), + ); + + await service.sendReport(); + + // Inside the window, a day without data saw no executions. + expect(dailyPoints(http)).toEqual([ + { value: 4, date: '2026-03-20' }, + { value: 0, date: '2026-03-21' }, + { value: 0, date: '2026-03-22' }, + { value: 0, date: '2026-03-23' }, + { value: 0, date: '2026-03-24' }, + { value: 42, date: REPORT_DATE }, + ]); + }); + + test('reports yesterday as 0 on a new instance without insights data', async () => { + const { service, reportRepository, insightsService, http } = makeHarness(); + reportRepository.findLastCoveredDay.mockResolvedValue(null); + insightsService.getDailyExecutionTotals.mockResolvedValue(new Map()); + + await service.sendReport(); + + expect(points(http)).toEqual([ + { kind: 'cumulative', name: 'billableExecutions', value: 815 }, + { kind: 'daily', name: 'billableExecutions', value: 0, date: REPORT_DATE }, + ]); + }); + + test('reports yesterday as 0 when the first insights data is from today', async () => { + const { service, reportRepository, insightsService, http } = makeHarness(); + reportRepository.findLastCoveredDay.mockResolvedValue(null); + insightsService.getEarliestDataDate.mockResolvedValue(new Date('2026-03-26T00:00:00.000Z')); + insightsService.getDailyExecutionTotals.mockResolvedValue(new Map()); + + await service.sendReport(); + + expect(dailyPoints(http)).toEqual([{ value: 0, date: REPORT_DATE }]); + }); + + test('still starts at the first day with data after earlier reports were skipped', async () => { + const { service, reportRepository, insightsService, http } = makeHarness(); + // Skipped reports never count as delivered, so no day is covered yet. + reportRepository.findPending.mockResolvedValue(null); + reportRepository.findLastCoveredDay.mockResolvedValue(null); + insightsService.getEarliestDataDate.mockResolvedValue(new Date('2026-03-22T00:00:00.000Z')); + + await service.sendReport(); + + expect(dailyPoints(http).map(({ date }) => date)).toEqual([ + '2026-03-22', + '2026-03-23', + '2026-03-24', + REPORT_DATE, + ]); + }); + + test('resends a pending first report as measured, without reading the history again', async () => { + const { service, reportRepository, insightsService, http } = makeHarness(); + reportRepository.findLastCoveredDay.mockResolvedValue(null); + const measured = [ + { kind: 'cumulative', name: 'billableExecutions', value: 800 }, + { kind: 'daily', name: 'billableExecutions', value: 3, date: '2026-03-20' }, + { kind: 'daily', name: 'billableExecutions', value: 40, date: REPORT_DATE }, + ] as InstanceMonitoringReport['dataPoints']; + reportRepository.findPending.mockResolvedValue( + makeReport({ dataPoints: measured, attempts: 1 }), + ); + + await service.sendReport(); + + expect(points(http)).toEqual(measured); + expect(insightsService.getEarliestDataDate).not.toHaveBeenCalled(); + expect(insightsService.getDailyExecutionTotals).not.toHaveBeenCalled(); + }); }); - test('drops the oldest days when the gap is longer than insights can bucket by day', async () => { - const { service, reportRepository, http } = makeHarness(); + test('backfills a gap longer than 30 days, reporting days without data as 0', async () => { + const { service, reportRepository, insightsService, http } = makeHarness(); reportRepository.findLastCoveredDay.mockResolvedValue('2026-01-01'); + insightsService.getEarliestDataDate.mockResolvedValue(new Date('2025-10-01T00:00:00.000Z')); await service.sendReport(); - const points = dailyPoints(http); - expect(points).toHaveLength(30); - // Still ends at yesterday, so the gap is not retried tomorrow. - expect(points.at(0)?.date).toBe('2026-02-24'); - expect(points.at(-1)?.date).toBe(REPORT_DATE); + const daily = dailyPoints(http); + expect(daily).toHaveLength(83); + expect(daily.at(0)).toEqual({ value: 0, date: '2026-01-02' }); + expect(daily.at(-1)).toEqual({ value: 42, date: REPORT_DATE }); + }); + + test('starts a gap no earlier than the first insights data', async () => { + const { service, reportRepository, insightsService, http } = makeHarness(); + reportRepository.findLastCoveredDay.mockResolvedValue('2025-12-30'); + insightsService.getEarliestDataDate.mockResolvedValue(new Date('2026-01-10T00:00:00.000Z')); + + await service.sendReport(); + + expect(dailyPoints(http).at(0)).toEqual({ value: 0, date: '2026-01-10' }); + }); + + test('reads the whole window in one day-bucketed read', async () => { + const { service, reportRepository, insightsService, http } = makeHarness(); + reportRepository.findLastCoveredDay.mockResolvedValue(null); + insightsService.getEarliestDataDate.mockResolvedValue(new Date('2026-01-01T00:00:00.000Z')); + + await service.sendReport(); + + expect(insightsService.getDailyExecutionTotals).toHaveBeenCalledTimes(1); + expect(insightsService.getDailyExecutionTotals).toHaveBeenCalledWith({ + startDate: new Date('2026-01-01T00:00:00.000Z'), + endDate: new Date('2026-03-25T00:00:00.000Z'), + }); + expect(dailyPoints(http)).toHaveLength(84); + }); + + test('carries no day older than the hourly compaction threshold, minus one day of margin', async () => { + const { service, reportRepository, insightsService, insightsConfig, http } = makeHarness(); + insightsConfig.compactionHourlyToDailyThresholdDays = 30; + reportRepository.findLastCoveredDay.mockResolvedValue(null); + insightsService.getEarliestDataDate.mockResolvedValue(new Date('2023-01-01T00:00:00.000Z')); + + await service.sendReport(); + + const daily = dailyPoints(http); + expect(daily).toHaveLength(29); + expect(daily.at(0)?.date).toBe('2026-02-25'); + expect(daily.at(-1)?.date).toBe(REPORT_DATE); + }); + + test('does not read the insights history when only yesterday is owed', async () => { + const { service, insightsService } = makeHarness(); + + await service.sendReport(); + + expect(insightsService.getEarliestDataDate).not.toHaveBeenCalled(); }); test('sends nothing when yesterday is already reported', async () => { diff --git a/packages/cli/src/modules/instance-reporting.ee/instance-reporting.service.ts b/packages/cli/src/modules/instance-reporting.ee/instance-reporting.service.ts index 94e465c56f7..8f74b9cc439 100644 --- a/packages/cli/src/modules/instance-reporting.ee/instance-reporting.service.ts +++ b/packages/cli/src/modules/instance-reporting.ee/instance-reporting.service.ts @@ -11,8 +11,8 @@ import { OperationalError } from 'n8n-workflow'; import { N8N_VERSION } from '@/constants'; import { EventService } from '@/events/event.service'; import { License } from '@/license'; +import { InsightsConfig } from '@/modules/insights/insights.config'; import { InsightsService } from '@/modules/insights/insights.service'; -import { OwnershipService } from '@/services/ownership.service'; import type { InstanceReportDataPoint } from './database/entities/instance-monitoring-report'; import { InstanceMonitoringReportRepository } from './database/repositories/instance-monitoring-report.repository'; @@ -53,16 +53,6 @@ const SKIP_MESSAGES: Record = { rejected: 'Giving up on the instance report because the receiver rejected its payload', }; -/** - * How many missed days one report may carry. - * - * Insights buckets a range of more than 30 days by week, which cannot fill a - * daily point, so a longer gap is unrecoverable. The oldest days are dropped and - * the report still ends at yesterday, rather than the instance retrying a window - * it can never read. - */ -const MAX_BACKFILL_DAYS = 30; - /** * Measures and delivers one instance report. *When* that happens is * {@link InstanceReportingScheduler}'s concern. @@ -75,8 +65,8 @@ export class InstanceReportingService { private readonly config: InstanceReportingConfig, private readonly reportRepository: InstanceMonitoringReportRepository, private readonly insightsService: InsightsService, + private readonly insightsConfig: InsightsConfig, private readonly instanceSettings: InstanceSettings, - private readonly ownershipService: OwnershipService, private readonly licenseMetricsRepository: LicenseMetricsRepository, private readonly license: License, private readonly logger: Logger, @@ -152,12 +142,6 @@ export class InstanceReportingService { const days = await this.missedDays(now); if (days.length === 0) return; - if (days.length > 1) { - this.logger.info('Reporting days missed since the last delivered instance report', { - days, - }); - } - report = await this.reportRepository.createPending(await this.collectDataPoints(days), now); if (!report) { throw new InstanceReportAlreadyCreatedError(); @@ -243,25 +227,60 @@ export class InstanceReportingService { * The UTC days this report must carry a daily point for, oldest first, ending * yesterday. Empty when yesterday is already reported. * - * A day the instance was down for is still unreported once it comes back, so - * the whole gap since the last delivered report is collected. Without that, - * downtime silently loses days from the daily series. + * Every day after the last delivered one is still owed, whatever happened to + * the reports in between, so neither downtime nor skipped reports lose days + * from the daily series. A first report carries the history insights holds. */ private async missedDays(now: Date): Promise { + const yesterday = utcDayBefore(now, 1); const lastCoveredDay = await this.reportRepository.findLastCoveredDay(); - const days: string[] = []; + if (lastCoveredDay && lastCoveredDay >= yesterday) return []; - // No last covered day means this is the first report: send yesterday alone - // rather than importing however much history insights happens to hold. - for (let back = 1; back <= (lastCoveredDay ? MAX_BACKFILL_DAYS : 1); back++) { - const day = utcDayBefore(now, back); - if (lastCoveredDay && day <= lastCoveredDay) break; - days.unshift(day); + const firstDay = minDay(await this.firstOwedDay(lastCoveredDay, yesterday), yesterday); + // Only hourly rows split exactly into UTC days: Postgres compacts older hours + // into days of the session's time zone, which need not be UTC. One day of + // margin, since Postgres also dates this threshold in that time zone. + const oldestAllowedDay = utcDayBefore( + now, + Math.max(1, this.insightsConfig.compactionHourlyToDailyThresholdDays - 1), + ); + + const days: string[] = []; + for (let day = maxDay(firstDay, oldestAllowedDay); day <= yesterday; day = addUtcDays(day, 1)) { + days.push(day); + } + + if (days.length > 1) { + this.logger.info( + lastCoveredDay + ? 'Reporting days missed since the last delivered instance report' + : 'Reporting the insights history, since no instance report was delivered yet', + { firstDay: days[0], lastDay: days.at(-1), count: days.length }, + ); } return days; } + /** + * The oldest day the next report owes: the day after the last delivered one, + * but never before the first insights data, since nothing before it shows + * that insights was collecting. + */ + private async firstOwedDay(lastCoveredDay: string | null, yesterday: string): Promise { + const dayAfterCovered = lastCoveredDay ? addUtcDays(lastCoveredDay, 1) : null; + + // Only yesterday is owed, which is the everyday case: skip the history read. + if (dayAfterCovered === yesterday) return yesterday; + + const earliest = await this.insightsService.getEarliestDataDate(); + const dataStart = earliest ? earliest.toISOString().slice(0, 10) : null; + + // Without any data, a first report still carries yesterday, so that a new + // instance shows up on the receiver. + return maxDay(dayAfterCovered ?? dataStart ?? yesterday, dataStart); + } + /** * One cumulative point plus one daily point for every day in `days`. * @@ -270,31 +289,16 @@ export class InstanceReportingService { * cumulative ones. */ private async collectDataPoints(days: string[]): Promise { - const startDate = new Date(`${days[0]}T00:00:00.000Z`); - const endDate = new Date( - new Date(`${days.at(-1)}T00:00:00.000Z`).getTime() + Time.days.toMilliseconds, - ); - - // Report instance-wide numbers, so read as the instance owner, whose global - // role grants access to every workflow. - const owner = await this.ownershipService.getInstanceOwner(); - - const [byDay, { productionRootExecutions }] = await Promise.all([ - // Per day rather than one total for the range, since a report may cover - // several days. A range of 1 to 30 days buckets by day; see MAX_BACKFILL_DAYS. - this.insightsService.getInsightsByTime({ - user: owner, - startDate, - endDate, - timeZone: 'UTC', + const [totals, { productionRootExecutions }] = await Promise.all([ + this.insightsService.getDailyExecutionTotals({ + startDate: new Date(`${days[0]}T00:00:00.000Z`), + endDate: new Date(`${days.at(-1)}T00:00:00.000Z`), }), // Same source as the `productionRootExecutions` license metric, so the // reported total matches what the license server sees. this.licenseMetricsRepository.getLicenseRenewalMetrics(), ]); - const totals = new Map(byDay.map((row) => [row.date.slice(0, 10), row.values.total ?? 0])); - return [ { kind: 'cumulative', name: 'billableExecutions', value: productionRootExecutions }, // A day with no executions returns no row, so fill it in as 0. Left out, @@ -313,3 +317,18 @@ export class InstanceReportingService { function utcDayBefore(instant: Date, count: number): string { return new Date(instant.getTime() - count * Time.days.toMilliseconds).toISOString().slice(0, 10); } + +/** The UTC calendar day `count` days after `day`, as `YYYY-MM-DD`. */ +function addUtcDays(day: string, count: number): string { + return utcDayBefore(new Date(`${day}T00:00:00.000Z`), -count); +} + +/** The later of two `YYYY-MM-DD` days; `null` counts as no bound. */ +function maxDay(day: string, other: string | null): string { + return other && other > day ? other : day; +} + +/** The earlier of two `YYYY-MM-DD` days. */ +function minDay(day: string, other: string): string { + return other < day ? other : day; +} From 86c23326c9501f4ccf5bb68705aa8bb234fc2ad2 Mon Sep 17 00:00:00 2001 From: "n8n-assistant[bot]" <100856346+n8n-assistant[bot]@users.noreply.github.com> Date: Tue, 29 Sep 2026 07:11:00 +0000 Subject: [PATCH 04/55] :rocket: Release 2.42.0 (#39826) Co-authored-by: n8n-release-helper[bot] <265227495+n8n-release-helper[bot]@users.noreply.github.com> --- CHANGELOG.md | 129 ++++++++++++++++++ package.json | 2 +- packages/@n8n/agents/package.json | 2 +- packages/@n8n/ai-node-sdk/package.json | 2 +- packages/@n8n/ai-utilities/package.json | 2 +- .../@n8n/ai-workflow-builder.ee/package.json | 2 +- packages/@n8n/api-types/package.json | 2 +- packages/@n8n/backend-common/package.json | 2 +- packages/@n8n/backend-network/package.json | 2 +- packages/@n8n/backend-services/package.json | 2 +- packages/@n8n/backend-test-utils/package.json | 2 +- packages/@n8n/benchmark/package.json | 2 +- packages/@n8n/blob-storage/package.json | 2 +- packages/@n8n/chat-hub/package.json | 2 +- packages/@n8n/cli/package.json | 2 +- packages/@n8n/client-oauth2/package.json | 2 +- packages/@n8n/computer-use/package.json | 2 +- packages/@n8n/config/package.json | 2 +- packages/@n8n/constants/package.json | 2 +- packages/@n8n/crdt/package.json | 2 +- packages/@n8n/create-node/package.json | 2 +- packages/@n8n/db/package.json | 2 +- packages/@n8n/decorators/package.json | 2 +- packages/@n8n/engine/package.json | 2 +- .../package.json | 2 +- packages/@n8n/expression-runtime/package.json | 2 +- packages/@n8n/imap/package.json | 2 +- packages/@n8n/instance-ai/package.json | 2 +- packages/@n8n/json-schema-to-zod/package.json | 2 +- packages/@n8n/mcp-apps/package.json | 2 +- packages/@n8n/node-cli/package.json | 2 +- .../node-engine-compatibility/package.json | 2 +- packages/@n8n/nodes-langchain/package.json | 2 +- packages/@n8n/permissions/package.json | 2 +- .../@n8n/scan-community-package/package.json | 2 +- packages/@n8n/scheduler/package.json | 2 +- packages/@n8n/syslog-client/package.json | 2 +- packages/@n8n/task-runner/package.json | 2 +- packages/@n8n/telemetry/package.json | 2 +- packages/@n8n/utils/package.json | 2 +- packages/@n8n/vitest-config/package.json | 2 +- packages/@n8n/workflow-sdk/package.json | 2 +- packages/cli/package.json | 2 +- packages/core/package.json | 2 +- packages/frontend/@n8n/chat/package.json | 2 +- .../frontend/@n8n/composables/package.json | 2 +- .../frontend/@n8n/design-system/package.json | 2 +- .../@n8n/frontend-constants/package.json | 2 +- .../@n8n/frontend-module-sdk/package.json | 2 +- packages/frontend/@n8n/i18n/package.json | 2 +- .../@n8n/rest-api-client/package.json | 2 +- packages/frontend/@n8n/stores/package.json | 2 +- packages/frontend/editor-ui/package.json | 2 +- .../modules/insights/frontend/package.json | 2 +- .../instance-registry/frontend/package.json | 2 +- packages/modules/otel/frontend/package.json | 2 +- .../frontend/package.json | 2 +- packages/nodes-base/package.json | 2 +- packages/workflow/package.json | 2 +- 59 files changed, 187 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8773d5f32ed..57d1558a9a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,132 @@ +# [2.42.0](https://github.com/n8n-io/n8n/compare/n8n@2.41.0...n8n@2.42.0) (2026-09-29) + + +### Bug Fixes + +* **AI Agent Node:** Let an AI Agent Tool use its own tools under a pre-v3 parent agent ([#39160](https://github.com/n8n-io/n8n/issues/39160)) ([60d0598](https://github.com/n8n-io/n8n/commit/60d0598a69ca75ebddb203163fbaa0e2fcb78ae0)) +* Align MCP workflow headings and remove outdated notice ([#39463](https://github.com/n8n-io/n8n/issues/39463)) ([b096147](https://github.com/n8n-io/n8n/commit/b096147894f6d77b771fe3edc14c6ccd18e26c50)) +* **API:** Accept workflow descriptions on creation ([#39705](https://github.com/n8n-io/n8n/issues/39705)) ([6c1edac](https://github.com/n8n-io/n8n/commit/6c1edac204abb4d241850fbeef95ea1ea720fc5c)) +* **API:** Allow extendsCredential in workflow create/update ([#39243](https://github.com/n8n-io/n8n/issues/39243)) ([9e7bc66](https://github.com/n8n-io/n8n/commit/9e7bc6691c2feee43fbe549010462e3f40ee6261)) +* **API:** Validate OpenAPI path parameter names ([#39067](https://github.com/n8n-io/n8n/issues/39067)) ([a2a2638](https://github.com/n8n-io/n8n/commit/a2a26384611b72fd682439036b11bd2b1d78924f)) +* **Azure OpenAI Chat Model Node:** Make the Azure Entra credential sign in as the app ([#39377](https://github.com/n8n-io/n8n/issues/39377)) ([2f98090](https://github.com/n8n-io/n8n/commit/2f98090707df0366543d79bddc8040b7d0c0a9d7)) +* **core:** Close Chat integration runtime on shutdown and let followers send ([#38958](https://github.com/n8n-io/n8n/issues/38958)) ([3628dc1](https://github.com/n8n-io/n8n/commit/3628dc1f04d5134d9a8eb861b8ba440e464fee2d)) +* **core:** Count a database ping as successful when its reply arrives during event-loop lag ([#39645](https://github.com/n8n-io/n8n/issues/39645)) ([bda9453](https://github.com/n8n-io/n8n/commit/bda94539539e77fce4c42a6086a8c187ee7b8de3)) +* **core:** Enforce one instance report per day with a unique index ([#39536](https://github.com/n8n-io/n8n/issues/39536)) ([20807d2](https://github.com/n8n-io/n8n/commit/20807d2d3d6b25d6df2e4b5b502c18bca784180a)) +* **core:** Gate the AI Assistant project list on the team-projects license ([#39235](https://github.com/n8n-io/n8n/issues/39235)) ([dec7c71](https://github.com/n8n-io/n8n/commit/dec7c712c7b9f565b3ba0176c3ca65ec7dfdea04)) +* **core:** Keep one failed binary-data dir from aborting the delete batch ([#39194](https://github.com/n8n-io/n8n/issues/39194)) ([34f77c0](https://github.com/n8n-io/n8n/commit/34f77c059b5a1176e5299c225fc3979125fba6d0)) +* **core:** Keep OpenTelemetry export working after a restart when Sentry is enabled ([#38480](https://github.com/n8n-io/n8n/issues/38480)) ([dbefd92](https://github.com/n8n-io/n8n/commit/dbefd920b5d82ecd9900fb4e129e1fe342078045)) +* **core:** Link MCP workflow access errors to workflow settings ([#39738](https://github.com/n8n-io/n8n/issues/39738)) ([9f5c752](https://github.com/n8n-io/n8n/commit/9f5c752cd179d52a91459dc98871c026fce49b51)) +* **core:** Record live n8n Assistant test runs as verification evidence ([#39361](https://github.com/n8n-io/n8n/issues/39361)) ([fb35797](https://github.com/n8n-io/n8n/commit/fb357977b5dfc7b1a88890a2b1bc76618a792983)) +* **core:** Release the expression isolate when the webhook response ends ([#39138](https://github.com/n8n-io/n8n/issues/39138)) ([82cd04c](https://github.com/n8n-io/n8n/commit/82cd04c9a6af23483728c6dd3c8ca34d8520974d)) +* **core:** Remove checkpoints when deleting agent sessions ([#39175](https://github.com/n8n-io/n8n/issues/39175)) ([6a678f0](https://github.com/n8n-io/n8n/commit/6a678f02a011c033afea3d7a71c847b8854417d0)) +* **core:** Remove feature flag for MCP Registry in AI Assistant ([#39018](https://github.com/n8n-io/n8n/issues/39018)) ([ecfec19](https://github.com/n8n-io/n8n/commit/ecfec194b481568d1d1de954068de320b5cf803b)) +* **core:** Restore pubsub subscriptions after Redis reconnect and detect half-open subscriber connections ([#39174](https://github.com/n8n-io/n8n/issues/39174)) ([3f76dcc](https://github.com/n8n-io/n8n/commit/3f76dccc32a204a48422fbc40af7f951d4c367f5)) +* **core:** Resume only private sessions in Preview ([#39151](https://github.com/n8n-io/n8n/issues/39151)) ([dda9a56](https://github.com/n8n-io/n8n/commit/dda9a56cb7faa6610d7f36b3870015a08f091ae9)) +* **core:** Retry each file removal in the byte store's delete ([#39193](https://github.com/n8n-io/n8n/issues/39193)) ([7bebbe4](https://github.com/n8n-io/n8n/commit/7bebbe403358313f4de843d8237ad5dceec9fd9f)) +* **core:** Retry instance reports that cross the UTC midnight boundary ([#39290](https://github.com/n8n-io/n8n/issues/39290)) ([f914770](https://github.com/n8n-io/n8n/commit/f9147709a639cb01cc209d3157945d53673216ca)) +* **core:** Retry the recursive removal of an execution's binary-data directory ([#39185](https://github.com/n8n-io/n8n/issues/39185)) ([3a31c38](https://github.com/n8n-io/n8n/commit/3a31c3875146cd5d728ad31d3e01d13a7ca8c0cb)) +* **core:** Return test webhook response when the call lands on another main ([#39382](https://github.com/n8n-io/n8n/issues/39382)) ([73bdae5](https://github.com/n8n-io/n8n/commit/73bdae55c655c4500a3054469c3443813469c7b4)) +* **core:** Stop retrying instance reports the receiver rejects with 400 or 413 ([#39502](https://github.com/n8n-io/n8n/issues/39502)) ([d9c9d6e](https://github.com/n8n-io/n8n/commit/d9c9d6e616eb3ef63e9077de142556508aa853df)) +* **core:** Store queue job results only for executions this process enqueued ([#39712](https://github.com/n8n-io/n8n/issues/39712)) ([5d508ec](https://github.com/n8n-io/n8n/commit/5d508ec1e1e75d51919bd38d32344846fff8ea9e)) +* **core:** Use Sessions tab terminology in n8n Assistant ([#39265](https://github.com/n8n-io/n8n/issues/39265)) ([43d7cc7](https://github.com/n8n-io/n8n/commit/43d7cc755ee5702ceb9aa41b26692fd5dc2ed482)) +* **core:** Validate token payload before resolving the user ([#39078](https://github.com/n8n-io/n8n/issues/39078)) ([7d1bae5](https://github.com/n8n-io/n8n/commit/7d1bae51fcbe1e93828a8cd5ef951f4a36749c57)) +* **editor:** Fetch publication status when a new workflow becomes saved ([#38382](https://github.com/n8n-io/n8n/issues/38382)) ([d658d18](https://github.com/n8n-io/n8n/commit/d658d182e4d58e716c7a57fd7177716a75b35cb9)) +* **editor:** Improve Agent layout and sub-agent guidance ([#39791](https://github.com/n8n-io/n8n/issues/39791)) ([3daa34d](https://github.com/n8n-io/n8n/commit/3daa34dba0ce883c7d31f8c4733afbb242bd89a9)) +* **editor:** Keep agent builder chat messages within the panel ([#39233](https://github.com/n8n-io/n8n/issues/39233)) ([9142b47](https://github.com/n8n-io/n8n/commit/9142b47bdf34ed96462e4ebb1c3ae752cfec606f)) +* **editor:** Keep agent capability chips within card width ([#39532](https://github.com/n8n-io/n8n/issues/39532)) ([9329d1f](https://github.com/n8n-io/n8n/commit/9329d1f7526195f1611fd838744b9b055590edec)) +* **editor:** Keep quick-connect OAuth credentials out of lists until the user authorizes ([#39297](https://github.com/n8n-io/n8n/issues/39297)) ([4c2f220](https://github.com/n8n-io/n8n/commit/4c2f2209712ff6892206946c2964d96d45cf528c)) +* **editor:** Keep typed text in the AI Assistant chat rename input ([#39570](https://github.com/n8n-io/n8n/issues/39570)) ([42dc399](https://github.com/n8n-io/n8n/commit/42dc3991c103711e1d1d7b502238613c13edea76)) +* **editor:** Make agent configuration header more compact ([#39172](https://github.com/n8n-io/n8n/issues/39172)) ([1c82574](https://github.com/n8n-io/n8n/commit/1c82574f86532271789c995c3c656b77c05f0df6)) +* **editor:** Move agent AI entry point below header ([#39318](https://github.com/n8n-io/n8n/issues/39318)) ([afa5fc3](https://github.com/n8n-io/n8n/commit/afa5fc3b1a216a7863222556a43e3953031f8186)) +* **editor:** Prefill the server URL in the Mistral Vibe connector link ([#39518](https://github.com/n8n-io/n8n/issues/39518)) ([d4999a2](https://github.com/n8n-io/n8n/commit/d4999a2b2f46c93c0fe1f48a451d09b6ca5b245d)) +* **editor:** Remove half-checked permissions from system instance roles and restore the Member preset ([#38652](https://github.com/n8n-io/n8n/issues/38652)) ([c114a18](https://github.com/n8n-io/n8n/commit/c114a18c877944036517106e1fccb8894fa40c1b)) +* **editor:** Shorten the Assistant name in navigation and settings ([#39288](https://github.com/n8n-io/n8n/issues/39288)) ([5cc6640](https://github.com/n8n-io/n8n/commit/5cc6640ae8ab0e46659051b53ec5aae2a3c762b1)) +* **editor:** Show a cached role at once and align the personal space block in the instance role editor ([#39394](https://github.com/n8n-io/n8n/issues/39394)) ([e648610](https://github.com/n8n-io/n8n/commit/e648610eae8c182e507681029202798fe41e3423)) +* **editor:** Show Agent Builder activity on prompt submission ([#39501](https://github.com/n8n-io/n8n/issues/39501)) ([8a0d833](https://github.com/n8n-io/n8n/commit/8a0d833080d4fced718f354a4f4e8bfc2b310176)) +* **editor:** Show project icons in Agent breadcrumbs ([#39649](https://github.com/n8n-io/n8n/issues/39649)) ([1fe19db](https://github.com/n8n-io/n8n/commit/1fe19db437e785aae631d8c4c4d1191497d9884f)) +* **editor:** Show when update available for community packages if only verified packages are enabled ([#39424](https://github.com/n8n-io/n8n/issues/39424)) ([070d9fe](https://github.com/n8n-io/n8n/commit/070d9fea4e3d731cdddc006db8abebd17ff9698d)) +* **editor:** Sorting data table rows by unknown column returns raw database error ([#39733](https://github.com/n8n-io/n8n/issues/39733)) ([b8cda55](https://github.com/n8n-io/n8n/commit/b8cda55e5713eec92328abc22dc794afe3c17bfc)) +* **Google Cloud Storage Node:** Preserve request headers on object create ([#35218](https://github.com/n8n-io/n8n/issues/35218)) ([bc9faf2](https://github.com/n8n-io/n8n/commit/bc9faf230a1b97f6c052d7e32a344d2d967fdc5a)) +* **googleFirebaseCloudFirestore Node:** Preserve field types for numeric strings and falsy values ([#27399](https://github.com/n8n-io/n8n/issues/27399)) ([b3d4cfe](https://github.com/n8n-io/n8n/commit/b3d4cfe311be36911c9c0ddc94310a0775431c7d)) +* **LinkedIn Node:** Validate post text ([#39201](https://github.com/n8n-io/n8n/issues/39201)) ([d1cd34b](https://github.com/n8n-io/n8n/commit/d1cd34b375a37f03e4ae2c266f0d8fbf49793d2d)) +* Make n8n-node release work with release-it 21 ([#39378](https://github.com/n8n-io/n8n/issues/39378)) ([d23f949](https://github.com/n8n-io/n8n/commit/d23f949801d5838d53a0c9aac0f6aefd267a5047)) +* **MCP Client Node:** Terminate session before closing the client ([#39526](https://github.com/n8n-io/n8n/issues/39526)) ([11acaa3](https://github.com/n8n-io/n8n/commit/11acaa3f2bf9d33260c94b1fff3a07a4662fe24c)) +* **Microsoft Teams Node:** Correct the v2 resource-locator copy ([#39443](https://github.com/n8n-io/n8n/issues/39443)) ([a924724](https://github.com/n8n-io/n8n/commit/a9247247af0bd3501455a929789058153b36deb8)) +* **Mistral Cloud Node:** Support citation reference chunks ([#38952](https://github.com/n8n-io/n8n/issues/38952)) ([a9bc9c5](https://github.com/n8n-io/n8n/commit/a9bc9c57e7f9cde1894decf7e8161daaaae712aa)) +* **Notion Node:** Clarify simplified output for AI workflow generation ([#39051](https://github.com/n8n-io/n8n/issues/39051)) ([67b70ea](https://github.com/n8n-io/n8n/commit/67b70ea814fc9ff32722b35057f1271c424ca1cb)) +* Preserve tables in AI workflow builder web fetch ([#38879](https://github.com/n8n-io/n8n/issues/38879)) ([07a741b](https://github.com/n8n-io/n8n/commit/07a741b59578ba6110d70bd3eb4e571f203b2c8e)) +* Preserve tool arguments in reconstructed history ([#39153](https://github.com/n8n-io/n8n/issues/39153)) ([fe0fad5](https://github.com/n8n-io/n8n/commit/fe0fad574337a5fa875a0e8650c0dbcc49a1c272)) +* Recover running preview chat after reload ([#39311](https://github.com/n8n-io/n8n/issues/39311)) ([98f012e](https://github.com/n8n-io/n8n/commit/98f012e26830bc1e926a4322aa217c93b7feb7a6)) +* Validate node codex categories during linting ([#39543](https://github.com/n8n-io/n8n/issues/39543)) ([a3aa6c0](https://github.com/n8n-io/n8n/commit/a3aa6c00749ac0905ac51a438b6d63b0d7295cea)) +* **Wise Node:** Use current sandbox API ([#39255](https://github.com/n8n-io/n8n/issues/39255)) ([3c48cef](https://github.com/n8n-io/n8n/commit/3c48cefad4bbc6e9f69a9fd1c186660adc29cb3b)) +* **Xero Node:** Send tenant header on every page when returning all items ([#39431](https://github.com/n8n-io/n8n/issues/39431)) ([58e55b5](https://github.com/n8n-io/n8n/commit/58e55b58985fccb2b37640edcdc1e84d45753f5e)) +* **Zoho CRM Node:** Update logo to current Zoho branding ([#39305](https://github.com/n8n-io/n8n/issues/39305)) ([be12181](https://github.com/n8n-io/n8n/commit/be121813f06414f0f4d0b23adfd29767cec0adc1)) + + +### Features + +* Add an n8n Assistant onboarding thread for new Cloud signups ([#39730](https://github.com/n8n-io/n8n/issues/39730)) ([d765bbd](https://github.com/n8n-io/n8n/commit/d765bbd68e9d4ee8aef219b4a68bc33bb3afaece)) +* Add credential descriptions behind an instance flag ([#39177](https://github.com/n8n-io/n8n/issues/39177)) ([57d654f](https://github.com/n8n-io/n8n/commit/57d654f9726c779989f752b15fa02e276e8782bd)) +* Add Gateway credits option to Agent web search ([#39169](https://github.com/n8n-io/n8n/issues/39169)) ([4dbe8de](https://github.com/n8n-io/n8n/commit/4dbe8de82d070a34f4c87b34716e9457cbd5f0f0)) +* Add pinned Apply and Apply Continue to the promotion CLI commands ([#39597](https://github.com/n8n-io/n8n/issues/39597)) ([6c57533](https://github.com/n8n-io/n8n/commit/6c57533958da912e1da163f7ac1f60447fb11a76)) +* **ai-builder:** Add approval settings for agent channel actions ([#39059](https://github.com/n8n-io/n8n/issues/39059)) ([d9f6568](https://github.com/n8n-io/n8n/commit/d9f65684f8bbfd6b08c9e9e3d29842f61f663070)) +* **ai-builder:** Confirm template selections before finishing the build ([#39517](https://github.com/n8n-io/n8n/issues/39517)) ([80e4084](https://github.com/n8n-io/n8n/commit/80e408442066bb5697d4012d1f2ce835afd637c4)) +* **ai-builder:** Prompt suggestions and intro on empty agent builder aia ([#39098](https://github.com/n8n-io/n8n/issues/39098)) ([9ee6a8f](https://github.com/n8n-io/n8n/commit/9ee6a8f88b0f47a2b92c511c7c549e1af5a6c41b)) +* **Anthropic Node:** Add prompt caching to the Message operation ([#35804](https://github.com/n8n-io/n8n/issues/35804)) ([6b9c48f](https://github.com/n8n-io/n8n/commit/6b9c48fe4dba5785958f2167549a2c64033ffaf6)) +* **API:** Create projects with source IDs and package metadata ([#39218](https://github.com/n8n-io/n8n/issues/39218)) ([8012f01](https://github.com/n8n-io/n8n/commit/8012f017bc80a35cef0c3012033e9089c9db8e03)) +* **API:** Migrate GET /users endpoints to the decorator pattern ([#38599](https://github.com/n8n-io/n8n/issues/38599)) ([6433934](https://github.com/n8n-io/n8n/commit/6433934db70b10073026235263c35687c5d3f239)) +* **API:** Migrate users write endpoints to the decorator pattern ([#39120](https://github.com/n8n-io/n8n/issues/39120)) ([428bab3](https://github.com/n8n-io/n8n/commit/428bab3201ad5c1cbfac1e7dc2ca793f9226b770)) +* **Azure OpenAI Chat Model Node:** Add the project input and the deployment list call ([#39375](https://github.com/n8n-io/n8n/issues/39375)) ([4e3d5c1](https://github.com/n8n-io/n8n/commit/4e3d5c10f1480db790d695c8c91dc626ba02c78d)) +* **Azure OpenAI Chat Model Node:** Rename to Azure AI Foundry Chat Model ([#39603](https://github.com/n8n-io/n8n/issues/39603)) ([ee9dfd1](https://github.com/n8n-io/n8n/commit/ee9dfd1a377e815667641b21920a16534e0d3dd4)) +* **core:** Add browser OAuth2 flow for n8n User Auth webhooks ([#39332](https://github.com/n8n-io/n8n/issues/39332)) ([38c92f4](https://github.com/n8n-io/n8n/commit/38c92f40116cdfb3b24d9daeecbf939812dcbe59)) +* **core:** Add feature flag for granular credential sharing work ([#39293](https://github.com/n8n-io/n8n/issues/39293)) ([fc68bb6](https://github.com/n8n-io/n8n/commit/fc68bb6a8f1bbfb521e2f3bbaf06ec0cf1bbc28c)) +* **core:** Add shared Agent context access ([#38930](https://github.com/n8n-io/n8n/issues/38930)) ([8a0ca48](https://github.com/n8n-io/n8n/commit/8a0ca48bb72a8fbdcba32bcdc94723d43421f991)) +* **core:** Add the durable agent message queue foundation ([#39435](https://github.com/n8n-io/n8n/issues/39435)) ([6c592bd](https://github.com/n8n-io/n8n/commit/6c592bdb3306a3cfc29e25872135486c4a45940c)) +* **core:** Allows custom roles to have view/use/manage access to instance credentials ([#39306](https://github.com/n8n-io/n8n/issues/39306)) ([72a14b0](https://github.com/n8n-io/n8n/commit/72a14b078cd8308c87e1f3e458bd7fce903042f5)) +* **core:** Answer lastNode webhooks from execution responses ([#38959](https://github.com/n8n-io/n8n/issues/38959)) ([dc3283d](https://github.com/n8n-io/n8n/commit/dc3283da192df70bc8fe7e610b1e4401b87a0301)) +* **core:** Attach the publishing user to triggered executions ([#39466](https://github.com/n8n-io/n8n/issues/39466)) ([6c4135d](https://github.com/n8n-io/n8n/commit/6c4135ddfb59f4ce3feec71559e343289e5c0b36)) +* **core:** Authenticate instance reports with the license certificate ([#39217](https://github.com/n8n-io/n8n/issues/39217)) ([a182724](https://github.com/n8n-io/n8n/commit/a1827241362b0440689b1919af259bda6b5e0d42)) +* **core:** Check credentials against the person running the workflow ([#39685](https://github.com/n8n-io/n8n/issues/39685)) ([4a01756](https://github.com/n8n-io/n8n/commit/4a01756ea453bc7cbdbc869968315934f76e800b)) +* **core:** Connect Preview and integrations to the agent queue ([#39468](https://github.com/n8n-io/n8n/issues/39468)) ([5ca15b3](https://github.com/n8n-io/n8n/commit/5ca15b37930e6ce41d10fa0eed2a1af3f026b34b)) +* **core:** Enable Agents by default ([#39312](https://github.com/n8n-io/n8n/issues/39312)) ([94b9627](https://github.com/n8n-io/n8n/commit/94b9627f75f5851cecf396af73d308504039155d)) +* **core:** Propagate project span attributes to node spans ([#39420](https://github.com/n8n-io/n8n/issues/39420)) ([ea1e4ef](https://github.com/n8n-io/n8n/commit/ea1e4ef249ad858fc3dce3fc9564fa6758478999)) +* **core:** Report crashed executions to OTel through an execution-crashed event ([#38853](https://github.com/n8n-io/n8n/issues/38853)) ([5131af5](https://github.com/n8n-io/n8n/commit/5131af5ab79ede69564792784cdc5997c2e1d3d2)) +* **core:** Scope agent preview sessions to their owner ([#39100](https://github.com/n8n-io/n8n/issues/39100)) ([5ba266d](https://github.com/n8n-io/n8n/commit/5ba266db25b794b254922cf68f821bf595751872)) +* **core:** Set OS process titles for n8n processes ([#39362](https://github.com/n8n-io/n8n/issues/39362)) ([53dc251](https://github.com/n8n-io/n8n/commit/53dc2515081cb534cf224349dca4f1111413591a)) +* **Databricks Node:** Show the Databricks Trigger in the node picker ([#39176](https://github.com/n8n-io/n8n/issues/39176)) ([27bb095](https://github.com/n8n-io/n8n/commit/27bb095b4580ed775227a5b50c75bdfb3968a670)) +* **editor:** Add a write lock to the agent builder ([#38758](https://github.com/n8n-io/n8n/issues/38758)) ([3b3ac39](https://github.com/n8n-io/n8n/commit/3b3ac393ba80ba66d4d891cc3ce0b478089ea9dd)) +* **editor:** Add adaptive comment input to the review activity feed ([#39583](https://github.com/n8n-io/n8n/issues/39583)) ([dda1166](https://github.com/n8n-io/n8n/commit/dda1166326595d0b5af65c6d16f0a5174af527de)) +* **editor:** Add destination binding dialog ([#39214](https://github.com/n8n-io/n8n/issues/39214)) ([af838bc](https://github.com/n8n-io/n8n/commit/af838bc8ea2b161110330946e4dcd63ff43b63ff)) +* **editor:** Add expanded N8nMarkdownEditor view ([#39296](https://github.com/n8n-io/n8n/issues/39296)) ([9f89eca](https://github.com/n8n-io/n8n/commit/9f89ecab7eb8dadf01f6b298009d597918f4e513)) +* **editor:** Add Mistral Vibe to the MCP connect-client picker ([#39183](https://github.com/n8n-io/n8n/issues/39183)) ([183d769](https://github.com/n8n-io/n8n/commit/183d769841bace1c600cb0bb349e69ccfecec8c1)) +* **editor:** Add queued message controls to agent Preview chat ([#39487](https://github.com/n8n-io/n8n/issues/39487)) ([8a4fd13](https://github.com/n8n-io/n8n/commit/8a4fd13423331e4059472cc568ed6a3e408b3bde)) +* **editor:** Animate height changes in the MCP callback URLs dialog ([#39490](https://github.com/n8n-io/n8n/issues/39490)) ([8d48d90](https://github.com/n8n-io/n8n/commit/8d48d90d7296ba432a2752720eff9ade67f46751)) +* **editor:** Create credentials and variables from promotion bindings ([#39342](https://github.com/n8n-io/n8n/issues/39342)) ([2b891f1](https://github.com/n8n-io/n8n/commit/2b891f17a54f41f4aa6b83f4c24e7e2f778f31a9)) +* **editor:** Displays instance admins (and owner) in all project user lists ([#39419](https://github.com/n8n-io/n8n/issues/39419)) ([96735b2](https://github.com/n8n-io/n8n/commit/96735b20d9ca9e591f765dc9437630305f41e370)) +* **editor:** Group canvas toolbar buttons in a single container ([#39587](https://github.com/n8n-io/n8n/issues/39587)) ([a96980b](https://github.com/n8n-io/n8n/commit/a96980bcc278400d8e2e34fff61f8ef031d639dc)) +* **editor:** Group the node credential picker by where each credential comes from ([#39646](https://github.com/n8n-io/n8n/issues/39646)) ([9af0f02](https://github.com/n8n-io/n8n/commit/9af0f02a74021e882762bf1ab1c3420fa9d5857b)) +* **editor:** Implement Reka UI based context menu for design system ([#37925](https://github.com/n8n-io/n8n/issues/37925)) ([22d18a7](https://github.com/n8n-io/n8n/commit/22d18a7bc9ec714027a6756ea3d6e79f0811535f)) +* **editor:** Improve Agent Builder intro interactions ([#39590](https://github.com/n8n-io/n8n/issues/39590)) ([b09676e](https://github.com/n8n-io/n8n/commit/b09676e34adfeadf796e5d469967c1ffe81b1386)) +* **editor:** Make SSO the primary option on the sign-in page when SSO is enabled ([#39268](https://github.com/n8n-io/n8n/issues/39268)) ([0827eb8](https://github.com/n8n-io/n8n/commit/0827eb8eda9ee2436763d5add1154c574a7be6ec)) +* **editor:** Preview own connected clients on the MCP settings overview ([#39489](https://github.com/n8n-io/n8n/issues/39489)) ([fdc6b9d](https://github.com/n8n-io/n8n/commit/fdc6b9d8921b002ac77b061e3c41f7cf50a8e470)) +* **editor:** Restyle AI Assistant tab bar and add a tab hover card ([#39625](https://github.com/n8n-io/n8n/issues/39625)) ([88d8390](https://github.com/n8n-io/n8n/commit/88d839022296630c6e74e41fcf7426bc4e5a3359)) +* **editor:** Revamp the Agent skills modal ([#39545](https://github.com/n8n-io/n8n/issues/39545)) ([4263117](https://github.com/n8n-io/n8n/commit/4263117b480a36510c2491aae0114e93175900b0)) +* **editor:** Route assistant credit CTAs to top-up for UBB accounts ([#39416](https://github.com/n8n-io/n8n/issues/39416)) ([dd4e7f6](https://github.com/n8n-io/n8n/commit/dd4e7f62b2ed0503b6d6fe550d4ccfc572b8ac1a)) +* **editor:** Show a restricted node panel in the NDV with contact-admin and replace paths ([#38869](https://github.com/n8n-io/n8n/issues/38869)) ([d42b762](https://github.com/n8n-io/n8n/commit/d42b7621deddaf13bd6353b242dbd817e6167b6f)) +* **editor:** Show each policy violation with its scope when a save, publish or execution is refused ([#39263](https://github.com/n8n-io/n8n/issues/39263)) ([2ec6ac7](https://github.com/n8n-io/n8n/commit/2ec6ac7c9cd0a73490b7a083b42ef3462db4de8e)) +* **editor:** Standardize Agent Builder modals ([#39220](https://github.com/n8n-io/n8n/issues/39220)) ([042d167](https://github.com/n8n-io/n8n/commit/042d16718ef74f0f73505f0ebfc868c480261ef5)) +* **editor:** Suggest Gateway credits after credential errors ([#38818](https://github.com/n8n-io/n8n/issues/38818)) ([7af6741](https://github.com/n8n-io/n8n/commit/7af6741bf29605d6db50cb60fa005bfeef6206a8)) +* **editor:** Use test label and icon for agent preview ([#38939](https://github.com/n8n-io/n8n/issues/38939)) ([b6b4e54](https://github.com/n8n-io/n8n/commit/b6b4e542515d32e395e8f854da06cc86610c7d1e)) +* **Microsoft Teams Node:** Add online meeting attendees on Create, Create or Get and Update ([#38850](https://github.com/n8n-io/n8n/issues/38850)) ([eb5eef3](https://github.com/n8n-io/n8n/commit/eb5eef341f068824c68f30e9ce8573e546f88e14)) +* **Microsoft Teams Node:** Send activity feed notifications to a user ([#38954](https://github.com/n8n-io/n8n/issues/38954)) ([dfe014a](https://github.com/n8n-io/n8n/commit/dfe014a2ba414efc5b37f20da3f2414db7716f4c)) +* Suggest Gateway credit services during node discovery ([#39631](https://github.com/n8n-io/n8n/issues/39631)) ([2b0719d](https://github.com/n8n-io/n8n/commit/2b0719dc70e1859701352a8589c544fdb0692e6c)) + + +### Performance Improvements + +* **core:** Avoid terminal outbox scans during publication reconciliation ([#39267](https://github.com/n8n-io/n8n/issues/39267)) ([6272d46](https://github.com/n8n-io/n8n/commit/6272d465df4d4e5455f9223b6649e6228dcb0a59)) + + # [2.41.0](https://github.com/n8n-io/n8n/compare/n8n@2.40.0...n8n@2.41.0) (2026-09-22) diff --git a/package.json b/package.json index c51e303cccc..d5ee0cbf5a4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n-monorepo", - "version": "2.41.0", + "version": "2.42.0", "private": true, "engines": { "node": ">=24.0.0", diff --git a/packages/@n8n/agents/package.json b/packages/@n8n/agents/package.json index 393e6d442fa..b10ec97b1d2 100644 --- a/packages/@n8n/agents/package.json +++ b/packages/@n8n/agents/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/agents", - "version": "0.26.0", + "version": "0.27.0", "description": "AI agent SDK for n8n's code-first execution engine", "engines": { "node": ">=24.0.0" diff --git a/packages/@n8n/ai-node-sdk/package.json b/packages/@n8n/ai-node-sdk/package.json index aa34e9477f5..2eb7f19bcf8 100644 --- a/packages/@n8n/ai-node-sdk/package.json +++ b/packages/@n8n/ai-node-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/ai-node-sdk", - "version": "0.31.0", + "version": "0.32.0", "description": "SDK for building AI nodes in n8n", "types": "dist/esm/index.d.ts", "module": "dist/esm/index.js", diff --git a/packages/@n8n/ai-utilities/package.json b/packages/@n8n/ai-utilities/package.json index bc3313a6562..1808864e75e 100644 --- a/packages/@n8n/ai-utilities/package.json +++ b/packages/@n8n/ai-utilities/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/ai-utilities", - "version": "0.34.0", + "version": "0.35.0", "description": "Utilities for building AI nodes in n8n", "types": "dist/esm/index.d.ts", "module": "dist/esm/index.js", diff --git a/packages/@n8n/ai-workflow-builder.ee/package.json b/packages/@n8n/ai-workflow-builder.ee/package.json index 7c5c112697a..5060863a0be 100644 --- a/packages/@n8n/ai-workflow-builder.ee/package.json +++ b/packages/@n8n/ai-workflow-builder.ee/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/ai-workflow-builder", - "version": "1.41.0", + "version": "1.42.0", "scripts": { "clean": "rimraf dist .turbo", "typecheck": "tsc --noEmit", diff --git a/packages/@n8n/api-types/package.json b/packages/@n8n/api-types/package.json index 9a89f5667e1..82756470753 100644 --- a/packages/@n8n/api-types/package.json +++ b/packages/@n8n/api-types/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/api-types", - "version": "1.41.0", + "version": "1.42.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/backend-common/package.json b/packages/@n8n/backend-common/package.json index 8bc779724f6..26e7204d4be 100644 --- a/packages/@n8n/backend-common/package.json +++ b/packages/@n8n/backend-common/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/backend-common", - "version": "1.41.0", + "version": "1.42.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/backend-network/package.json b/packages/@n8n/backend-network/package.json index 3b3ed1161e5..53265e2a043 100644 --- a/packages/@n8n/backend-network/package.json +++ b/packages/@n8n/backend-network/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/backend-network", - "version": "1.15.0", + "version": "1.16.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/backend-services/package.json b/packages/@n8n/backend-services/package.json index 439c8ab5f53..6ded6ee54ef 100644 --- a/packages/@n8n/backend-services/package.json +++ b/packages/@n8n/backend-services/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/backend-services", - "version": "1.1.0", + "version": "1.2.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/backend-test-utils/package.json b/packages/@n8n/backend-test-utils/package.json index 5dba2e3d485..2f66d27ee29 100644 --- a/packages/@n8n/backend-test-utils/package.json +++ b/packages/@n8n/backend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/backend-test-utils", - "version": "1.41.0", + "version": "1.42.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/benchmark/package.json b/packages/@n8n/benchmark/package.json index 2b343fdd5c1..e5707c421e4 100644 --- a/packages/@n8n/benchmark/package.json +++ b/packages/@n8n/benchmark/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/n8n-benchmark", - "version": "2.22.0", + "version": "2.23.0", "description": "Cli for running benchmark tests for n8n", "main": "dist/index", "scripts": { diff --git a/packages/@n8n/blob-storage/package.json b/packages/@n8n/blob-storage/package.json index 3a0e723bf5d..a9ede9620b7 100644 --- a/packages/@n8n/blob-storage/package.json +++ b/packages/@n8n/blob-storage/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/blob-storage", - "version": "1.9.0", + "version": "1.10.0", "description": "Blob storage layer for n8n, with filesystem, S3, and Azure Blob backends", "scripts": { "clean": "rimraf dist .turbo", diff --git a/packages/@n8n/chat-hub/package.json b/packages/@n8n/chat-hub/package.json index 8cf7a45273f..cd18446f84f 100644 --- a/packages/@n8n/chat-hub/package.json +++ b/packages/@n8n/chat-hub/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/chat-hub", - "version": "1.34.0", + "version": "1.35.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/cli/package.json b/packages/@n8n/cli/package.json index 96b63f338d5..7d666edcdc8 100644 --- a/packages/@n8n/cli/package.json +++ b/packages/@n8n/cli/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/cli", - "version": "0.20.0", + "version": "0.21.0", "description": "Client CLI for n8n — manage workflows, executions, and credentials from the terminal", "license": "LicenseRef-n8n-sustainable-use", "bin": { diff --git a/packages/@n8n/client-oauth2/package.json b/packages/@n8n/client-oauth2/package.json index 77125e2f19f..0bb93ed9bfc 100644 --- a/packages/@n8n/client-oauth2/package.json +++ b/packages/@n8n/client-oauth2/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/client-oauth2", - "version": "1.23.0", + "version": "1.24.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/computer-use/package.json b/packages/@n8n/computer-use/package.json index b0aa3211bcc..e013ba1b1e2 100644 --- a/packages/@n8n/computer-use/package.json +++ b/packages/@n8n/computer-use/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/computer-use", - "version": "0.24.0", + "version": "0.25.0", "description": "Local AI gateway for n8n Assistant — filesystem, shell, screenshots, mouse/keyboard, and browser automation", "publishConfig": { "bin": { diff --git a/packages/@n8n/config/package.json b/packages/@n8n/config/package.json index bf9b0bce237..b916a41f832 100644 --- a/packages/@n8n/config/package.json +++ b/packages/@n8n/config/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/config", - "version": "2.39.0", + "version": "2.40.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/constants/package.json b/packages/@n8n/constants/package.json index f3435457d5f..d0c1f81e7c1 100644 --- a/packages/@n8n/constants/package.json +++ b/packages/@n8n/constants/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/constants", - "version": "0.39.0", + "version": "0.40.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/crdt/package.json b/packages/@n8n/crdt/package.json index 5438c1ad9ff..229343e4978 100644 --- a/packages/@n8n/crdt/package.json +++ b/packages/@n8n/crdt/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/crdt", - "version": "0.11.0", + "version": "0.12.0", "description": "CRDT abstraction layer for n8n collaborative editing", "scripts": { "clean": "rimraf dist .turbo", diff --git a/packages/@n8n/create-node/package.json b/packages/@n8n/create-node/package.json index bc75fcb1e25..43c3d5e39e4 100644 --- a/packages/@n8n/create-node/package.json +++ b/packages/@n8n/create-node/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/create-node", - "version": "0.49.0", + "version": "0.50.0", "description": "Official CLI to create new community nodes for n8n", "bin": { "create-node": "bin/create-node.cjs" diff --git a/packages/@n8n/db/package.json b/packages/@n8n/db/package.json index 79fda1ce664..aa6ebb8d6ad 100644 --- a/packages/@n8n/db/package.json +++ b/packages/@n8n/db/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/db", - "version": "1.41.0", + "version": "1.42.0", "scripts": { "clean": "rimraf dist .turbo .tmp-schema-docs", "gen:migration-index": "node scripts/generate-migration-index.mjs", diff --git a/packages/@n8n/decorators/package.json b/packages/@n8n/decorators/package.json index 88b48fe0246..f8c8b3f6fff 100644 --- a/packages/@n8n/decorators/package.json +++ b/packages/@n8n/decorators/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/decorators", - "version": "1.41.0", + "version": "1.42.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/engine/package.json b/packages/@n8n/engine/package.json index 9f4f4a68ab9..b19c5422ebe 100644 --- a/packages/@n8n/engine/package.json +++ b/packages/@n8n/engine/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/engine", - "version": "0.21.0", + "version": "0.22.0", "description": "n8n workflow execution engine (v2)", "scripts": { "clean": "rimraf dist .turbo compiled", diff --git a/packages/@n8n/eslint-plugin-community-nodes/package.json b/packages/@n8n/eslint-plugin-community-nodes/package.json index 18d05a93f7f..caf705c0d28 100644 --- a/packages/@n8n/eslint-plugin-community-nodes/package.json +++ b/packages/@n8n/eslint-plugin-community-nodes/package.json @@ -1,7 +1,7 @@ { "name": "@n8n/eslint-plugin-community-nodes", "type": "module", - "version": "0.34.0", + "version": "0.35.0", "main": "./dist/plugin.js", "types": "./dist/plugin.d.ts", "exports": { diff --git a/packages/@n8n/expression-runtime/package.json b/packages/@n8n/expression-runtime/package.json index fd7b6894330..bb643aa2cd2 100644 --- a/packages/@n8n/expression-runtime/package.json +++ b/packages/@n8n/expression-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/expression-runtime", - "version": "0.32.0", + "version": "0.33.0", "description": "Secure, isolated expression evaluation runtime for n8n", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", diff --git a/packages/@n8n/imap/package.json b/packages/@n8n/imap/package.json index 25085573044..84b327804bd 100644 --- a/packages/@n8n/imap/package.json +++ b/packages/@n8n/imap/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/imap", - "version": "0.27.0", + "version": "0.28.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/instance-ai/package.json b/packages/@n8n/instance-ai/package.json index 4335785cdbe..add91a691bc 100644 --- a/packages/@n8n/instance-ai/package.json +++ b/packages/@n8n/instance-ai/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/instance-ai", - "version": "1.26.0", + "version": "1.27.0", "scripts": { "clean": "rimraf dist .turbo", "typecheck": "tsc --noEmit && tsc -p evaluations/tsconfig.json", diff --git a/packages/@n8n/json-schema-to-zod/package.json b/packages/@n8n/json-schema-to-zod/package.json index 4be80726460..2ef25e97ea8 100644 --- a/packages/@n8n/json-schema-to-zod/package.json +++ b/packages/@n8n/json-schema-to-zod/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/json-schema-to-zod", - "version": "1.16.0", + "version": "1.17.0", "description": "Converts JSON schema objects into Zod schemas", "types": "./dist/types/index.d.ts", "main": "./dist/cjs/index.js", diff --git a/packages/@n8n/mcp-apps/package.json b/packages/@n8n/mcp-apps/package.json index 8d545366838..bfe2c00a983 100644 --- a/packages/@n8n/mcp-apps/package.json +++ b/packages/@n8n/mcp-apps/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/mcp-apps", - "version": "0.18.0", + "version": "0.19.0", "description": "MCP Apps UI resources and server helpers for n8n", "main": "dist/server/index.js", "types": "dist/server/index.d.ts", diff --git a/packages/@n8n/node-cli/package.json b/packages/@n8n/node-cli/package.json index 636052e97ee..e0783e7203c 100644 --- a/packages/@n8n/node-cli/package.json +++ b/packages/@n8n/node-cli/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/node-cli", - "version": "0.50.0", + "version": "0.51.0", "description": "Official CLI for developing community nodes for n8n", "bin": { "n8n-node": "bin/n8n-node.mjs" diff --git a/packages/@n8n/node-engine-compatibility/package.json b/packages/@n8n/node-engine-compatibility/package.json index 641704e945d..1bd0e0cabb3 100644 --- a/packages/@n8n/node-engine-compatibility/package.json +++ b/packages/@n8n/node-engine-compatibility/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/node-engine-compatibility", - "version": "0.9.0", + "version": "0.10.0", "description": "Adapts v1 nodes and workflows to the Engine v2 execution model", "scripts": { "clean": "rimraf dist .turbo", diff --git a/packages/@n8n/nodes-langchain/package.json b/packages/@n8n/nodes-langchain/package.json index 7255d7c24d7..dd4076a60b5 100644 --- a/packages/@n8n/nodes-langchain/package.json +++ b/packages/@n8n/nodes-langchain/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/n8n-nodes-langchain", - "version": "2.41.0", + "version": "2.42.0", "description": "", "main": "index.js", "exports": { diff --git a/packages/@n8n/permissions/package.json b/packages/@n8n/permissions/package.json index 25da67464d5..8403cef05d5 100644 --- a/packages/@n8n/permissions/package.json +++ b/packages/@n8n/permissions/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/permissions", - "version": "0.78.0", + "version": "0.79.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/scan-community-package/package.json b/packages/@n8n/scan-community-package/package.json index cb62c6f028d..4a8438c8a44 100644 --- a/packages/@n8n/scan-community-package/package.json +++ b/packages/@n8n/scan-community-package/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/scan-community-package", - "version": "0.37.0", + "version": "0.38.0", "description": "Static code analyser for n8n community packages", "license": "LicenseRef-n8n-sustainable-use", "bin": "scanner/cli.mjs", diff --git a/packages/@n8n/scheduler/package.json b/packages/@n8n/scheduler/package.json index 9398e866bab..22a7eebacef 100644 --- a/packages/@n8n/scheduler/package.json +++ b/packages/@n8n/scheduler/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/scheduler", - "version": "1.10.0", + "version": "1.11.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/syslog-client/package.json b/packages/@n8n/syslog-client/package.json index 6d6a215a658..9d8b363e7a8 100644 --- a/packages/@n8n/syslog-client/package.json +++ b/packages/@n8n/syslog-client/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/syslog-client", - "version": "1.11.0", + "version": "1.12.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/task-runner/package.json b/packages/@n8n/task-runner/package.json index 49f73835e7a..9e7d60cc20e 100644 --- a/packages/@n8n/task-runner/package.json +++ b/packages/@n8n/task-runner/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/task-runner", - "version": "2.41.0", + "version": "2.42.0", "scripts": { "clean": "rimraf dist .turbo", "start": "node dist/start.js", diff --git a/packages/@n8n/telemetry/package.json b/packages/@n8n/telemetry/package.json index dfa44e8a516..7fb206c5432 100644 --- a/packages/@n8n/telemetry/package.json +++ b/packages/@n8n/telemetry/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/telemetry", - "version": "1.9.0", + "version": "1.10.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/utils/package.json b/packages/@n8n/utils/package.json index b0c20c7e0c5..a73fe3cb1ad 100644 --- a/packages/@n8n/utils/package.json +++ b/packages/@n8n/utils/package.json @@ -1,7 +1,7 @@ { "name": "@n8n/utils", "type": "module", - "version": "1.48.0", + "version": "1.49.0", "files": [ "dist" ], diff --git a/packages/@n8n/vitest-config/package.json b/packages/@n8n/vitest-config/package.json index d3a3aeae045..060065035aa 100644 --- a/packages/@n8n/vitest-config/package.json +++ b/packages/@n8n/vitest-config/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/vitest-config", - "version": "1.22.0", + "version": "1.23.0", "type": "module", "peerDependencies": { "@testing-library/jest-dom": "catalog:frontend", diff --git a/packages/@n8n/workflow-sdk/package.json b/packages/@n8n/workflow-sdk/package.json index 0eeba97793b..9ffdd64749f 100644 --- a/packages/@n8n/workflow-sdk/package.json +++ b/packages/@n8n/workflow-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/workflow-sdk", - "version": "0.34.0", + "version": "0.35.0", "description": "TypeScript SDK for programmatically creating n8n workflows", "exports": { ".": { diff --git a/packages/cli/package.json b/packages/cli/package.json index 81321cb5a57..47c4091453c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "2.41.0", + "version": "2.42.0", "description": "n8n Workflow Automation Tool", "main": "dist/index", "types": "dist/index.d.ts", diff --git a/packages/core/package.json b/packages/core/package.json index 693d1b5d296..bdffb10ca34 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "n8n-core", - "version": "2.41.0", + "version": "2.42.0", "description": "Core functionality of n8n", "main": "dist/index", "types": "dist/index.d.ts", diff --git a/packages/frontend/@n8n/chat/package.json b/packages/frontend/@n8n/chat/package.json index a941304be57..304770cbebd 100644 --- a/packages/frontend/@n8n/chat/package.json +++ b/packages/frontend/@n8n/chat/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/chat", - "version": "1.40.0", + "version": "1.41.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm run --dir=../storybook dev --initial-path=/docs/chat-chat--docs", diff --git a/packages/frontend/@n8n/composables/package.json b/packages/frontend/@n8n/composables/package.json index efade1f89b1..a30915a4a4b 100644 --- a/packages/frontend/@n8n/composables/package.json +++ b/packages/frontend/@n8n/composables/package.json @@ -1,7 +1,7 @@ { "name": "@n8n/composables", "type": "module", - "version": "1.31.0", + "version": "1.32.0", "files": [ "dist" ], diff --git a/packages/frontend/@n8n/design-system/package.json b/packages/frontend/@n8n/design-system/package.json index 9bf22552974..05eab7fe6f4 100644 --- a/packages/frontend/@n8n/design-system/package.json +++ b/packages/frontend/@n8n/design-system/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "@n8n/design-system", - "version": "2.40.0", + "version": "2.41.0", "main": "./dist/index.js", "module": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/frontend/@n8n/frontend-constants/package.json b/packages/frontend/@n8n/frontend-constants/package.json index 8b886e5335a..24dfc967534 100644 --- a/packages/frontend/@n8n/frontend-constants/package.json +++ b/packages/frontend/@n8n/frontend-constants/package.json @@ -1,7 +1,7 @@ { "name": "@n8n/frontend-constants", "type": "module", - "version": "0.8.0", + "version": "0.9.0", "files": [ "dist" ], diff --git a/packages/frontend/@n8n/frontend-module-sdk/package.json b/packages/frontend/@n8n/frontend-module-sdk/package.json index e603174e19d..a2737ec7f64 100644 --- a/packages/frontend/@n8n/frontend-module-sdk/package.json +++ b/packages/frontend/@n8n/frontend-module-sdk/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "@n8n/frontend-module-sdk", - "version": "0.11.0", + "version": "0.12.0", "main": "src/index.ts", "import": "src/index.ts", "exports": { diff --git a/packages/frontend/@n8n/i18n/package.json b/packages/frontend/@n8n/i18n/package.json index bfa1ed6568d..25946ee24b8 100644 --- a/packages/frontend/@n8n/i18n/package.json +++ b/packages/frontend/@n8n/i18n/package.json @@ -1,7 +1,7 @@ { "name": "@n8n/i18n", "type": "module", - "version": "2.41.0", + "version": "2.42.0", "files": [ "dist" ], diff --git a/packages/frontend/@n8n/rest-api-client/package.json b/packages/frontend/@n8n/rest-api-client/package.json index 5f0c488a9d9..b8aa3c100aa 100644 --- a/packages/frontend/@n8n/rest-api-client/package.json +++ b/packages/frontend/@n8n/rest-api-client/package.json @@ -1,7 +1,7 @@ { "name": "@n8n/rest-api-client", "type": "module", - "version": "2.41.0", + "version": "2.42.0", "files": [ "dist" ], diff --git a/packages/frontend/@n8n/stores/package.json b/packages/frontend/@n8n/stores/package.json index afb59a67234..90a0470d054 100644 --- a/packages/frontend/@n8n/stores/package.json +++ b/packages/frontend/@n8n/stores/package.json @@ -1,7 +1,7 @@ { "name": "@n8n/stores", "type": "module", - "version": "2.41.0", + "version": "2.42.0", "files": [ "dist" ], diff --git a/packages/frontend/editor-ui/package.json b/packages/frontend/editor-ui/package.json index 2ccd717bbad..c7497e69f26 100644 --- a/packages/frontend/editor-ui/package.json +++ b/packages/frontend/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "2.41.0", + "version": "2.42.0", "description": "Workflow Editor UI for n8n", "main": "index.js", "type": "module", diff --git a/packages/modules/insights/frontend/package.json b/packages/modules/insights/frontend/package.json index 326294c809e..1993b7776d5 100644 --- a/packages/modules/insights/frontend/package.json +++ b/packages/modules/insights/frontend/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/frontend-module-insights", - "version": "0.3.0", + "version": "0.4.0", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/modules/instance-registry/frontend/package.json b/packages/modules/instance-registry/frontend/package.json index 048a252b9a3..510b2be6216 100644 --- a/packages/modules/instance-registry/frontend/package.json +++ b/packages/modules/instance-registry/frontend/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/frontend-module-instance-registry", - "version": "0.6.0", + "version": "0.7.0", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/modules/otel/frontend/package.json b/packages/modules/otel/frontend/package.json index 0466e39556a..a19a8a746c2 100644 --- a/packages/modules/otel/frontend/package.json +++ b/packages/modules/otel/frontend/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/frontend-module-otel", - "version": "0.5.0", + "version": "0.6.0", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/modules/type-availability-policies/frontend/package.json b/packages/modules/type-availability-policies/frontend/package.json index 9ada49f6f40..a408a8163f3 100644 --- a/packages/modules/type-availability-policies/frontend/package.json +++ b/packages/modules/type-availability-policies/frontend/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/frontend-module-type-availability-policies", - "version": "0.2.0", + "version": "0.3.0", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 52f17b8f1e5..5cdccc0c2e8 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "2.41.0", + "version": "2.42.0", "description": "Base nodes of n8n", "main": "index.js", "typesVersions": { diff --git a/packages/workflow/package.json b/packages/workflow/package.json index c143f2da2b7..85be52da007 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -1,6 +1,6 @@ { "name": "n8n-workflow", - "version": "2.41.0", + "version": "2.42.0", "description": "Workflow base code of n8n", "types": "dist/esm/index.d.ts", "module": "dist/esm/index.js", From 2bf9b94936b7a01218496e3be0a694f0cb174e32 Mon Sep 17 00:00:00 2001 From: Charlie Kolb Date: Tue, 29 Sep 2026 07:19:05 +0000 Subject: [PATCH 05/55] refactor(core): Move shared errors to @n8n/errors (no-changelog) (#39753) --- .../base => @n8n/errors/src}/base.error.ts | 4 ++-- packages/@n8n/errors/src/index.ts | 21 +++++++++++++++++++ .../errors/src}/operational.error.ts | 3 +-- .../errors/src}/response-errors/auth.error.ts | 2 +- .../src}/response-errors/bad-request.error.ts | 2 +- .../src}/response-errors/conflict.error.ts | 2 +- .../content-too-large.error.ts | 2 +- .../src}/response-errors/forbidden.error.ts | 2 +- .../response-errors/internal-server.error.ts | 2 +- .../src}/response-errors/locked.error.ts | 2 +- .../src}/response-errors/not-found.error.ts | 2 +- .../response-errors/not-implemented.error.ts | 2 +- .../response-errors/payment-required.error.ts | 2 +- .../src/response-errors}/response.error.ts | 2 +- .../response-errors/scope-forbidden.error.ts | 2 +- .../service-unavailable.error.ts | 2 +- .../too-many-requests.error.ts | 2 +- .../response-errors/unauthenticated.error.ts | 2 +- .../response-errors/unprocessable.error.ts | 2 +- .../unsupported-media-type.error.ts | 2 +- .../errors/src}/unexpected.error.ts | 3 +-- .../base => @n8n/errors/src}/user.error.ts | 3 +-- .../src/__tests__/controller.registry.test.ts | 2 +- .../cli/src/__tests__/response-helper.test.ts | 5 +---- .../src/__tests__/workflow-helpers.test.ts | 2 +- packages/cli/src/abstract-server.ts | 2 +- packages/cli/src/auth/auth.service.ts | 3 +-- .../__tests__/email.auth-handler.test.ts | 2 +- .../src/auth/handlers/email.auth-handler.ts | 2 +- .../cli/src/chat/chat-execution-manager.ts | 2 +- .../collaboration/collaboration.service.ts | 4 +--- packages/cli/src/controller.registry.ts | 3 +-- .../__tests__/ai.controller.test.ts | 10 +++++---- .../__tests__/auth.controller.test.ts | 4 +--- .../__tests__/change-email.controller.test.ts | 3 +-- .../__tests__/invitation.controller.test.ts | 2 +- .../__tests__/me.controller.test.ts | 3 +-- .../__tests__/owner.controller.test.ts | 2 +- .../__tests__/role.controller.test.ts | 2 +- .../security-settings.controller.test.ts | 2 +- .../__tests__/translation.controller.test.ts | 2 +- .../__tests__/users.controller.test.ts | 2 +- packages/cli/src/controllers/ai.controller.ts | 14 +++++++------ .../src/controllers/api-keys.controller.ts | 2 +- .../cli/src/controllers/auth.controller.ts | 5 +---- .../src/controllers/binary-data.controller.ts | 2 +- .../controllers/change-email.controller.ts | 4 +--- .../cli/src/controllers/folder.controller.ts | 4 +--- .../src/controllers/invitation.controller.ts | 2 +- packages/cli/src/controllers/me.controller.ts | 3 +-- .../cli/src/controllers/mfa.controller.ts | 3 +-- .../controllers/password-reset.controller.ts | 12 ++++++----- .../cli/src/controllers/project.controller.ts | 4 +--- .../cli/src/controllers/role.controller.ts | 2 +- .../security-settings.controller.ts | 2 +- .../src/controllers/translation.controller.ts | 3 +-- .../controllers/user-settings.controller.ts | 2 +- .../cli/src/controllers/users.controller.ts | 3 +-- .../workflow-statistics.controller.ts | 2 +- .../__tests__/credentials.controller.test.ts | 3 +-- .../__tests__/credentials.service.test.ts | 3 +-- .../src/credentials/credentials.controller.ts | 4 +--- .../src/credentials/credentials.service.ee.ts | 2 +- .../src/credentials/credentials.service.ts | 5 +---- .../credentials/instance-credential-broker.ts | 2 +- packages/cli/src/credentials/validation.ts | 2 +- .../__tests__/key-manager.service.test.ts | 2 +- .../cli/src/encryption/key-manager.service.ts | 2 +- .../variables/variables.controller.ee.ts | 3 +-- .../variables/variables.service.ee.ts | 3 +-- .../__tests__/http-error-classifier.test.ts | 10 ++++++--- .../__tests__/http-error-serializers.test.ts | 4 +--- .../errors/aborted-execution-retry.error.ts | 2 +- .../cache-errors/uncacheable-value.error.ts | 2 +- .../src/errors/credential-missing-id.error.ts | 2 +- .../src/errors/credential-not-found.error.ts | 2 +- ...redentials-overwrites-already-set.error.ts | 2 +- .../cli/src/errors/deduplication.error.ts | 2 +- .../src/errors/duplicate-execution.error.ts | 2 +- .../execution-already-resuming.error.ts | 2 +- .../src/errors/execution-not-found-error.ts | 2 +- .../src/errors/feature-not-licensed.error.ts | 2 +- .../cli/src/errors/folder-not-found.error.ts | 2 +- .../cli/src/errors/http-error-classifier.ts | 3 +-- .../errors/invalid-concurrency-limit.error.ts | 2 +- packages/cli/src/errors/invalid-role.error.ts | 2 +- .../cli/src/errors/max-stalled-count.error.ts | 2 +- .../errors/missing-execution-stop.error.ts | 2 +- .../cli/src/errors/non-json-body.error.ts | 2 +- .../postgres-live-rows-retrieval.error.ts | 2 +- .../src/errors/pre-execute-blocked.error.ts | 2 +- .../errors/queued-execution-retry.error.ts | 2 +- packages/cli/src/errors/redactable.error.ts | 2 +- .../ai-preference-scope-full.error.ts | 2 +- .../incompatible-nodes-api-version.error.ts | 2 +- .../response-errors/invalid-mfa-code.error.ts | 2 +- .../invalid-mfa-recovery-code-error.ts | 2 +- .../license-eula-required.error.ts | 2 +- .../transfer-credential.error.ts | 2 +- .../transfer-workflow.error.ts | 2 +- .../webhook-not-found.error.ts | 2 +- .../workflow-activation-bad-request.error.ts | 2 +- ...workflow-deactivation-bad-request.error.ts | 2 +- .../workflow-publish-blocked.error.ts | 2 +- .../workflow-publish-forbidden.error.ts | 2 +- .../errors/shared-workflow-not-found.error.ts | 2 +- .../errors/single-webhook-trigger.error.ts | 2 +- .../errors/unknown-execution-mode.error.ts | 2 +- ...orted-end-user-credential-trigger.error.ts | 2 +- .../variable-count-limit-reached.error.ts | 2 +- .../src/errors/variable-validation.error.ts | 2 +- .../webhook-response-too-large.error.ts | 2 +- ...orkflow-history-version-not-found.error.ts | 2 +- .../src/errors/workflow-missing-id.error.ts | 2 +- .../evaluation-collection.service.test.ts | 3 +-- .../evaluation-collections.controller.test.ts | 3 +-- .../evaluation-config.controller.test.ts | 3 +-- .../evaluation-dataset.service.test.ts | 4 +--- .../evaluation-test-run.service.test.ts | 2 +- .../__tests__/test-runs.controller.ee.test.ts | 3 +-- .../evaluation-collection.service.ts | 3 +-- .../evaluation-collections.controller.ee.ts | 3 +-- .../evaluation-config.controller.ts | 3 +-- .../evaluation-dataset.service.ts | 4 +--- .../evaluation-test-run.service.ts | 2 +- .../eval-insights.controller.test.ts | 2 +- .../__tests__/eval-insights.service.test.ts | 4 +--- .../insights/eval-insights.controller.ee.ts | 2 +- .../insights/eval-insights.service.ts | 4 +--- .../evaluation.ee/test-runs.controller.ee.ts | 3 +-- .../__tests__/execution.service.test.ts | 3 +-- .../__tests__/executions.controller.test.ts | 4 +--- .../cli/src/executions/execution-cursor.ts | 2 +- .../cli/src/executions/execution.service.ts | 5 +---- .../src/executions/executions.controller.ts | 4 +--- .../parse-range-query.middleware.ts | 2 +- packages/cli/src/executions/validation.ts | 2 +- packages/cli/src/generic-helpers.ts | 2 +- .../license/__tests__/license.service.test.ts | 2 +- .../cli/src/license/license.controller.ts | 2 +- packages/cli/src/license/license.service.ts | 2 +- .../middlewares/__tests__/body-parser.test.ts | 2 +- packages/cli/src/middlewares/body-parser.ts | 3 +-- ...agent-eval-case-generation.service.test.ts | 3 +-- .../agent-eval-rating.service.test.ts | 3 +-- .../agent-eval-runner.service.test.ts | 2 +- .../__tests__/agent-eval.service.test.ts | 3 +-- .../__tests__/agent-evals.controller.test.ts | 3 +-- .../agent-eval-case-generation.service.ts | 2 +- .../agent-evals/agent-eval-rating.service.ts | 3 +-- .../agent-evals/agent-eval-runner.service.ts | 4 +--- .../modules/agent-evals/agent-eval.service.ts | 3 +-- .../agent-evals/agent-evals-flag-gate.ts | 2 +- .../agent-evals-required-modules.ts | 2 +- .../agent-evals/agent-evals.controller.ts | 2 +- .../agent-chat-execution.service.test.ts | 2 +- .../__tests__/agent-chat.controller.test.ts | 2 +- .../agent-custom-tools.service.test.ts | 2 +- ...ent-integration-management.service.test.ts | 3 +-- .../agent-integrations.controller.test.ts | 2 +- .../agent-knowledge.controller.test.ts | 3 +-- .../agent-mcp-access.service.test.ts | 2 +- .../agent-publish.controller.test.ts | 2 +- .../__tests__/agent-publish.service.test.ts | 2 +- ...t-session-langsmith-export.service.test.ts | 2 +- .../__tests__/agent-task.service.test.ts | 3 +-- .../__tests__/agent-tasks.controller.test.ts | 2 +- .../agents-builder-tools.service.test.ts | 3 +-- .../agents-catalog.controller.test.ts | 2 +- .../agents/__tests__/agents.service.test.ts | 3 +-- .../instance-ai-agent-context.adapter.test.ts | 3 +-- ...stance-ai-builder-delegate.adapter.test.ts | 3 +-- .../agents/agent-chat-execution.service.ts | 2 +- .../modules/agents/agent-chat.controller.ts | 3 +-- .../modules/agents/agent-config.service.ts | 2 +- .../modules/agents/agent-execution.service.ts | 2 +- .../agent-integration-management.service.ts | 3 +-- .../agents/agent-integrations.controller.ts | 2 +- .../agents/agent-knowledge-mirror.service.ts | 3 +-- .../agents/agent-knowledge-retrieval.ts | 2 +- .../modules/agents/agent-knowledge-storage.ts | 2 +- .../agents/agent-knowledge.controller.ts | 3 +-- .../modules/agents/agent-knowledge.service.ts | 2 +- .../agents/agent-mcp-access.service.ts | 2 +- .../agents/agent-message-queue.service.ts | 4 +--- .../modules/agents/agent-publish.service.ts | 3 +-- .../agents/agent-sandbox.controller.ts | 2 +- .../agent-session-langsmith-export.service.ts | 4 +--- .../modules/agents/agent-skills.service.ts | 3 +-- .../src/modules/agents/agent-task.service.ts | 3 +-- .../agents/agent-threads.controller.ts | 2 +- .../modules/agents/agent-upload.middleware.ts | 2 +- .../agents/agents-catalog.controller.ts | 2 +- .../agents/agents-config.controller.ts | 2 +- .../src/modules/agents/agents.controller.ts | 2 +- .../cli/src/modules/agents/agents.service.ts | 2 +- .../builder/agents-builder-tools.service.ts | 3 +-- .../agents/builder/agents-builder.service.ts | 2 +- .../instance-ai-agent-context.adapter.ts | 3 +-- .../instance-ai-builder-delegate.adapter.ts | 2 +- .../discord-gateway-lifecycle.test.ts | 2 +- .../__tests__/discord-integration.test.ts | 3 +-- .../__tests__/slack-app-setup.service.test.ts | 2 +- .../__tests__/slack-integration.test.ts | 2 +- .../slack-manual-setup.service.test.ts | 2 +- .../__tests__/teams-integration.test.ts | 2 +- .../agents/integrations/credential-claim.ts | 2 +- .../leader-channel-relay.service.ts | 2 +- .../__tests__/telegram/integration.test.ts | 3 +-- .../platforms/discord-integration.ts | 3 +-- .../slack/slack-managed-setup.service.ts | 3 +-- .../slack/slack-manual-setup.service.ts | 4 +--- .../platforms/slack/slack-methods.service.ts | 2 +- .../platforms/slack/slack-setup.types.ts | 2 +- .../platforms/teams/teams-setup.service.ts | 3 +-- .../platforms/telegram-integration.ts | 2 +- .../__tests__/delegate-sub-agent-tool.test.ts | 2 +- .../sub-agents/delegate-sub-agent-tool.ts | 2 +- .../sub-agents/sub-agent-source-resolver.ts | 2 +- .../modules/agents/utils/agent-draft.utils.ts | 2 +- .../agents/utils/get-agent-or-throw.ts | 2 +- .../breaking-changes.controller.ts | 2 +- .../breaking-changes.migration.service.ts | 3 +-- .../migrations/node-migration.ts | 2 +- .../__tests__/chat-hub-agent.service.test.ts | 2 +- .../chat-hub-credentials.service.test.ts | 3 +-- .../chat-hub-settings.service.test.ts | 2 +- .../__tests__/chat-hub-tool.service.test.ts | 3 +-- .../chat-hub.attachment.service.test.ts | 2 +- .../__tests__/chat-hub.controller.test.ts | 2 +- .../chat-hub/chat-hub-agent.service.ts | 3 +-- .../chat-hub/chat-hub-credentials.service.ts | 3 +-- .../chat-hub/chat-hub-execution.service.ts | 3 +-- .../chat-hub/chat-hub-title.service.ts | 2 +- .../modules/chat-hub/chat-hub-tool.service.ts | 3 +-- .../chat-hub/chat-hub-workflow.service.ts | 2 +- .../chat-hub/chat-hub.attachment.service.ts | 3 +-- .../modules/chat-hub/chat-hub.controller.ts | 3 +-- .../src/modules/chat-hub/chat-hub.service.ts | 3 +-- .../chat-hub/chat-hub.settings.controller.ts | 2 +- .../chat-hub/chat-hub.settings.service.ts | 2 +- .../chat-hub/chat-session.repository.ts | 2 +- ...mmunity-packages.lifecycle.service.test.ts | 2 +- .../community-packages.lifecycle.service.ts | 4 +--- .../multer-upload-middleware.test.ts | 2 +- .../data-table/data-table-permissions.ts | 2 +- .../data-table/data-table-proxy.service.ts | 2 +- .../data-table-uploads.controller.ts | 2 +- .../data-table/data-table.controller.ts | 14 +++++++------ .../data-table-column-not-found.error.ts | 2 +- .../errors/data-table-not-found.error.ts | 2 +- .../branch-write-access-middleware.ts | 2 +- .../data-table/multer-upload-middleware.ts | 2 +- .../src/modules/data-table/utils/sql-utils.ts | 2 +- .../credential-resolvers.controller.test.ts | 4 +--- .../workflow-status.controller.test.ts | 3 +-- .../credential-resolvers.controller.ts | 4 +--- .../__tests__/n8n-identifier.test.ts | 2 +- .../dynamic-credentials.controller.ts | 3 +-- .../credential-resolver-workflow.service.ts | 3 +-- .../dynamic-credential-web.service.ts | 2 +- .../workflow-status.controller.ts | 3 +-- .../encryption-key.controller.test.ts | 2 +- .../encryption-key.controller.ts | 2 +- .../engine-credentials.controller.test.ts | 3 +-- .../engine-credentials.service.test.ts | 3 +-- .../engine-lifecycle-event.controller.test.ts | 2 +- .../engine-control-plane-auth.middleware.ts | 2 +- .../engine-credentials.controller.ts | 2 +- .../engine-v2/engine-credentials.service.ts | 3 +-- .../engine-lifecycle-event.controller.ts | 2 +- ...t-provider-access-check.service.ee.test.ts | 3 +-- ...roviders-connections.controller.ee.test.ts | 2 +- ...s-providers-connections.service.ee.test.ts | 2 +- .../external-secrets-manager.ee.ts | 2 +- ...external-secrets-settings.controller.ee.ts | 2 +- .../external-secrets.controller.ee.ts | 2 +- ...secret-provider-access-check.service.ee.ts | 3 +-- ...ets-providers-completions.controller.ee.ts | 2 +- ...ets-providers-connections.controller.ee.ts | 2 +- ...ecrets-providers-connections.service.ee.ts | 3 +-- ...secrets-providers-project.controller.ee.ts | 2 +- .../secrets-providers-types.controller.ee.ts | 3 +-- .../__tests__/favorites.controller.test.ts | 2 +- .../__tests__/favorites.service.test.ts | 3 +-- .../modules/favorites/favorites.controller.ts | 2 +- .../modules/favorites/favorites.service.ts | 3 +-- .../__tests__/insights.controller.test.ts | 3 +-- .../insights.service.integration.test.ts | 2 +- .../modules/insights/insights.controller.ts | 4 +--- .../src/modules/insights/insights.service.ts | 2 +- .../instance-ai-pending-agent.service.test.ts | 3 +-- .../instance-ai-settings.service.test.ts | 2 +- .../instance-ai-test.controller.test.ts | 2 +- .../instance-ai.adapter.service.test.ts | 14 +++++++------ .../__tests__/instance-ai.controller.test.ts | 5 +---- .../__tests__/instance-ai.service.test.ts | 2 +- .../instance-ai/__tests__/onboarding.test.ts | 2 +- .../__tests__/thread-restore.service.test.ts | 2 +- .../eval/thread-restore.service.ts | 2 +- .../instance-ai/instance-ai-memory.service.ts | 4 +--- .../instance-ai-pending-agent.service.ts | 3 +-- .../instance-ai-run-limit.error.ts | 2 +- .../instance-ai-settings.service.ts | 4 +--- .../instance-ai-test.controller.ts | 2 +- .../instance-ai.adapter.service.ts | 4 +--- .../instance-ai/instance-ai.controller.ts | 5 +---- .../instance-ai/instance-ai.service.ts | 3 +-- ...tance-ai-mcp-connection.controller.test.ts | 2 +- .../instance-ai-mcp-registry.service.test.ts | 4 +--- .../instance-ai-mcp-connection.controller.ts | 2 +- .../mcp/instance-ai-mcp-registry.service.ts | 4 +--- .../cli/src/modules/instance-ai/onboarding.ts | 2 +- .../storage/typeorm-agent-memory.ts | 3 +-- .../src/modules/ldap.ee/ldap.controller.ee.ts | 2 +- .../src/modules/ldap.ee/ldap.service.ee.ts | 2 +- .../log-streaming.controller.test.ts | 3 +-- .../destination-credentials-access.test.ts | 2 +- .../destination-credentials-access.ts | 2 +- .../log-streaming.controller.ts | 3 +-- .../mcp-registry-test.controller.test.ts | 2 +- .../mcp-registry-test.controller.ts | 2 +- .../mcp/__tests__/agent-tools.service.test.ts | 2 +- .../create-workflow-from-code.tool.test.ts | 2 +- .../__tests__/credential-validation.test.ts | 2 +- .../__tests__/mcp.settings.controller.test.ts | 2 +- .../__tests__/mcp.settings.service.test.ts | 2 +- .../save-user-preference.tool.test.ts | 4 +--- .../undo-user-preference.tool.test.ts | 3 +-- .../update-user-preference.tool.test.ts | 3 +-- .../__tests__/update-workflow.tool.test.ts | 2 +- .../mcp/mcp-server-middleware.service.ts | 2 +- .../modules/mcp/mcp.settings.controller.ts | 2 +- .../src/modules/mcp/mcp.settings.service.ts | 2 +- .../mcp/tools/agents/agent-tools.service.ts | 3 +-- .../mcp/tools/user-preference-tool.utils.ts | 2 +- .../__tests__/error-code.utils.test.ts | 2 +- .../create-workflow-from-code.tool.ts | 2 +- .../workflow-builder/credential-validation.ts | 2 +- .../import-data-tables.integration.test.ts | 2 +- .../import-folders.integration.test.ts | 4 +--- .../import-package-policy.integration.test.ts | 2 +- ...port-package-selection.integration.test.ts | 4 +--- .../import-package.integration.test.ts | 3 +-- .../import-projects.integration.test.ts | 4 +--- ...ort-workflow-with-tags.integration.test.ts | 3 +-- ...orkflow-with-variables.integration.test.ts | 3 +-- .../package-failure-classifier.test.ts | 12 ++++++----- .../utils/import-package-upload.test.ts | 2 +- .../__tests__/import-blocked.error.test.ts | 3 +-- .../engine/__tests__/import-gates.test.ts | 2 +- .../engine/import-blocked.error.ts | 3 +-- .../n8n-packages/engine/import-gates.ts | 2 +- .../n8n-packages/engine/n8n-package-parser.ts | 2 +- .../engine/project-package-importer.ts | 2 +- .../engine/workflow-package-importer.ts | 3 +-- .../__tests__/credential-importer.test.ts | 2 +- .../__tests__/credential-matcher.test.ts | 2 +- .../credential/credential-matcher-factory.ts | 2 +- .../data-table/data-table-importer.ts | 2 +- .../entities/project/project-importer.ts | 2 +- .../tag/__tests__/tag-importer.test.ts | 2 +- .../n8n-packages/entities/tag/tag-importer.ts | 2 +- .../entities/variable/variable-importer.ts | 2 +- .../__tests__/workflow-publisher.test.ts | 3 +-- .../__tests__/workflow-remover.test.ts | 2 +- .../entities/workflow/workflow-importer.ts | 2 +- .../entities/workflow/workflow-publisher.ts | 3 +-- .../entities/workflow/workflow-remover.ts | 2 +- .../directory-package-reader.test.ts | 2 +- .../io/__tests__/tar-package-reader.test.ts | 2 +- .../io/directory/directory-package-reader.ts | 2 +- .../n8n-packages/io/tar/tar-package-reader.ts | 2 +- .../n8n-packages/n8n-packages.service.ts | 3 +-- .../package-failure-classifier.ts | 12 ++++++----- .../utils/import-package-upload.ts | 2 +- .../__tests__/oauth-consent.service.test.ts | 2 +- .../oauth-server/oauth-clients.controller.ts | 3 +-- .../oauth-server/oauth-consent.controller.ts | 2 +- .../oauth-server/oauth-consent.service.ts | 2 +- .../oauth-server/oauth-server.service.ts | 2 +- .../src/modules/oauth-server/oauth.errors.ts | 2 +- ...motion-config.resolver.integration.test.ts | 2 +- .../promotion-connections.service.test.ts | 4 +--- .../promotion-providers.service.test.ts | 4 +--- .../__tests__/promotions-git.service.test.ts | 3 +-- .../promotions-package.integration.test.ts | 2 +- .../__tests__/promotions.service.test.ts | 4 +--- .../promotions.ee/promotion-change.service.ts | 3 +-- .../promotion-changes.controller.ts | 2 +- .../promotion-config.resolver.ts | 3 +-- .../promotions.ee/promotion-conflicts.ts | 2 +- .../promotion-connections.service.ts | 5 +---- .../promotion-providers.service.ts | 4 +--- .../promotions.ee/promotions-git.service.ts | 3 +-- .../promotions.ee/promotions.service.ts | 3 +-- .../promotions.ee/working-copy-updater.ts | 3 +-- .../__tests__/provisioning.service.ee.test.ts | 3 +-- .../role-mapping-rule.service.ee.test.ts | 4 +--- .../role-mapping-rule.validation.test.ts | 2 +- .../errors/sso-access-denied.error.ts | 2 +- .../provisioning.controller.ee.ts | 2 +- .../provisioning.service.ee.ts | 2 +- .../role-mapping-rule.service.ee.ts | 4 +--- .../role-mapping-rule.validation.ts | 2 +- .../__tests__/quick-connect.service.test.ts | 2 +- .../quick-connect/quick-connect.service.ts | 2 +- .../redaction-enforcement.service.test.ts | 2 +- .../execution-redaction.service.test.ts | 3 +-- .../executions/execution-redaction.service.ts | 3 +-- .../redaction-enforcement.service.ts | 2 +- .../source-control-scoped.service.test.ts | 2 +- .../source-control-status.service.test.ts | 2 +- .../source-control.controller.ee.test.ts | 2 +- .../__tests__/source-control.service.test.ts | 2 +- .../source-control-scoped.service.ts | 2 +- .../source-control-status.service.ee.ts | 2 +- .../source-control.controller.ee.ts | 3 +-- .../source-control.service.ee.ts | 3 +-- .../__tests__/oidc.controller.ee.test.ts | 2 +- .../__tests__/oidc.service.ee.test.ts | 3 +-- .../modules/sso-oidc/oidc.controller.ee.ts | 3 +-- .../src/modules/sso-oidc/oidc.service.ee.ts | 3 +-- .../__tests__/saml.service.ee.test.ts | 3 +-- .../cli/src/modules/sso-saml/saml-helpers.ts | 2 +- .../modules/sso-saml/saml.controller.ee.ts | 3 +-- .../src/modules/sso-saml/saml.service.ee.ts | 3 +-- .../token-exchange.controller.test.ts | 3 +-- .../controllers/token-exchange.controller.ts | 3 +-- .../__tests__/token-exchange.service.test.ts | 3 +-- .../token-exchange/token-exchange.errors.ts | 3 +-- .../type-availability-policy.service.test.ts | 3 +-- ...dential-type-policy-instance.controller.ts | 2 +- ...availability-policy-instance.controller.ts | 2 +- .../type-availability-policy.service.ts | 3 +-- ...eview-authorization.access.service.test.ts | 2 +- ...rkflow-review-inbox.detail.service.test.ts | 2 +- ...ow-review-request-decision.service.test.ts | 5 +---- ...flow-review-request-status.service.test.ts | 3 +-- ...-review-request-submission.service.test.ts | 5 +---- ...-submission.version-update.service.test.ts | 5 +---- .../workflow-review-activity.service.ts | 4 +--- .../workflow-review-authorization.service.ts | 2 +- .../workflow-review-feature-gate.service.ts | 2 +- .../workflow-review-inbox.service.ts | 2 +- ...orkflow-review-request-decision.service.ts | 4 +--- ...w-review-request-mutation-guard.service.ts | 4 +--- .../workflow-review-request-status.service.ts | 2 +- ...kflow-review-request-submission.service.ts | 4 +--- .../__tests__/execute-node.service.test.ts | 3 +-- .../node-execution/execute-node.service.ts | 3 +-- .../src/oauth/__tests__/oauth.service.test.ts | 4 +--- packages/cli/src/oauth/oauth.service.ts | 4 +--- packages/cli/src/oauth/validate-oauth-url.ts | 2 +- .../__tests__/check-access.test.ts | 2 +- .../cli/src/permissions.ee/check-access.ts | 2 +- .../public-api-controller.registry.test.ts | 2 +- .../public-api-controller.registry.ts | 2 +- .../src/public-api/public-api-media-type.ts | 2 +- .../public-api-error-response.test.ts | 4 +--- ...al-type-policies.public.controller.test.ts | 2 +- ...de-type-policies.public.controller.test.ts | 2 +- .../workflows.public.controller.test.ts | 2 +- ...dential-type-policies.public.controller.ts | 3 +-- .../credentials.public.controller.ts | 4 +--- .../data-tables.public.controller.ts | 5 +---- .../controllers/discover.public.controller.ts | 2 +- .../evaluations.public.controller.ts | 4 +--- .../executions.public.controller.ts | 4 +--- .../controllers/folders.public.controller.ts | 3 +-- .../controllers/insights.public.controller.ts | 3 +-- .../node-type-policies.public.controller.ts | 3 +-- .../controllers/projects.public.controller.ts | 3 +-- .../promotions.public.controller.ts | 10 +++++---- .../role-mapping-rules.public.controller.ts | 2 +- .../v1/controllers/roles.public.controller.ts | 2 +- .../security-policy.public.controller.ts | 2 +- .../source-control.public.controller.ts | 3 +-- .../controllers/sso-oidc.public.controller.ts | 2 +- .../v1/controllers/tags.public.controller.ts | 3 +-- .../v1/controllers/users.public.controller.ts | 2 +- .../workflows.public.controller.ts | 4 +--- .../handlers/credentials/credentials.utils.ts | 2 +- .../__tests__/data-tables.handler.test.ts | 3 +-- .../data-tables.columns.handler.ts | 3 +-- .../data-tables/data-tables.rows.handler.ts | 3 +-- .../v1/handlers/ldap/ldap.handler.ts | 3 +-- .../log-streaming/log-streaming.handler.ts | 4 +--- .../__tests__/n8n-packages.handler.test.ts | 5 +---- .../n8n-packages/n8n-packages.handler.ts | 3 +-- .../v1/handlers/otel/otel.handler.ts | 3 +-- .../v1/handlers/sso-saml/sso-saml.handler.ts | 3 +-- .../handlers/workflows/workflows.handler.ts | 2 +- .../shared/middlewares/global.middleware.ts | 2 +- .../__tests__/pagination.service.test.ts | 2 +- .../v1/shared/services/pagination.service.ts | 2 +- .../v1/shared/services/utils.service.ts | 3 +-- packages/cli/src/push/__tests__/index.test.ts | 2 +- packages/cli/src/push/index.ts | 3 +-- packages/cli/src/response-helper.ts | 2 +- .../active-workflows.service.test.ts | 2 +- .../__tests__/ai-gateway.service.test.ts | 2 +- .../__tests__/ai-preference-write.test.ts | 12 ++++++----- .../__tests__/ai-preference.service.test.ts | 3 +-- .../dynamic-node-parameters.service.test.ts | 3 +-- .../__tests__/email-change.service.test.ts | 3 +-- .../__tests__/ownership.service.test.ts | 2 +- .../__tests__/project.service.ee.test.ts | 3 +-- .../__tests__/public-api-key.service.test.ts | 3 +-- .../__tests__/role-authorization.test.ts | 2 +- .../role.service.assignments.test.ts | 2 +- .../role.service.customRoles.test.ts | 2 +- .../services/__tests__/user.service.test.ts | 4 +--- .../src/services/active-workflows.service.ts | 2 +- .../cli/src/services/ai-gateway.service.ts | 2 +- .../cli/src/services/ai-preference-write.ts | 4 +--- .../cli/src/services/ai-preference.service.ts | 5 +---- .../dynamic-node-parameters.service.ts | 3 +-- .../cli/src/services/email-change.service.ts | 3 +-- .../cli/src/services/ownership.service.ts | 2 +- .../cli/src/services/project.service.ee.ts | 5 +---- .../src/services/public-api-key.service.ts | 3 +-- .../cli/src/services/role-authorization.ts | 2 +- packages/cli/src/services/role.service.ts | 3 +-- .../src/services/sandbox-settings.service.ts | 2 +- .../services/session-cookie-auth.strategy.ts | 2 +- packages/cli/src/services/user.service.ts | 5 +---- packages/cli/src/sso.ee/sso-helpers.ts | 2 +- .../task-broker-auth.controller.test.ts | 3 +-- .../auth/task-broker-auth.controller.ts | 3 +-- .../email/user-management-mailer.ts | 2 +- .../webhooks/__tests__/test-webhooks.test.ts | 2 +- .../__tests__/waiting-webhooks.test.ts | 3 +-- .../__tests__/webhook-form-data.test.ts | 3 +-- .../__tests__/webhook-helpers.test.ts | 2 +- .../__tests__/webhook-request-handler.test.ts | 2 +- packages/cli/src/webhooks/live-webhooks.ts | 2 +- packages/cli/src/webhooks/test-webhooks.ts | 2 +- packages/cli/src/webhooks/waiting-forms.ts | 3 +-- packages/cli/src/webhooks/waiting-webhooks.ts | 3 +-- .../cli/src/webhooks/webhook-form-data.ts | 3 +-- packages/cli/src/webhooks/webhook-helpers.ts | 12 ++++++----- packages/cli/src/workflow-helpers.ts | 2 +- .../workflow-creation.service.test.ts | 4 +--- .../workflow-execution.service.test.ts | 2 +- .../__tests__/workflow.service.test.ts | 10 +++++---- .../__tests__/workflows.controller.test.ts | 3 +-- .../workflows/workflow-creation.service.ts | 5 +---- .../workflows/workflow-execution.service.ts | 2 +- .../workflow-history.controller.test.ts | 2 +- .../workflow-history.controller.ts | 2 +- .../cli/src/workflows/workflow.service.ee.ts | 3 +-- .../cli/src/workflows/workflow.service.ts | 5 +---- .../cli/src/workflows/workflows.controller.ts | 4 +--- .../instance-credentials.api.test.ts | 2 +- .../source-control-access-control.test.ts | 2 +- .../source-control.service.test.ts | 3 +-- .../cli/test/integration/mfa/mfa.api.test.ts | 2 +- .../middlewares/body-parser.test.ts | 3 +-- .../integration/oidc/oidc.service.ee.test.ts | 3 +-- .../integration/public-api/executions.test.ts | 2 +- .../integration/public-api/promotions.test.ts | 2 +- .../public-api/source-control.test.ts | 3 +-- .../test/integration/saml/saml.api.test.ts | 3 +-- .../integration/services/role.service.test.ts | 3 +-- packages/workflow/package.json | 2 -- .../errors/abstract/execution-base.error.ts | 2 +- .../src/errors/db-connection-timeout-error.ts | 2 +- packages/workflow/src/errors/index.ts | 16 +++++++++----- .../src/errors/node-operation.error.ts | 2 +- .../errors/node-version-not-found.error.ts | 2 +- .../src/errors/trigger-close.error.ts | 2 +- .../src/node-parameters/filter-parameter.ts | 2 +- packages/workflow/src/utils.ts | 2 +- .../errors/base/operational.error.test.ts | 8 +++++-- .../test/errors/base/unexpected.error.test.ts | 8 +++++-- .../test/errors/base/user.error.test.ts | 8 +++++-- pnpm-lock.yaml | 6 ------ 578 files changed, 694 insertions(+), 954 deletions(-) rename packages/{workflow/src/errors/base => @n8n/errors/src}/base.error.ts (89%) rename packages/{workflow/src/errors/base => @n8n/errors/src}/operational.error.ts (85%) rename packages/{cli/src/errors => @n8n/errors/src}/response-errors/auth.error.ts (70%) rename packages/{cli/src/errors => @n8n/errors/src}/response-errors/bad-request.error.ts (84%) rename packages/{cli/src/errors => @n8n/errors/src}/response-errors/conflict.error.ts (78%) rename packages/{cli/src/errors => @n8n/errors/src}/response-errors/content-too-large.error.ts (74%) rename packages/{cli/src/errors => @n8n/errors/src}/response-errors/forbidden.error.ts (71%) rename packages/{cli/src/errors => @n8n/errors/src}/response-errors/internal-server.error.ts (76%) rename packages/{cli/src/errors => @n8n/errors/src}/response-errors/locked.error.ts (72%) rename packages/{cli/src/errors => @n8n/errors/src}/response-errors/not-found.error.ts (86%) rename packages/{cli/src/errors => @n8n/errors/src}/response-errors/not-implemented.error.ts (73%) rename packages/{cli/src/errors => @n8n/errors/src}/response-errors/payment-required.error.ts (72%) rename packages/{cli/src/errors/response-errors/abstract => @n8n/errors/src/response-errors}/response.error.ts (96%) rename packages/{cli/src/errors => @n8n/errors/src}/response-errors/scope-forbidden.error.ts (81%) rename packages/{cli/src/errors => @n8n/errors/src}/response-errors/service-unavailable.error.ts (71%) rename packages/{cli/src/errors => @n8n/errors/src}/response-errors/too-many-requests.error.ts (74%) rename packages/{cli/src/errors => @n8n/errors/src}/response-errors/unauthenticated.error.ts (72%) rename packages/{cli/src/errors => @n8n/errors/src}/response-errors/unprocessable.error.ts (79%) rename packages/{cli/src/errors => @n8n/errors/src}/response-errors/unsupported-media-type.error.ts (74%) rename packages/{workflow/src/errors/base => @n8n/errors/src}/unexpected.error.ts (84%) rename packages/{workflow/src/errors/base => @n8n/errors/src}/user.error.ts (86%) diff --git a/packages/workflow/src/errors/base/base.error.ts b/packages/@n8n/errors/src/base.error.ts similarity index 89% rename from packages/workflow/src/errors/base/base.error.ts rename to packages/@n8n/errors/src/base.error.ts index f460688bf46..6fbe3593880 100644 --- a/packages/workflow/src/errors/base/base.error.ts +++ b/packages/@n8n/errors/src/base.error.ts @@ -1,7 +1,7 @@ import type { Event } from '@sentry/node'; import callsites from 'callsites'; -import type { ErrorTags, ErrorLevel, ReportingOptions } from '@n8n/errors'; +import type { ErrorLevel, ErrorTags, ReportingOptions } from './types'; export type BaseErrorOptions = { description?: string | undefined | null } & ErrorOptions & ReportingOptions; @@ -50,7 +50,7 @@ export abstract class BaseError extends Error { try { const filePath = callsites()[2].getFileName() ?? ''; - const match = /packages\/([^\/]+)\//.exec(filePath)?.[1]; + const match = /packages\/([^/]+)\//.exec(filePath)?.[1]; if (match) this.tags.packageName = match; } catch {} diff --git a/packages/@n8n/errors/src/index.ts b/packages/@n8n/errors/src/index.ts index 2e38b3d0bdc..bda29ba2e2e 100644 --- a/packages/@n8n/errors/src/index.ts +++ b/packages/@n8n/errors/src/index.ts @@ -1,3 +1,24 @@ export { ApplicationError } from './application.error'; +export { BaseError, type BaseErrorOptions } from './base.error'; export { IsolateError } from './isolate.error'; +export { OperationalError, type OperationalErrorOptions } from './operational.error'; +export { AuthError } from './response-errors/auth.error'; +export { BadRequestError } from './response-errors/bad-request.error'; +export { ConflictError } from './response-errors/conflict.error'; +export { ContentTooLargeError } from './response-errors/content-too-large.error'; +export { ForbiddenError } from './response-errors/forbidden.error'; +export { InternalServerError } from './response-errors/internal-server.error'; +export { LockedError } from './response-errors/locked.error'; +export { NotFoundError } from './response-errors/not-found.error'; +export { NotImplementedError } from './response-errors/not-implemented.error'; +export { PaymentRequiredError } from './response-errors/payment-required.error'; +export { ResponseError } from './response-errors/response.error'; +export { ScopeForbiddenError } from './response-errors/scope-forbidden.error'; +export { ServiceUnavailableError } from './response-errors/service-unavailable.error'; +export { TooManyRequestsError } from './response-errors/too-many-requests.error'; +export { UnauthenticatedError } from './response-errors/unauthenticated.error'; +export { UnprocessableRequestError } from './response-errors/unprocessable.error'; +export { UnsupportedMediaTypeError } from './response-errors/unsupported-media-type.error'; +export { UnexpectedError, type UnexpectedErrorOptions } from './unexpected.error'; +export { UserError, type UserErrorOptions } from './user.error'; export type * from './types'; diff --git a/packages/workflow/src/errors/base/operational.error.ts b/packages/@n8n/errors/src/operational.error.ts similarity index 85% rename from packages/workflow/src/errors/base/operational.error.ts rename to packages/@n8n/errors/src/operational.error.ts index c67848dca82..307155f43fb 100644 --- a/packages/workflow/src/errors/base/operational.error.ts +++ b/packages/@n8n/errors/src/operational.error.ts @@ -1,5 +1,4 @@ -import type { BaseErrorOptions } from './base.error'; -import { BaseError } from './base.error'; +import { BaseError, type BaseErrorOptions } from './base.error'; export type OperationalErrorOptions = Omit & { level?: 'info' | 'warning' | 'error'; diff --git a/packages/cli/src/errors/response-errors/auth.error.ts b/packages/@n8n/errors/src/response-errors/auth.error.ts similarity index 70% rename from packages/cli/src/errors/response-errors/auth.error.ts rename to packages/@n8n/errors/src/response-errors/auth.error.ts index cb3441211ba..4f3af6042f9 100644 --- a/packages/cli/src/errors/response-errors/auth.error.ts +++ b/packages/@n8n/errors/src/response-errors/auth.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from './response.error'; export class AuthError extends ResponseError { constructor(message: string, errorCode?: number) { diff --git a/packages/cli/src/errors/response-errors/bad-request.error.ts b/packages/@n8n/errors/src/response-errors/bad-request.error.ts similarity index 84% rename from packages/cli/src/errors/response-errors/bad-request.error.ts rename to packages/@n8n/errors/src/response-errors/bad-request.error.ts index cdb7973cca4..40b444a7193 100644 --- a/packages/cli/src/errors/response-errors/bad-request.error.ts +++ b/packages/@n8n/errors/src/response-errors/bad-request.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from './response.error'; export class BadRequestError extends ResponseError { constructor(message: string, errorCode?: number, hint?: string, cause?: unknown) { diff --git a/packages/cli/src/errors/response-errors/conflict.error.ts b/packages/@n8n/errors/src/response-errors/conflict.error.ts similarity index 78% rename from packages/cli/src/errors/response-errors/conflict.error.ts rename to packages/@n8n/errors/src/response-errors/conflict.error.ts index 3dca76a65c5..821010e19e1 100644 --- a/packages/cli/src/errors/response-errors/conflict.error.ts +++ b/packages/@n8n/errors/src/response-errors/conflict.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from './response.error'; export class ConflictError extends ResponseError { constructor( diff --git a/packages/cli/src/errors/response-errors/content-too-large.error.ts b/packages/@n8n/errors/src/response-errors/content-too-large.error.ts similarity index 74% rename from packages/cli/src/errors/response-errors/content-too-large.error.ts rename to packages/@n8n/errors/src/response-errors/content-too-large.error.ts index c56aed1129e..32a6b9a3818 100644 --- a/packages/cli/src/errors/response-errors/content-too-large.error.ts +++ b/packages/@n8n/errors/src/response-errors/content-too-large.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from './response.error'; export class ContentTooLargeError extends ResponseError { constructor(message: string, hint: string | undefined = undefined) { diff --git a/packages/cli/src/errors/response-errors/forbidden.error.ts b/packages/@n8n/errors/src/response-errors/forbidden.error.ts similarity index 71% rename from packages/cli/src/errors/response-errors/forbidden.error.ts rename to packages/@n8n/errors/src/response-errors/forbidden.error.ts index 4856f7cd472..8484b62dbbc 100644 --- a/packages/cli/src/errors/response-errors/forbidden.error.ts +++ b/packages/@n8n/errors/src/response-errors/forbidden.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from './response.error'; export class ForbiddenError extends ResponseError { constructor(message = 'Forbidden', hint?: string) { diff --git a/packages/cli/src/errors/response-errors/internal-server.error.ts b/packages/@n8n/errors/src/response-errors/internal-server.error.ts similarity index 76% rename from packages/cli/src/errors/response-errors/internal-server.error.ts rename to packages/@n8n/errors/src/response-errors/internal-server.error.ts index 2ebe5ca67c1..b702535610b 100644 --- a/packages/cli/src/errors/response-errors/internal-server.error.ts +++ b/packages/@n8n/errors/src/response-errors/internal-server.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from './response.error'; export class InternalServerError extends ResponseError { constructor(message?: string, cause?: unknown) { diff --git a/packages/cli/src/errors/response-errors/locked.error.ts b/packages/@n8n/errors/src/response-errors/locked.error.ts similarity index 72% rename from packages/cli/src/errors/response-errors/locked.error.ts rename to packages/@n8n/errors/src/response-errors/locked.error.ts index d8e43a7f6fa..3476f436f28 100644 --- a/packages/cli/src/errors/response-errors/locked.error.ts +++ b/packages/@n8n/errors/src/response-errors/locked.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from './response.error'; export class LockedError extends ResponseError { constructor(message: string, hint: string | undefined = undefined) { diff --git a/packages/cli/src/errors/response-errors/not-found.error.ts b/packages/@n8n/errors/src/response-errors/not-found.error.ts similarity index 86% rename from packages/cli/src/errors/response-errors/not-found.error.ts rename to packages/@n8n/errors/src/response-errors/not-found.error.ts index 6bbcea9581d..36bac6214c9 100644 --- a/packages/cli/src/errors/response-errors/not-found.error.ts +++ b/packages/@n8n/errors/src/response-errors/not-found.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from './response.error'; export class NotFoundError extends ResponseError { static isDefinedAndNotNull( diff --git a/packages/cli/src/errors/response-errors/not-implemented.error.ts b/packages/@n8n/errors/src/response-errors/not-implemented.error.ts similarity index 73% rename from packages/cli/src/errors/response-errors/not-implemented.error.ts rename to packages/@n8n/errors/src/response-errors/not-implemented.error.ts index f6c66391ffa..eacc88b5f6e 100644 --- a/packages/cli/src/errors/response-errors/not-implemented.error.ts +++ b/packages/@n8n/errors/src/response-errors/not-implemented.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from './response.error'; export class NotImplementedError extends ResponseError { constructor(message: string, hint: string | undefined = undefined) { diff --git a/packages/cli/src/errors/response-errors/payment-required.error.ts b/packages/@n8n/errors/src/response-errors/payment-required.error.ts similarity index 72% rename from packages/cli/src/errors/response-errors/payment-required.error.ts rename to packages/@n8n/errors/src/response-errors/payment-required.error.ts index ed9723520b2..e57d894bf2d 100644 --- a/packages/cli/src/errors/response-errors/payment-required.error.ts +++ b/packages/@n8n/errors/src/response-errors/payment-required.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from './response.error'; export class PaymentRequiredError extends ResponseError { constructor(message = 'Payment required', hint?: string) { diff --git a/packages/cli/src/errors/response-errors/abstract/response.error.ts b/packages/@n8n/errors/src/response-errors/response.error.ts similarity index 96% rename from packages/cli/src/errors/response-errors/abstract/response.error.ts rename to packages/@n8n/errors/src/response-errors/response.error.ts index 4a8f604ce6f..68fdb406c5b 100644 --- a/packages/cli/src/errors/response-errors/abstract/response.error.ts +++ b/packages/@n8n/errors/src/response-errors/response.error.ts @@ -1,4 +1,4 @@ -import { BaseError } from 'n8n-workflow'; +import { BaseError } from '../base.error'; /** * Special Error which allows to return also an error code and http status code diff --git a/packages/cli/src/errors/response-errors/scope-forbidden.error.ts b/packages/@n8n/errors/src/response-errors/scope-forbidden.error.ts similarity index 81% rename from packages/cli/src/errors/response-errors/scope-forbidden.error.ts rename to packages/@n8n/errors/src/response-errors/scope-forbidden.error.ts index fa12dc6019d..ca9d9dbb226 100644 --- a/packages/cli/src/errors/response-errors/scope-forbidden.error.ts +++ b/packages/@n8n/errors/src/response-errors/scope-forbidden.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from './response.error'; export class ScopeForbiddenError extends ResponseError { constructor( diff --git a/packages/cli/src/errors/response-errors/service-unavailable.error.ts b/packages/@n8n/errors/src/response-errors/service-unavailable.error.ts similarity index 71% rename from packages/cli/src/errors/response-errors/service-unavailable.error.ts rename to packages/@n8n/errors/src/response-errors/service-unavailable.error.ts index 7736e85f5f2..e5ff19d7f20 100644 --- a/packages/cli/src/errors/response-errors/service-unavailable.error.ts +++ b/packages/@n8n/errors/src/response-errors/service-unavailable.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from './response.error'; export class ServiceUnavailableError extends ResponseError { constructor(message: string, errorCode = 503) { diff --git a/packages/cli/src/errors/response-errors/too-many-requests.error.ts b/packages/@n8n/errors/src/response-errors/too-many-requests.error.ts similarity index 74% rename from packages/cli/src/errors/response-errors/too-many-requests.error.ts rename to packages/@n8n/errors/src/response-errors/too-many-requests.error.ts index d280dd399cb..6d66b0e5927 100644 --- a/packages/cli/src/errors/response-errors/too-many-requests.error.ts +++ b/packages/@n8n/errors/src/response-errors/too-many-requests.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from './response.error'; export class TooManyRequestsError extends ResponseError { constructor(message: string, hint: string | undefined = undefined) { diff --git a/packages/cli/src/errors/response-errors/unauthenticated.error.ts b/packages/@n8n/errors/src/response-errors/unauthenticated.error.ts similarity index 72% rename from packages/cli/src/errors/response-errors/unauthenticated.error.ts rename to packages/@n8n/errors/src/response-errors/unauthenticated.error.ts index 7f1409da7fa..fa6b921e37c 100644 --- a/packages/cli/src/errors/response-errors/unauthenticated.error.ts +++ b/packages/@n8n/errors/src/response-errors/unauthenticated.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from './response.error'; export class UnauthenticatedError extends ResponseError { constructor(message = 'Unauthenticated', hint?: string) { diff --git a/packages/cli/src/errors/response-errors/unprocessable.error.ts b/packages/@n8n/errors/src/response-errors/unprocessable.error.ts similarity index 79% rename from packages/cli/src/errors/response-errors/unprocessable.error.ts rename to packages/@n8n/errors/src/response-errors/unprocessable.error.ts index c5e96d8893a..f8c07c0422d 100644 --- a/packages/cli/src/errors/response-errors/unprocessable.error.ts +++ b/packages/@n8n/errors/src/response-errors/unprocessable.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from './response.error'; export class UnprocessableRequestError extends ResponseError { constructor( diff --git a/packages/cli/src/errors/response-errors/unsupported-media-type.error.ts b/packages/@n8n/errors/src/response-errors/unsupported-media-type.error.ts similarity index 74% rename from packages/cli/src/errors/response-errors/unsupported-media-type.error.ts rename to packages/@n8n/errors/src/response-errors/unsupported-media-type.error.ts index cd20a507997..7c283f229b4 100644 --- a/packages/cli/src/errors/response-errors/unsupported-media-type.error.ts +++ b/packages/@n8n/errors/src/response-errors/unsupported-media-type.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from './response.error'; export class UnsupportedMediaTypeError extends ResponseError { constructor(message: string, hint: string | undefined = undefined) { diff --git a/packages/workflow/src/errors/base/unexpected.error.ts b/packages/@n8n/errors/src/unexpected.error.ts similarity index 84% rename from packages/workflow/src/errors/base/unexpected.error.ts rename to packages/@n8n/errors/src/unexpected.error.ts index a2045690524..8f88ba14896 100644 --- a/packages/workflow/src/errors/base/unexpected.error.ts +++ b/packages/@n8n/errors/src/unexpected.error.ts @@ -1,5 +1,4 @@ -import type { BaseErrorOptions } from './base.error'; -import { BaseError } from './base.error'; +import { BaseError, type BaseErrorOptions } from './base.error'; export type UnexpectedErrorOptions = Omit & { level?: 'error' | 'fatal'; diff --git a/packages/workflow/src/errors/base/user.error.ts b/packages/@n8n/errors/src/user.error.ts similarity index 86% rename from packages/workflow/src/errors/base/user.error.ts rename to packages/@n8n/errors/src/user.error.ts index 725f41694c5..cd2ea48aa0d 100644 --- a/packages/workflow/src/errors/base/user.error.ts +++ b/packages/@n8n/errors/src/user.error.ts @@ -1,5 +1,4 @@ -import type { BaseErrorOptions } from './base.error'; -import { BaseError } from './base.error'; +import { BaseError, type BaseErrorOptions } from './base.error'; export type UserErrorOptions = Omit & { level?: 'info' | 'warning'; diff --git a/packages/cli/src/__tests__/controller.registry.test.ts b/packages/cli/src/__tests__/controller.registry.test.ts index 544df733ceb..d4cc9d23660 100644 --- a/packages/cli/src/__tests__/controller.registry.test.ts +++ b/packages/cli/src/__tests__/controller.registry.test.ts @@ -28,7 +28,7 @@ import { z } from 'zod'; import type { AuthService } from '@/auth/auth.service'; import { ControllerRegistry } from '@/controller.registry'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { License } from '@/license'; import type { LastActiveAtService } from '@/services/last-active-at.service'; import { RateLimitService } from '@/services/rate-limit.service'; diff --git a/packages/cli/src/__tests__/response-helper.test.ts b/packages/cli/src/__tests__/response-helper.test.ts index dac4f9d66b2..4954207f0bd 100644 --- a/packages/cli/src/__tests__/response-helper.test.ts +++ b/packages/cli/src/__tests__/response-helper.test.ts @@ -4,11 +4,8 @@ import { ErrorReporter } from 'n8n-core'; import { UserError } from 'n8n-workflow'; import { mock } from 'vitest-mock-extended'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; +import { ConflictError, ForbiddenError, InternalServerError, NotFoundError } from '@n8n/errors'; import { LicenseEulaRequiredError } from '@/errors/response-errors/license-eula-required.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { PolicyViolationError } from '@/policy/policy-violation.error'; import { reportError, sendErrorResponse } from '@/response-helper'; diff --git a/packages/cli/src/__tests__/workflow-helpers.test.ts b/packages/cli/src/__tests__/workflow-helpers.test.ts index 18bf1d5222a..507ddde05f5 100644 --- a/packages/cli/src/__tests__/workflow-helpers.test.ts +++ b/packages/cli/src/__tests__/workflow-helpers.test.ts @@ -32,7 +32,7 @@ import { sanitizeNodeGroupDescriptions, WorkflowStructureBadRequestError, } from '@/workflow-helpers'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { mock } from 'vitest-mock-extended'; describe('workflow-helpers', () => { diff --git a/packages/cli/src/abstract-server.ts b/packages/cli/src/abstract-server.ts index c9cebd322b5..39c0fa02669 100644 --- a/packages/cli/src/abstract-server.ts +++ b/packages/cli/src/abstract-server.ts @@ -12,7 +12,7 @@ import { SLACK_HITL_WEBHOOK_SUFFIX, TELEGRAM_HITL_WEBHOOK_SUFFIX } from 'n8n-cor import config from '@/config'; import { N8N_VERSION, TEMPLATES_DIR } from '@/constants'; -import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error'; +import { ServiceUnavailableError } from '@n8n/errors'; import { ExternalHooks } from '@/external-hooks'; import { bodyParser, corsMiddleware, rawBodyReader } from '@/middlewares'; import { sendErrorResponse } from '@/response-helper'; diff --git a/packages/cli/src/auth/auth.service.ts b/packages/cli/src/auth/auth.service.ts index 97131070be6..c33dae7f30d 100644 --- a/packages/cli/src/auth/auth.service.ts +++ b/packages/cli/src/auth/auth.service.ts @@ -12,8 +12,7 @@ import escapeRegExp from 'lodash/escapeRegExp'; import type { StringValue as TimeUnitValue } from 'ms'; import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from '@/constants'; -import { AuthError } from '@/errors/response-errors/auth.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { AuthError, ForbiddenError } from '@n8n/errors'; import { License } from '@/license'; import { MfaService } from '@/mfa/mfa.service'; import { JwtService } from '@/services/jwt.service'; diff --git a/packages/cli/src/auth/handlers/__tests__/email.auth-handler.test.ts b/packages/cli/src/auth/handlers/__tests__/email.auth-handler.test.ts index 3f19f3d02e0..d0e3a224a22 100644 --- a/packages/cli/src/auth/handlers/__tests__/email.auth-handler.test.ts +++ b/packages/cli/src/auth/handlers/__tests__/email.auth-handler.test.ts @@ -2,7 +2,7 @@ import type { GlobalConfig } from '@n8n/config'; import type { AuthIdentity, User, UserRepository } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import { AuthError } from '@/errors/response-errors/auth.error'; +import { AuthError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { PasswordUtility } from '@/services/password.utility'; diff --git a/packages/cli/src/auth/handlers/email.auth-handler.ts b/packages/cli/src/auth/handlers/email.auth-handler.ts index bd597d44848..10a41e86585 100644 --- a/packages/cli/src/auth/handlers/email.auth-handler.ts +++ b/packages/cli/src/auth/handlers/email.auth-handler.ts @@ -4,7 +4,7 @@ import type { IPasswordAuthHandler } from '@n8n/decorators'; import { AuthHandler } from '@n8n/decorators'; import { Constructable } from '@n8n/di'; -import { AuthError } from '@/errors/response-errors/auth.error'; +import { AuthError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { PasswordUtility } from '@/services/password.utility'; diff --git a/packages/cli/src/chat/chat-execution-manager.ts b/packages/cli/src/chat/chat-execution-manager.ts index 7bbf1ffd256..d62551c5338 100644 --- a/packages/cli/src/chat/chat-execution-manager.ts +++ b/packages/cli/src/chat/chat-execution-manager.ts @@ -17,7 +17,7 @@ import { isHitlToolType, } from 'n8n-workflow'; -import { NotFoundError } from '../errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { ExecutionPersistence } from '../executions/execution-persistence'; import * as WorkflowExecuteAdditionalData from '../workflow-execute-additional-data'; import { preserveInputOverride } from '../workflow-helpers'; diff --git a/packages/cli/src/collaboration/collaboration.service.ts b/packages/cli/src/collaboration/collaboration.service.ts index c906d0e5d0e..13781dedf46 100644 --- a/packages/cli/src/collaboration/collaboration.service.ts +++ b/packages/cli/src/collaboration/collaboration.service.ts @@ -22,9 +22,7 @@ import type { } from './collaboration.message'; import { CollaborationState, type WriteLock } from '@/collaboration/collaboration.state'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { LockedError } from '@/errors/response-errors/locked.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ConflictError, LockedError, NotFoundError } from '@n8n/errors'; import { AgentRepository } from '@/modules/agents/repositories/agent.repository'; import { Push } from '@/push'; import type { OnPushMessage } from '@/push/types'; diff --git a/packages/cli/src/controller.registry.ts b/packages/cli/src/controller.registry.ts index 1b60b7a96cd..bb332e0f462 100644 --- a/packages/cli/src/controller.registry.ts +++ b/packages/cli/src/controller.registry.ts @@ -12,6 +12,7 @@ import type { KeyedRateLimiterConfig, } from '@n8n/decorators'; import { Container, Service } from '@n8n/di'; +import { NotFoundError, UnauthenticatedError } from '@n8n/errors'; import { ensureError } from '@n8n/utils/errors/ensure-error'; import { Router } from 'express'; import type { Application, Request, Response, RequestHandler } from 'express'; @@ -20,13 +21,11 @@ import assert from 'node:assert'; import { AuthService } from '@/auth/auth.service'; import { RESPONSE_ERROR_MESSAGES } from '@/constants'; -import { UnauthenticatedError } from '@/errors/response-errors/unauthenticated.error'; import { License } from '@/license'; import { userHasScopes } from '@/permissions.ee/check-access'; import { reportError, send, sendErrorResponse } from '@/response-helper'; import { AbstractServer } from './abstract-server'; -import { NotFoundError } from './errors/response-errors/not-found.error'; import { CorsService } from './services/cors-service'; import { LastActiveAtService } from './services/last-active-at.service'; import { RateLimitService } from './services/rate-limit.service'; diff --git a/packages/cli/src/controllers/__tests__/ai.controller.test.ts b/packages/cli/src/controllers/__tests__/ai.controller.test.ts index f43762a5a3a..bf4ef325b6c 100644 --- a/packages/cli/src/controllers/__tests__/ai.controller.test.ts +++ b/packages/cli/src/controllers/__tests__/ai.controller.test.ts @@ -9,10 +9,12 @@ import type { AuthenticatedRequest } from '@n8n/db'; import { APIResponseError, NetworkError, type AiAssistantSDK } from '@n8n_io/ai-assistant-sdk'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error'; +import { + BadRequestError, + InternalServerError, + NotFoundError, + ServiceUnavailableError, +} from '@n8n/errors'; import type { AiGatewayService } from '@/services/ai-gateway.service'; import type { AiUsageService } from '@/services/ai-usage.service'; import type { WorkflowBuilderService } from '@/services/ai-workflow-builder.service'; diff --git a/packages/cli/src/controllers/__tests__/auth.controller.test.ts b/packages/cli/src/controllers/__tests__/auth.controller.test.ts index 9548d0ba967..e8b26bdb1e5 100644 --- a/packages/cli/src/controllers/__tests__/auth.controller.test.ts +++ b/packages/cli/src/controllers/__tests__/auth.controller.test.ts @@ -14,9 +14,7 @@ import { AuthService } from '@/auth/auth.service'; import type { EmailAuthHandler } from '@/auth/handlers/email.auth-handler'; import config from '@/config'; import { RESPONSE_ERROR_MESSAGES } from '@/constants'; -import { AuthError } from '@/errors/response-errors/auth.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { AuthError, BadRequestError, ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { License } from '@/license'; import { MfaService } from '@/mfa/mfa.service'; diff --git a/packages/cli/src/controllers/__tests__/change-email.controller.test.ts b/packages/cli/src/controllers/__tests__/change-email.controller.test.ts index 069068cae3c..404f54373a0 100644 --- a/packages/cli/src/controllers/__tests__/change-email.controller.test.ts +++ b/packages/cli/src/controllers/__tests__/change-email.controller.test.ts @@ -8,8 +8,7 @@ import { mock } from 'vitest-mock-extended'; import { AuthService } from '@/auth/auth.service'; import { ChangeEmailController } from '@/controllers/change-email.controller'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { ExternalHooks } from '@/external-hooks'; import type { AuthlessRequest } from '@/requests'; diff --git a/packages/cli/src/controllers/__tests__/invitation.controller.test.ts b/packages/cli/src/controllers/__tests__/invitation.controller.test.ts index 9b6aeb7847a..6f266e7ba62 100644 --- a/packages/cli/src/controllers/__tests__/invitation.controller.test.ts +++ b/packages/cli/src/controllers/__tests__/invitation.controller.test.ts @@ -9,7 +9,7 @@ import { v4 as uuidv4 } from 'uuid'; import { mock } from 'vitest-mock-extended'; import { AuthService } from '@/auth/auth.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { ExternalHooks } from '@/external-hooks'; import { PostHogClient } from '@/posthog'; diff --git a/packages/cli/src/controllers/__tests__/me.controller.test.ts b/packages/cli/src/controllers/__tests__/me.controller.test.ts index fb4e7f38a32..5648d1fe7a1 100644 --- a/packages/cli/src/controllers/__tests__/me.controller.test.ts +++ b/packages/cli/src/controllers/__tests__/me.controller.test.ts @@ -11,8 +11,7 @@ import { mock, anyObject } from 'vitest-mock-extended'; import { AUTH_COOKIE_NAME } from '@/constants'; import { MeController } from '@/controllers/me.controller'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { InvalidMfaCodeError } from '@/errors/response-errors/invalid-mfa-code.error'; import { EventService } from '@/events/event.service'; import { ExternalHooks } from '@/external-hooks'; diff --git a/packages/cli/src/controllers/__tests__/owner.controller.test.ts b/packages/cli/src/controllers/__tests__/owner.controller.test.ts index e1f3a6f49ea..309499b4e60 100644 --- a/packages/cli/src/controllers/__tests__/owner.controller.test.ts +++ b/packages/cli/src/controllers/__tests__/owner.controller.test.ts @@ -3,7 +3,7 @@ import { mock } from 'vitest-mock-extended'; import type { AuthService } from '@/auth/auth.service'; import { OwnerController } from '@/controllers/owner.controller'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { PostHogClient } from '@/posthog'; import type { BannerService } from '@/services/banner.service'; import type { OwnershipService } from '@/services/ownership.service'; diff --git a/packages/cli/src/controllers/__tests__/role.controller.test.ts b/packages/cli/src/controllers/__tests__/role.controller.test.ts index 86276712a01..b8e072114e4 100644 --- a/packages/cli/src/controllers/__tests__/role.controller.test.ts +++ b/packages/cli/src/controllers/__tests__/role.controller.test.ts @@ -2,7 +2,7 @@ import type { AuthenticatedRequest, Project } from '@n8n/db'; import type { Role } from '@n8n/permissions'; import { mock } from 'vitest-mock-extended'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { ProjectService } from '@/services/project.service.ee'; import type { RoleService } from '@/services/role.service'; diff --git a/packages/cli/src/controllers/__tests__/security-settings.controller.test.ts b/packages/cli/src/controllers/__tests__/security-settings.controller.test.ts index fdafa150bb3..e63f30d8fb1 100644 --- a/packages/cli/src/controllers/__tests__/security-settings.controller.test.ts +++ b/packages/cli/src/controllers/__tests__/security-settings.controller.test.ts @@ -4,7 +4,7 @@ import type { InstanceSettingsLoaderConfig } from '@n8n/config'; import type { AuthenticatedRequest } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { SecuritySettingsService } from '@/services/security-settings.service'; import type { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; diff --git a/packages/cli/src/controllers/__tests__/translation.controller.test.ts b/packages/cli/src/controllers/__tests__/translation.controller.test.ts index a5ba1b2165b..2c1725b040b 100644 --- a/packages/cli/src/controllers/__tests__/translation.controller.test.ts +++ b/packages/cli/src/controllers/__tests__/translation.controller.test.ts @@ -4,7 +4,7 @@ import { mock } from 'vitest-mock-extended'; import type { TranslationRequest } from '@/controllers/translation.controller'; import { TranslationController } from '@/controllers/translation.controller'; import type { CredentialTypes } from '@/credential-types'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; // The controller loads the translation file via `require()`. // Vitest cannot mock a path that doesn't resolve to a real module, so write a diff --git a/packages/cli/src/controllers/__tests__/users.controller.test.ts b/packages/cli/src/controllers/__tests__/users.controller.test.ts index e772f32bac0..4c53da2a431 100644 --- a/packages/cli/src/controllers/__tests__/users.controller.test.ts +++ b/packages/cli/src/controllers/__tests__/users.controller.test.ts @@ -2,7 +2,7 @@ import type { AuthenticatedRequest, User, UserRepository } from '@n8n/db'; import type { Response } from 'express'; import { mock } from 'vitest-mock-extended'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { UserRequest } from '@/requests'; import type { JwtService } from '@/services/jwt.service'; import type { UrlService } from '@n8n/backend-services'; diff --git a/packages/cli/src/controllers/ai.controller.ts b/packages/cli/src/controllers/ai.controller.ts index 5d7158a83ab..16e48b26008 100644 --- a/packages/cli/src/controllers/ai.controller.ts +++ b/packages/cli/src/controllers/ai.controller.ts @@ -23,12 +23,14 @@ import { strict as assert } from 'node:assert'; import { WritableStream } from 'node:stream/web'; import { STREAM_SEPARATOR } from '@/constants'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ContentTooLargeError } from '@/errors/response-errors/content-too-large.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error'; -import { TooManyRequestsError } from '@/errors/response-errors/too-many-requests.error'; +import { + BadRequestError, + ContentTooLargeError, + InternalServerError, + NotFoundError, + ServiceUnavailableError, + TooManyRequestsError, +} from '@n8n/errors'; import { AiGatewayService } from '@/services/ai-gateway.service'; import { AiUsageService } from '@/services/ai-usage.service'; import { WorkflowBuilderService } from '@/services/ai-workflow-builder.service'; diff --git a/packages/cli/src/controllers/api-keys.controller.ts b/packages/cli/src/controllers/api-keys.controller.ts index f788cf96fd2..b236c069d24 100644 --- a/packages/cli/src/controllers/api-keys.controller.ts +++ b/packages/cli/src/controllers/api-keys.controller.ts @@ -18,7 +18,7 @@ import { import { getApiKeyScopesForRole } from '@n8n/permissions'; import type { RequestHandler } from 'express'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { isApiKeyAuthEnabled } from '@/public-api'; import { PublicApiKeyService } from '@/services/public-api-key.service'; diff --git a/packages/cli/src/controllers/auth.controller.ts b/packages/cli/src/controllers/auth.controller.ts index 0d7208b0372..21991e14677 100644 --- a/packages/cli/src/controllers/auth.controller.ts +++ b/packages/cli/src/controllers/auth.controller.ts @@ -21,10 +21,7 @@ import { Response } from 'express'; import { AuthHandlerRegistry } from '@/auth/auth-handler.registry'; import { AuthService } from '@/auth/auth.service'; import { RESPONSE_ERROR_MESSAGES } from '@/constants'; -import { AuthError } from '@/errors/response-errors/auth.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; +import { AuthError, BadRequestError, ForbiddenError, InternalServerError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { License } from '@/license'; import { MfaService } from '@/mfa/mfa.service'; diff --git a/packages/cli/src/controllers/binary-data.controller.ts b/packages/cli/src/controllers/binary-data.controller.ts index d21f3b96277..e4390d9f648 100644 --- a/packages/cli/src/controllers/binary-data.controller.ts +++ b/packages/cli/src/controllers/binary-data.controller.ts @@ -11,7 +11,7 @@ import { } from 'n8n-core'; import { BinaryDataAccessService } from '@/binary-data/binary-data-access.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; @RestController('/binary-data') export class BinaryDataController { diff --git a/packages/cli/src/controllers/change-email.controller.ts b/packages/cli/src/controllers/change-email.controller.ts index 277ffe0e0d2..fef9f3167e3 100644 --- a/packages/cli/src/controllers/change-email.controller.ts +++ b/packages/cli/src/controllers/change-email.controller.ts @@ -17,9 +17,7 @@ import { import { Response } from 'express'; import { AuthService } from '@/auth/auth.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, InternalServerError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { ExternalHooks } from '@/external-hooks'; import { AuthlessRequest } from '@/requests'; diff --git a/packages/cli/src/controllers/folder.controller.ts b/packages/cli/src/controllers/folder.controller.ts index 18ae5e75365..86b87e3e6a0 100644 --- a/packages/cli/src/controllers/folder.controller.ts +++ b/packages/cli/src/controllers/folder.controller.ts @@ -24,9 +24,7 @@ import { NextFunction, Response } from 'express'; import { UserError } from 'n8n-workflow'; import { FolderNotFoundError } from '@/errors/folder-not-found.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, InternalServerError, NotFoundError } from '@n8n/errors'; import { FolderService } from '@/services/folder.service'; import { ProjectService } from '@/services/project.service.ee'; import { EnterpriseWorkflowService } from '@/workflows/workflow.service.ee'; diff --git a/packages/cli/src/controllers/invitation.controller.ts b/packages/cli/src/controllers/invitation.controller.ts index 88c27fa269a..2c23281cdf3 100644 --- a/packages/cli/src/controllers/invitation.controller.ts +++ b/packages/cli/src/controllers/invitation.controller.ts @@ -7,7 +7,7 @@ import { Post, GlobalScope, RestController, Body } from '@n8n/decorators'; import { Response } from 'express'; import { AuthService } from '@/auth/auth.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { ExternalHooks } from '@/external-hooks'; import { PostHogClient } from '@/posthog'; diff --git a/packages/cli/src/controllers/me.controller.ts b/packages/cli/src/controllers/me.controller.ts index 46cd1bc5c83..f08cd94c173 100644 --- a/packages/cli/src/controllers/me.controller.ts +++ b/packages/cli/src/controllers/me.controller.ts @@ -13,8 +13,7 @@ import { plainToInstance } from 'class-transformer'; import { Response } from 'express'; import { AuthService } from '@/auth/auth.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { InvalidMfaCodeError } from '@/errors/response-errors/invalid-mfa-code.error'; import { EventService } from '@/events/event.service'; import { ExternalHooks } from '@/external-hooks'; diff --git a/packages/cli/src/controllers/mfa.controller.ts b/packages/cli/src/controllers/mfa.controller.ts index b58e57d7fe3..b26b78e1362 100644 --- a/packages/cli/src/controllers/mfa.controller.ts +++ b/packages/cli/src/controllers/mfa.controller.ts @@ -10,8 +10,7 @@ import { import { Response } from 'express'; import { AuthService } from '@/auth/auth.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { ExternalHooks } from '@/external-hooks'; import { MfaService } from '@/mfa/mfa.service'; diff --git a/packages/cli/src/controllers/password-reset.controller.ts b/packages/cli/src/controllers/password-reset.controller.ts index 67cf5d3576e..729828d9378 100644 --- a/packages/cli/src/controllers/password-reset.controller.ts +++ b/packages/cli/src/controllers/password-reset.controller.ts @@ -20,11 +20,13 @@ import { ErrorReporter } from 'n8n-core'; import { AuthService } from '@/auth/auth.service'; import { RESPONSE_ERROR_MESSAGES } from '@/constants'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { + BadRequestError, + ForbiddenError, + InternalServerError, + NotFoundError, + UnprocessableRequestError, +} from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { ExternalHooks } from '@/external-hooks'; import { License } from '@/license'; diff --git a/packages/cli/src/controllers/project.controller.ts b/packages/cli/src/controllers/project.controller.ts index 46ee797a391..6baaf93a3bc 100644 --- a/packages/cli/src/controllers/project.controller.ts +++ b/packages/cli/src/controllers/project.controller.ts @@ -23,9 +23,7 @@ import { import { combineScopes, getAuthPrincipalScopes } from '@n8n/permissions'; import { Response } from 'express'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ForbiddenError, NotFoundError } from '@n8n/errors'; import { ProvisioningService } from '@/modules/provisioning.ee/provisioning.service.ee'; import type { ProjectRequest } from '@/requests'; import { diff --git a/packages/cli/src/controllers/role.controller.ts b/packages/cli/src/controllers/role.controller.ts index 79b5f373baa..723b3a06112 100644 --- a/packages/cli/src/controllers/role.controller.ts +++ b/packages/cli/src/controllers/role.controller.ts @@ -29,7 +29,7 @@ import { } from '@n8n/decorators'; import { Role as RoleDTO } from '@n8n/permissions'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { ProjectService } from '@/services/project.service.ee'; import { assertCanManageRoleType, canReassignUsers } from '@/services/role-authorization'; diff --git a/packages/cli/src/controllers/security-settings.controller.ts b/packages/cli/src/controllers/security-settings.controller.ts index ef8ad841d08..2aab7a1089d 100644 --- a/packages/cli/src/controllers/security-settings.controller.ts +++ b/packages/cli/src/controllers/security-settings.controller.ts @@ -5,7 +5,7 @@ import { type AuthenticatedRequest } from '@n8n/db'; import { Body, Get, GlobalScope, Licensed, Post, RestController } from '@n8n/decorators'; import type { Response } from 'express'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { SecuritySettingsService } from '@/services/security-settings.service'; import { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; diff --git a/packages/cli/src/controllers/translation.controller.ts b/packages/cli/src/controllers/translation.controller.ts index 38d7d3688f2..614bc78f637 100644 --- a/packages/cli/src/controllers/translation.controller.ts +++ b/packages/cli/src/controllers/translation.controller.ts @@ -6,8 +6,7 @@ import { access } from 'fs/promises'; import { NODES_BASE_DIR } from '@/constants'; import { CredentialTypes } from '@/credential-types'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; +import { BadRequestError, InternalServerError } from '@n8n/errors'; export const CREDENTIAL_TRANSLATIONS_DIR = 'n8n-nodes-base/dist/credentials/translations'; export const NODE_HEADERS_PATH = safeJoinPath(NODES_BASE_DIR, 'dist/nodes/headers'); diff --git a/packages/cli/src/controllers/user-settings.controller.ts b/packages/cli/src/controllers/user-settings.controller.ts index 7b1505fe0b2..0172da43084 100644 --- a/packages/cli/src/controllers/user-settings.controller.ts +++ b/packages/cli/src/controllers/user-settings.controller.ts @@ -1,7 +1,7 @@ import { Patch, RestController } from '@n8n/decorators'; import type { NpsSurveyState } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { NpsSurveyRequest } from '@/requests'; import { UserService } from '@/services/user.service'; diff --git a/packages/cli/src/controllers/users.controller.ts b/packages/cli/src/controllers/users.controller.ts index 7e458ac4510..017a3737a9e 100644 --- a/packages/cli/src/controllers/users.controller.ts +++ b/packages/cli/src/controllers/users.controller.ts @@ -30,8 +30,7 @@ import { hasGlobalScope } from '@n8n/permissions'; import { Response } from 'express'; import { AuthService } from '@/auth/auth.service'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import { UserRequest } from '@/requests'; import { JwtService } from '@/services/jwt.service'; import { UrlService } from '@n8n/backend-services'; diff --git a/packages/cli/src/controllers/workflow-statistics.controller.ts b/packages/cli/src/controllers/workflow-statistics.controller.ts index fb1341de7e1..bc01359c5ef 100644 --- a/packages/cli/src/controllers/workflow-statistics.controller.ts +++ b/packages/cli/src/controllers/workflow-statistics.controller.ts @@ -4,7 +4,7 @@ import { StatisticsNames, WorkflowStatisticsRepository } from '@n8n/db'; import { Get, Middleware, RestController } from '@n8n/decorators'; import { Response, NextFunction } from 'express'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { IWorkflowStatisticsDataLoaded } from '@/interfaces'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; diff --git a/packages/cli/src/credentials/__tests__/credentials.controller.test.ts b/packages/cli/src/credentials/__tests__/credentials.controller.test.ts index ea59699dea3..1f3da0e8313 100644 --- a/packages/cli/src/credentials/__tests__/credentials.controller.test.ts +++ b/packages/cli/src/credentials/__tests__/credentials.controller.test.ts @@ -20,8 +20,7 @@ import { GLOBAL_OWNER_ROLE, GLOBAL_MEMBER_ROLE } from '@n8n/db'; import type { Scope } from '@n8n/permissions'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import * as checkAccess from '@/permissions.ee/check-access'; import type { CredentialRequest } from '@/requests'; diff --git a/packages/cli/src/credentials/__tests__/credentials.service.test.ts b/packages/cli/src/credentials/__tests__/credentials.service.test.ts index 5dac73ba391..39bf8ed859a 100644 --- a/packages/cli/src/credentials/__tests__/credentials.service.test.ts +++ b/packages/cli/src/credentials/__tests__/credentials.service.test.ts @@ -51,8 +51,7 @@ import type { CredentialsHelper } from '@/credentials-helper'; import type { PolicyEnforcementService } from '@/policy/policy-enforcement.service'; import { PolicyViolationError } from '@/policy/policy-violation.error'; import { CredentialNotFoundError } from '@/errors/credential-not-found.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { ExternalHooks } from '@/external-hooks'; import type { ExternalSecretsConfig } from '@/modules/external-secrets.ee/external-secrets.config'; diff --git a/packages/cli/src/credentials/credentials.controller.ts b/packages/cli/src/credentials/credentials.controller.ts index 191d3eb9db6..dbdf613ad70 100644 --- a/packages/cli/src/credentials/credentials.controller.ts +++ b/packages/cli/src/credentials/credentials.controller.ts @@ -42,9 +42,7 @@ import { getExternalSecretExpressionPaths } from './external-secrets.utils'; import { CredentialsOverwrites } from '@/credentials-overwrites'; import { CredentialNotFoundError } from '@/errors/credential-not-found.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ForbiddenError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { listQueryMiddleware } from '@/middlewares'; import { userHasScopes } from '@/permissions.ee/check-access'; diff --git a/packages/cli/src/credentials/credentials.service.ee.ts b/packages/cli/src/credentials/credentials.service.ee.ts index eda4083e7c9..31442cd0525 100644 --- a/packages/cli/src/credentials/credentials.service.ee.ts +++ b/packages/cli/src/credentials/credentials.service.ee.ts @@ -12,7 +12,7 @@ import { hasGlobalScope } from '@n8n/permissions'; import { In, type EntityManager } from '@n8n/typeorm'; import type { ICredentialDataDecryptedObject } from 'n8n-workflow'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { TransferCredentialError } from '@/errors/response-errors/transfer-credential.error'; import { ExternalSecretsConfig } from '@/modules/external-secrets.ee/external-secrets.config'; import { SecretsProviderAccessCheckService } from '@/modules/external-secrets.ee/secret-provider-access-check.service.ee'; diff --git a/packages/cli/src/credentials/credentials.service.ts b/packages/cli/src/credentials/credentials.service.ts index 28298f9cf91..a01dd19a4e1 100644 --- a/packages/cli/src/credentials/credentials.service.ts +++ b/packages/cli/src/credentials/credentials.service.ts @@ -60,10 +60,7 @@ import { isCredSharingEnabled } from '@/constants/credential-sharing'; import { CredentialTypes } from '@/credential-types'; import { createCredentialsFromCredentialsEntity, CredentialsHelper } from '@/credentials-helper'; import { CredentialNotFoundError } from '@/errors/credential-not-found.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { ExternalHooks } from '@/external-hooks'; import { validateEntity } from '@/generic-helpers'; diff --git a/packages/cli/src/credentials/instance-credential-broker.ts b/packages/cli/src/credentials/instance-credential-broker.ts index cb4c03a6105..197c33661bc 100644 --- a/packages/cli/src/credentials/instance-credential-broker.ts +++ b/packages/cli/src/credentials/instance-credential-broker.ts @@ -7,7 +7,7 @@ import { import { Service } from '@n8n/di'; import type { ICredentialDataDecryptedObject } from 'n8n-workflow'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { UnprocessableRequestError } from '@n8n/errors'; import { CredentialsService } from './credentials.service'; import { diff --git a/packages/cli/src/credentials/validation.ts b/packages/cli/src/credentials/validation.ts index 2c862801f29..847743489e9 100644 --- a/packages/cli/src/credentials/validation.ts +++ b/packages/cli/src/credentials/validation.ts @@ -3,7 +3,7 @@ import type { User } from '@n8n/db'; import get from 'lodash/get'; import { type ICredentialDataDecryptedObject } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { SecretsProviderAccessCheckService } from '@/modules/external-secrets.ee/secret-provider-access-check.service.ee'; import { userHasScopes } from '@/permissions.ee/check-access'; diff --git a/packages/cli/src/encryption/__tests__/key-manager.service.test.ts b/packages/cli/src/encryption/__tests__/key-manager.service.test.ts index 046815a5949..f2218ae24cc 100644 --- a/packages/cli/src/encryption/__tests__/key-manager.service.test.ts +++ b/packages/cli/src/encryption/__tests__/key-manager.service.test.ts @@ -7,7 +7,7 @@ import { Cipher, type EncryptionKeyProxy, InstanceSettings } from 'n8n-core'; import { randomBytes } from 'node:crypto'; import { mock } from 'vitest-mock-extended'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { KeyManagerService } from '@/encryption/key-manager.service'; const makeKey = (overrides: Partial = {}): DeploymentKey => diff --git a/packages/cli/src/encryption/key-manager.service.ts b/packages/cli/src/encryption/key-manager.service.ts index f46f51ff3b2..5b740534ca8 100644 --- a/packages/cli/src/encryption/key-manager.service.ts +++ b/packages/cli/src/encryption/key-manager.service.ts @@ -16,7 +16,7 @@ import { } from 'n8n-core'; import { randomBytes } from 'node:crypto'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { isKeyRotationEnabled } from './key-rotation-flag'; diff --git a/packages/cli/src/environments.ee/variables/variables.controller.ee.ts b/packages/cli/src/environments.ee/variables/variables.controller.ee.ts index 5d0701bfe52..3e90478f9ff 100644 --- a/packages/cli/src/environments.ee/variables/variables.controller.ee.ts +++ b/packages/cli/src/environments.ee/variables/variables.controller.ee.ts @@ -7,8 +7,7 @@ import { AuthenticatedRequest } from '@n8n/db'; import { Body, Delete, Get, Licensed, Patch, Post, Query, RestController } from '@n8n/decorators'; import type { Response } from 'express'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { VariableCountLimitReachedError } from '@/errors/variable-count-limit-reached.error'; import { VariableValidationError } from '@/errors/variable-validation.error'; diff --git a/packages/cli/src/environments.ee/variables/variables.service.ee.ts b/packages/cli/src/environments.ee/variables/variables.service.ee.ts index ca6b7493470..9d7b453640d 100644 --- a/packages/cli/src/environments.ee/variables/variables.service.ee.ts +++ b/packages/cli/src/environments.ee/variables/variables.service.ee.ts @@ -11,8 +11,7 @@ import { Service } from '@n8n/di'; import { hasGlobalScope, Scope } from '@n8n/permissions'; import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import { VariableCountLimitReachedError } from '@/errors/variable-count-limit-reached.error'; import { VariableValidationError } from '@/errors/variable-validation.error'; import { EventService } from '@/events/event.service'; diff --git a/packages/cli/src/errors/__tests__/http-error-classifier.test.ts b/packages/cli/src/errors/__tests__/http-error-classifier.test.ts index 69c8bb0c91c..7899c1be2ff 100644 --- a/packages/cli/src/errors/__tests__/http-error-classifier.test.ts +++ b/packages/cli/src/errors/__tests__/http-error-classifier.test.ts @@ -1,9 +1,13 @@ import { BadRequest, Unauthorized } from 'express-openapi-validator/dist/framework/types'; -import { OperationalError, UnexpectedError, UserError } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { + BadRequestError, + NotFoundError, + OperationalError, + UnexpectedError, + UserError, +} from '@n8n/errors'; import { LicenseEulaRequiredError } from '@/errors/response-errors/license-eula-required.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { classifyHttpError, HttpErrorKind, isResponseError } from '../http-error-classifier'; diff --git a/packages/cli/src/errors/__tests__/http-error-serializers.test.ts b/packages/cli/src/errors/__tests__/http-error-serializers.test.ts index d925d5f9646..bdafd2ef7e7 100644 --- a/packages/cli/src/errors/__tests__/http-error-serializers.test.ts +++ b/packages/cli/src/errors/__tests__/http-error-serializers.test.ts @@ -1,14 +1,12 @@ import type { SourceControlledFile } from '@n8n/api-types'; -import { UnexpectedError, UserError } from 'n8n-workflow'; import { classifyHttpError } from '@/errors/http-error-classifier'; import { serializeInternalRestError, serializePublicApiError, } from '@/errors/http-error-serializers'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError, NotFoundError, UnexpectedError, UserError } from '@n8n/errors'; import { LicenseEulaRequiredError } from '@/errors/response-errors/license-eula-required.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { WorkflowPublishBlockedError } from '@/errors/response-errors/workflow-publish-blocked.error'; import { toImportBlockedError } from '@/modules/n8n-packages/engine/import-blocked.error'; import { PolicyViolationError } from '@/policy/policy-violation.error'; diff --git a/packages/cli/src/errors/aborted-execution-retry.error.ts b/packages/cli/src/errors/aborted-execution-retry.error.ts index 88ba834ee06..d05804fc3c2 100644 --- a/packages/cli/src/errors/aborted-execution-retry.error.ts +++ b/packages/cli/src/errors/aborted-execution-retry.error.ts @@ -1,4 +1,4 @@ -import { UnexpectedError } from 'n8n-workflow'; +import { UnexpectedError } from '@n8n/errors'; export class AbortedExecutionRetryError extends UnexpectedError { constructor() { diff --git a/packages/cli/src/errors/cache-errors/uncacheable-value.error.ts b/packages/cli/src/errors/cache-errors/uncacheable-value.error.ts index 83f859e99de..c7bf537daac 100644 --- a/packages/cli/src/errors/cache-errors/uncacheable-value.error.ts +++ b/packages/cli/src/errors/cache-errors/uncacheable-value.error.ts @@ -1,4 +1,4 @@ -import { UnexpectedError } from 'n8n-workflow'; +import { UnexpectedError } from '@n8n/errors'; export class UncacheableValueError extends UnexpectedError { constructor(key: string) { diff --git a/packages/cli/src/errors/credential-missing-id.error.ts b/packages/cli/src/errors/credential-missing-id.error.ts index bb1024656e8..0401a5a55cc 100644 --- a/packages/cli/src/errors/credential-missing-id.error.ts +++ b/packages/cli/src/errors/credential-missing-id.error.ts @@ -1,4 +1,4 @@ -import { UnexpectedError } from 'n8n-workflow'; +import { UnexpectedError } from '@n8n/errors'; export class CredentialMissingIdError extends UnexpectedError { constructor(credentialName: string, credentialType: string) { diff --git a/packages/cli/src/errors/credential-not-found.error.ts b/packages/cli/src/errors/credential-not-found.error.ts index e13d730e6a7..64d288a4470 100644 --- a/packages/cli/src/errors/credential-not-found.error.ts +++ b/packages/cli/src/errors/credential-not-found.error.ts @@ -1,4 +1,4 @@ -import { UserError } from 'n8n-workflow'; +import { UserError } from '@n8n/errors'; export class CredentialNotFoundError extends UserError { constructor(credentialId: string, credentialType?: string) { diff --git a/packages/cli/src/errors/credentials-overwrites-already-set.error.ts b/packages/cli/src/errors/credentials-overwrites-already-set.error.ts index 84480d641a2..c02d7f9890e 100644 --- a/packages/cli/src/errors/credentials-overwrites-already-set.error.ts +++ b/packages/cli/src/errors/credentials-overwrites-already-set.error.ts @@ -1,4 +1,4 @@ -import { UserError } from 'n8n-workflow'; +import { UserError } from '@n8n/errors'; export class CredentialsOverwritesAlreadySetError extends UserError { constructor() { diff --git a/packages/cli/src/errors/deduplication.error.ts b/packages/cli/src/errors/deduplication.error.ts index 996a41fc362..ae36ab43b1c 100644 --- a/packages/cli/src/errors/deduplication.error.ts +++ b/packages/cli/src/errors/deduplication.error.ts @@ -1,4 +1,4 @@ -import { UnexpectedError } from 'n8n-workflow'; +import { UnexpectedError } from '@n8n/errors'; export class DeduplicationError extends UnexpectedError { constructor(message: string) { diff --git a/packages/cli/src/errors/duplicate-execution.error.ts b/packages/cli/src/errors/duplicate-execution.error.ts index 01af6f960d3..fe4ad011517 100644 --- a/packages/cli/src/errors/duplicate-execution.error.ts +++ b/packages/cli/src/errors/duplicate-execution.error.ts @@ -1,4 +1,4 @@ -import { OperationalError } from 'n8n-workflow'; +import { OperationalError } from '@n8n/errors'; export class DuplicateExecutionError extends OperationalError { constructor( diff --git a/packages/cli/src/errors/execution-already-resuming.error.ts b/packages/cli/src/errors/execution-already-resuming.error.ts index 26ed588cab2..8343aa5958f 100644 --- a/packages/cli/src/errors/execution-already-resuming.error.ts +++ b/packages/cli/src/errors/execution-already-resuming.error.ts @@ -1,4 +1,4 @@ -import { OperationalError } from 'n8n-workflow'; +import { OperationalError } from '@n8n/errors'; export class ExecutionAlreadyResumingError extends OperationalError { constructor(executionId: string) { diff --git a/packages/cli/src/errors/execution-not-found-error.ts b/packages/cli/src/errors/execution-not-found-error.ts index f6e6f3a7c4a..ef9b6e5743f 100644 --- a/packages/cli/src/errors/execution-not-found-error.ts +++ b/packages/cli/src/errors/execution-not-found-error.ts @@ -1,4 +1,4 @@ -import { UnexpectedError } from 'n8n-workflow'; +import { UnexpectedError } from '@n8n/errors'; export class ExecutionNotFoundError extends UnexpectedError { constructor(executionId: string) { diff --git a/packages/cli/src/errors/feature-not-licensed.error.ts b/packages/cli/src/errors/feature-not-licensed.error.ts index 41f211fbdbf..0ac84dd120c 100644 --- a/packages/cli/src/errors/feature-not-licensed.error.ts +++ b/packages/cli/src/errors/feature-not-licensed.error.ts @@ -1,5 +1,5 @@ import type { LICENSE_FEATURES } from '@n8n/constants'; -import { UserError } from 'n8n-workflow'; +import { UserError } from '@n8n/errors'; export class FeatureNotLicensedError extends UserError { constructor( diff --git a/packages/cli/src/errors/folder-not-found.error.ts b/packages/cli/src/errors/folder-not-found.error.ts index 1066410b544..025d6292963 100644 --- a/packages/cli/src/errors/folder-not-found.error.ts +++ b/packages/cli/src/errors/folder-not-found.error.ts @@ -1,4 +1,4 @@ -import { OperationalError } from 'n8n-workflow'; +import { OperationalError } from '@n8n/errors'; export class FolderNotFoundError extends OperationalError { constructor(folderId: string) { diff --git a/packages/cli/src/errors/http-error-classifier.ts b/packages/cli/src/errors/http-error-classifier.ts index d80dff32da2..b5f9a94b6f9 100644 --- a/packages/cli/src/errors/http-error-classifier.ts +++ b/packages/cli/src/errors/http-error-classifier.ts @@ -1,7 +1,6 @@ import { HttpError, Unauthorized } from 'express-openapi-validator/dist/framework/types'; -import { UnexpectedError, UserError } from 'n8n-workflow'; -import { ResponseError } from '@/errors/response-errors/abstract/response.error'; +import { ResponseError, UnexpectedError, UserError } from '@n8n/errors'; export const enum HttpErrorKind { responseError = 'responseError', diff --git a/packages/cli/src/errors/invalid-concurrency-limit.error.ts b/packages/cli/src/errors/invalid-concurrency-limit.error.ts index 48c584b8b0f..731c636931a 100644 --- a/packages/cli/src/errors/invalid-concurrency-limit.error.ts +++ b/packages/cli/src/errors/invalid-concurrency-limit.error.ts @@ -1,4 +1,4 @@ -import { UserError } from 'n8n-workflow'; +import { UserError } from '@n8n/errors'; export class InvalidConcurrencyLimitError extends UserError { constructor(value: number) { diff --git a/packages/cli/src/errors/invalid-role.error.ts b/packages/cli/src/errors/invalid-role.error.ts index 7e14bdc2e12..34aa606b7d3 100644 --- a/packages/cli/src/errors/invalid-role.error.ts +++ b/packages/cli/src/errors/invalid-role.error.ts @@ -1,3 +1,3 @@ -import { UnexpectedError } from 'n8n-workflow'; +import { UnexpectedError } from '@n8n/errors'; export class InvalidRoleError extends UnexpectedError {} diff --git a/packages/cli/src/errors/max-stalled-count.error.ts b/packages/cli/src/errors/max-stalled-count.error.ts index 4e92fad4054..29c81446ac7 100644 --- a/packages/cli/src/errors/max-stalled-count.error.ts +++ b/packages/cli/src/errors/max-stalled-count.error.ts @@ -1,4 +1,4 @@ -import { OperationalError } from 'n8n-workflow'; +import { OperationalError } from '@n8n/errors'; /** * @docs https://docs.bullmq.io/guide/workers/stalled-jobs diff --git a/packages/cli/src/errors/missing-execution-stop.error.ts b/packages/cli/src/errors/missing-execution-stop.error.ts index be8dea83022..3de3259b965 100644 --- a/packages/cli/src/errors/missing-execution-stop.error.ts +++ b/packages/cli/src/errors/missing-execution-stop.error.ts @@ -1,4 +1,4 @@ -import { UserError } from 'n8n-workflow'; +import { UserError } from '@n8n/errors'; export class MissingExecutionStopError extends UserError { constructor(executionId: string) { diff --git a/packages/cli/src/errors/non-json-body.error.ts b/packages/cli/src/errors/non-json-body.error.ts index b6da36aead9..0d6449d45fd 100644 --- a/packages/cli/src/errors/non-json-body.error.ts +++ b/packages/cli/src/errors/non-json-body.error.ts @@ -1,4 +1,4 @@ -import { UserError } from 'n8n-workflow'; +import { UserError } from '@n8n/errors'; export class NonJsonBodyError extends UserError { constructor() { diff --git a/packages/cli/src/errors/postgres-live-rows-retrieval.error.ts b/packages/cli/src/errors/postgres-live-rows-retrieval.error.ts index 0bd59000cbd..ac4e8768ddd 100644 --- a/packages/cli/src/errors/postgres-live-rows-retrieval.error.ts +++ b/packages/cli/src/errors/postgres-live-rows-retrieval.error.ts @@ -1,4 +1,4 @@ -import { UnexpectedError } from 'n8n-workflow'; +import { UnexpectedError } from '@n8n/errors'; export class PostgresLiveRowsRetrievalError extends UnexpectedError { constructor(rows: unknown) { diff --git a/packages/cli/src/errors/pre-execute-blocked.error.ts b/packages/cli/src/errors/pre-execute-blocked.error.ts index a6fa845f67a..b065f98f3a9 100644 --- a/packages/cli/src/errors/pre-execute-blocked.error.ts +++ b/packages/cli/src/errors/pre-execute-blocked.error.ts @@ -1,4 +1,4 @@ -import { UserError } from 'n8n-workflow'; +import { UserError } from '@n8n/errors'; /** * Thrown when `workflow.preExecute` refuses the run. The original hook error diff --git a/packages/cli/src/errors/queued-execution-retry.error.ts b/packages/cli/src/errors/queued-execution-retry.error.ts index 9ccd17732cb..92647f3f1f2 100644 --- a/packages/cli/src/errors/queued-execution-retry.error.ts +++ b/packages/cli/src/errors/queued-execution-retry.error.ts @@ -1,4 +1,4 @@ -import { UnexpectedError } from 'n8n-workflow'; +import { UnexpectedError } from '@n8n/errors'; export class QueuedExecutionRetryError extends UnexpectedError { constructor() { diff --git a/packages/cli/src/errors/redactable.error.ts b/packages/cli/src/errors/redactable.error.ts index ecc492d22b8..34a793b828b 100644 --- a/packages/cli/src/errors/redactable.error.ts +++ b/packages/cli/src/errors/redactable.error.ts @@ -1,4 +1,4 @@ -import { UnexpectedError } from 'n8n-workflow'; +import { UnexpectedError } from '@n8n/errors'; export class RedactableError extends UnexpectedError { constructor(fieldName: string, args: string) { diff --git a/packages/cli/src/errors/response-errors/ai-preference-scope-full.error.ts b/packages/cli/src/errors/response-errors/ai-preference-scope-full.error.ts index f834b411718..513e92fd231 100644 --- a/packages/cli/src/errors/response-errors/ai-preference-scope-full.error.ts +++ b/packages/cli/src/errors/response-errors/ai-preference-scope-full.error.ts @@ -1,6 +1,6 @@ import type { AiPreferenceScope } from '@n8n/api-types'; -import { BadRequestError } from './bad-request.error'; +import { BadRequestError } from '@n8n/errors'; /** The per-scope cap refused a preference write. `meta` stays out of the REST body. */ export class AiPreferenceScopeFullError extends BadRequestError { diff --git a/packages/cli/src/errors/response-errors/incompatible-nodes-api-version.error.ts b/packages/cli/src/errors/response-errors/incompatible-nodes-api-version.error.ts index 0322ffd772d..d49fe274df1 100644 --- a/packages/cli/src/errors/response-errors/incompatible-nodes-api-version.error.ts +++ b/packages/cli/src/errors/response-errors/incompatible-nodes-api-version.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from '@n8n/errors'; /** * A community package requires a node-authoring API version this runtime does diff --git a/packages/cli/src/errors/response-errors/invalid-mfa-code.error.ts b/packages/cli/src/errors/response-errors/invalid-mfa-code.error.ts index cc00976a84e..7038124c76a 100644 --- a/packages/cli/src/errors/response-errors/invalid-mfa-code.error.ts +++ b/packages/cli/src/errors/response-errors/invalid-mfa-code.error.ts @@ -1,4 +1,4 @@ -import { ForbiddenError } from './forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; export class InvalidMfaCodeError extends ForbiddenError { constructor(hint?: string) { diff --git a/packages/cli/src/errors/response-errors/invalid-mfa-recovery-code-error.ts b/packages/cli/src/errors/response-errors/invalid-mfa-recovery-code-error.ts index baa0ead8701..09761bcf210 100644 --- a/packages/cli/src/errors/response-errors/invalid-mfa-recovery-code-error.ts +++ b/packages/cli/src/errors/response-errors/invalid-mfa-recovery-code-error.ts @@ -1,4 +1,4 @@ -import { ForbiddenError } from './forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; export class InvalidMfaRecoveryCodeError extends ForbiddenError { constructor(hint?: string) { diff --git a/packages/cli/src/errors/response-errors/license-eula-required.error.ts b/packages/cli/src/errors/response-errors/license-eula-required.error.ts index 64222349ee0..7acee7e2d3d 100644 --- a/packages/cli/src/errors/response-errors/license-eula-required.error.ts +++ b/packages/cli/src/errors/response-errors/license-eula-required.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from '@n8n/errors'; /** * Error thrown when license activation requires EULA acceptance. diff --git a/packages/cli/src/errors/response-errors/transfer-credential.error.ts b/packages/cli/src/errors/response-errors/transfer-credential.error.ts index f10de3104ae..c9257822f63 100644 --- a/packages/cli/src/errors/response-errors/transfer-credential.error.ts +++ b/packages/cli/src/errors/response-errors/transfer-credential.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from '@n8n/errors'; export class TransferCredentialError extends ResponseError { constructor(message: string) { diff --git a/packages/cli/src/errors/response-errors/transfer-workflow.error.ts b/packages/cli/src/errors/response-errors/transfer-workflow.error.ts index c22c0730890..a2b7d5a0932 100644 --- a/packages/cli/src/errors/response-errors/transfer-workflow.error.ts +++ b/packages/cli/src/errors/response-errors/transfer-workflow.error.ts @@ -1,4 +1,4 @@ -import { ResponseError } from './abstract/response.error'; +import { ResponseError } from '@n8n/errors'; export class TransferWorkflowError extends ResponseError { constructor(message: string) { diff --git a/packages/cli/src/errors/response-errors/webhook-not-found.error.ts b/packages/cli/src/errors/response-errors/webhook-not-found.error.ts index 94f48f288c7..69004fdc63e 100644 --- a/packages/cli/src/errors/response-errors/webhook-not-found.error.ts +++ b/packages/cli/src/errors/response-errors/webhook-not-found.error.ts @@ -1,4 +1,4 @@ -import { NotFoundError } from './not-found.error'; +import { NotFoundError } from '@n8n/errors'; export const webhookNotFoundErrorMessage = ({ path, diff --git a/packages/cli/src/errors/response-errors/workflow-activation-bad-request.error.ts b/packages/cli/src/errors/response-errors/workflow-activation-bad-request.error.ts index efb72525179..ee0ff6df835 100644 --- a/packages/cli/src/errors/response-errors/workflow-activation-bad-request.error.ts +++ b/packages/cli/src/errors/response-errors/workflow-activation-bad-request.error.ts @@ -1,4 +1,4 @@ -import { BadRequestError } from './bad-request.error'; +import { BadRequestError } from '@n8n/errors'; /** * Error thrown when a workflow fails to activate due to a node-level error. diff --git a/packages/cli/src/errors/response-errors/workflow-deactivation-bad-request.error.ts b/packages/cli/src/errors/response-errors/workflow-deactivation-bad-request.error.ts index d707da2ede4..838088bc845 100644 --- a/packages/cli/src/errors/response-errors/workflow-deactivation-bad-request.error.ts +++ b/packages/cli/src/errors/response-errors/workflow-deactivation-bad-request.error.ts @@ -1,4 +1,4 @@ -import { BadRequestError } from './bad-request.error'; +import { BadRequestError } from '@n8n/errors'; /** * Error thrown when a workflow fails to deactivate, e.g. because a diff --git a/packages/cli/src/errors/response-errors/workflow-publish-blocked.error.ts b/packages/cli/src/errors/response-errors/workflow-publish-blocked.error.ts index 88ddbbf4749..0f136f42b4b 100644 --- a/packages/cli/src/errors/response-errors/workflow-publish-blocked.error.ts +++ b/packages/cli/src/errors/response-errors/workflow-publish-blocked.error.ts @@ -1,6 +1,6 @@ import type { WorkflowReviewBlockedDetails, WorkflowReviewBlockedReason } from '@n8n/api-types'; -import { ConflictError } from './conflict.error'; +import { ConflictError } from '@n8n/errors'; const messages: Record = { review_pending: diff --git a/packages/cli/src/errors/response-errors/workflow-publish-forbidden.error.ts b/packages/cli/src/errors/response-errors/workflow-publish-forbidden.error.ts index 73b0e11b8b4..0874628452b 100644 --- a/packages/cli/src/errors/response-errors/workflow-publish-forbidden.error.ts +++ b/packages/cli/src/errors/response-errors/workflow-publish-forbidden.error.ts @@ -3,7 +3,7 @@ import type { WorkflowPublishForbiddenReason, } from '@n8n/api-types'; -import { ForbiddenError } from './forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; const messages: Record = { insufficient_api_key_scope: diff --git a/packages/cli/src/errors/shared-workflow-not-found.error.ts b/packages/cli/src/errors/shared-workflow-not-found.error.ts index f9b9d6564e9..c0fcf5e52d0 100644 --- a/packages/cli/src/errors/shared-workflow-not-found.error.ts +++ b/packages/cli/src/errors/shared-workflow-not-found.error.ts @@ -1,3 +1,3 @@ -import { UserError } from 'n8n-workflow'; +import { UserError } from '@n8n/errors'; export class SharedWorkflowNotFoundError extends UserError {} diff --git a/packages/cli/src/errors/single-webhook-trigger.error.ts b/packages/cli/src/errors/single-webhook-trigger.error.ts index 2627960863b..c558e884107 100644 --- a/packages/cli/src/errors/single-webhook-trigger.error.ts +++ b/packages/cli/src/errors/single-webhook-trigger.error.ts @@ -1,4 +1,4 @@ -import { UserError } from 'n8n-workflow'; +import { UserError } from '@n8n/errors'; export class SingleWebhookTriggerError extends UserError { constructor(triggerName: string) { diff --git a/packages/cli/src/errors/unknown-execution-mode.error.ts b/packages/cli/src/errors/unknown-execution-mode.error.ts index 8b30b4fec84..4e0b66ce7cb 100644 --- a/packages/cli/src/errors/unknown-execution-mode.error.ts +++ b/packages/cli/src/errors/unknown-execution-mode.error.ts @@ -1,4 +1,4 @@ -import { UnexpectedError } from 'n8n-workflow'; +import { UnexpectedError } from '@n8n/errors'; export class UnknownExecutionModeError extends UnexpectedError { constructor(mode: string) { diff --git a/packages/cli/src/errors/unsupported-end-user-credential-trigger.error.ts b/packages/cli/src/errors/unsupported-end-user-credential-trigger.error.ts index 5852a63046a..95124016811 100644 --- a/packages/cli/src/errors/unsupported-end-user-credential-trigger.error.ts +++ b/packages/cli/src/errors/unsupported-end-user-credential-trigger.error.ts @@ -1,4 +1,4 @@ -import { UserError } from 'n8n-workflow'; +import { UserError } from '@n8n/errors'; const SYSTEM_RESOLVER_MESSAGE = "End-user credentials aren't supported by this workflow's trigger. Supported triggers: Manual, Sub-workflow, Chat available in n8n Chat Hub or using n8n user authentication in hosted chat mode, and MCP, Form, or Webhook with n8n user authentication. To use another trigger, switch this credential to Fixed."; diff --git a/packages/cli/src/errors/variable-count-limit-reached.error.ts b/packages/cli/src/errors/variable-count-limit-reached.error.ts index ac1bdc0059f..8db5157da06 100644 --- a/packages/cli/src/errors/variable-count-limit-reached.error.ts +++ b/packages/cli/src/errors/variable-count-limit-reached.error.ts @@ -1,3 +1,3 @@ -import { UserError } from 'n8n-workflow'; +import { UserError } from '@n8n/errors'; export class VariableCountLimitReachedError extends UserError {} diff --git a/packages/cli/src/errors/variable-validation.error.ts b/packages/cli/src/errors/variable-validation.error.ts index 166659bafab..d552554bbac 100644 --- a/packages/cli/src/errors/variable-validation.error.ts +++ b/packages/cli/src/errors/variable-validation.error.ts @@ -1,3 +1,3 @@ -import { UserError } from 'n8n-workflow'; +import { UserError } from '@n8n/errors'; export class VariableValidationError extends UserError {} diff --git a/packages/cli/src/errors/webhook-response-too-large.error.ts b/packages/cli/src/errors/webhook-response-too-large.error.ts index 7a3f7c2f2fb..cae95e4792a 100644 --- a/packages/cli/src/errors/webhook-response-too-large.error.ts +++ b/packages/cli/src/errors/webhook-response-too-large.error.ts @@ -1,4 +1,4 @@ -import { UserError } from 'n8n-workflow'; +import { UserError } from '@n8n/errors'; /** * A response a worker cannot deliver back to the main instance because of its size. diff --git a/packages/cli/src/errors/workflow-history-version-not-found.error.ts b/packages/cli/src/errors/workflow-history-version-not-found.error.ts index 177ee22d9aa..a026c1c7662 100644 --- a/packages/cli/src/errors/workflow-history-version-not-found.error.ts +++ b/packages/cli/src/errors/workflow-history-version-not-found.error.ts @@ -1,3 +1,3 @@ -import { UnexpectedError } from 'n8n-workflow'; +import { UnexpectedError } from '@n8n/errors'; export class WorkflowHistoryVersionNotFoundError extends UnexpectedError {} diff --git a/packages/cli/src/errors/workflow-missing-id.error.ts b/packages/cli/src/errors/workflow-missing-id.error.ts index c709b753565..684558b1dfa 100644 --- a/packages/cli/src/errors/workflow-missing-id.error.ts +++ b/packages/cli/src/errors/workflow-missing-id.error.ts @@ -1,5 +1,5 @@ import type { Workflow, IWorkflowBase } from 'n8n-workflow'; -import { UnexpectedError } from 'n8n-workflow'; +import { UnexpectedError } from '@n8n/errors'; export class WorkflowMissingIdError extends UnexpectedError { constructor(workflow: Workflow | IWorkflowBase) { diff --git a/packages/cli/src/evaluation.ee/__tests__/evaluation-collection.service.test.ts b/packages/cli/src/evaluation.ee/__tests__/evaluation-collection.service.test.ts index f0cf6dd9d2a..18e877565c9 100644 --- a/packages/cli/src/evaluation.ee/__tests__/evaluation-collection.service.test.ts +++ b/packages/cli/src/evaluation.ee/__tests__/evaluation-collection.service.test.ts @@ -20,8 +20,7 @@ import { OperationalError } from 'n8n-workflow'; import type { Mocked } from 'vitest'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import type { Telemetry } from '@/telemetry'; import type { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; diff --git a/packages/cli/src/evaluation.ee/__tests__/evaluation-collections.controller.test.ts b/packages/cli/src/evaluation.ee/__tests__/evaluation-collections.controller.test.ts index ffff29f1f95..ba0bb5923a9 100644 --- a/packages/cli/src/evaluation.ee/__tests__/evaluation-collections.controller.test.ts +++ b/packages/cli/src/evaluation.ee/__tests__/evaluation-collections.controller.test.ts @@ -6,8 +6,7 @@ import { Container } from '@n8n/di'; import type { Mocked } from 'vitest'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import type { PostHogClient } from '@/posthog'; import type { EvaluationCollectionService } from '../evaluation-collection.service'; diff --git a/packages/cli/src/evaluation.ee/__tests__/evaluation-config.controller.test.ts b/packages/cli/src/evaluation.ee/__tests__/evaluation-config.controller.test.ts index 8c4b39cbae0..eedcfb8ead9 100644 --- a/packages/cli/src/evaluation.ee/__tests__/evaluation-config.controller.test.ts +++ b/packages/cli/src/evaluation.ee/__tests__/evaluation-config.controller.test.ts @@ -2,8 +2,7 @@ import type { Mocked } from 'vitest'; import type { AuthenticatedRequest, EvaluationConfig, User, WorkflowEntity } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { EvaluationApiError } from '../evaluation-api-error'; diff --git a/packages/cli/src/evaluation.ee/__tests__/evaluation-dataset.service.test.ts b/packages/cli/src/evaluation.ee/__tests__/evaluation-dataset.service.test.ts index 90132ff8c8d..622aed88181 100644 --- a/packages/cli/src/evaluation.ee/__tests__/evaluation-dataset.service.test.ts +++ b/packages/cli/src/evaluation.ee/__tests__/evaluation-dataset.service.test.ts @@ -5,9 +5,7 @@ import type { EvaluationConfigRepository } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; import type { IConnections, IRunData } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ForbiddenError, NotFoundError } from '@n8n/errors'; import type { ExecutionPersistence } from '@/executions/execution-persistence'; import type { DataTableColumn } from '@/modules/data-table/data-table-column.entity'; import type { DataTableService } from '@/modules/data-table/data-table.service'; diff --git a/packages/cli/src/evaluation.ee/__tests__/evaluation-test-run.service.test.ts b/packages/cli/src/evaluation.ee/__tests__/evaluation-test-run.service.test.ts index 38c046ee99d..29d7ab0ada1 100644 --- a/packages/cli/src/evaluation.ee/__tests__/evaluation-test-run.service.test.ts +++ b/packages/cli/src/evaluation.ee/__tests__/evaluation-test-run.service.test.ts @@ -10,7 +10,7 @@ import type { import type { Mocked } from 'vitest'; import { mock } from 'vitest-mock-extended'; -import { PaymentRequiredError } from '@/errors/response-errors/payment-required.error'; +import { PaymentRequiredError } from '@n8n/errors'; import { EvaluationTestRunService } from '../evaluation-test-run.service'; diff --git a/packages/cli/src/evaluation.ee/__tests__/test-runs.controller.ee.test.ts b/packages/cli/src/evaluation.ee/__tests__/test-runs.controller.ee.test.ts index 6706ce0e1ab..0e4f883d30c 100644 --- a/packages/cli/src/evaluation.ee/__tests__/test-runs.controller.ee.test.ts +++ b/packages/cli/src/evaluation.ee/__tests__/test-runs.controller.ee.test.ts @@ -8,8 +8,7 @@ import type { } from '@n8n/db'; import type express from 'express'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ConflictError, NotFoundError } from '@n8n/errors'; import type { TestRunnerService } from '@/evaluation.ee/test-runner/test-runner.service.ee'; import { TestRunsController } from '@/evaluation.ee/test-runs.controller.ee'; import type { TestRunsRequest } from '@/evaluation.ee/test-runs.types.ee'; diff --git a/packages/cli/src/evaluation.ee/evaluation-collection.service.ts b/packages/cli/src/evaluation.ee/evaluation-collection.service.ts index 32a0503a6be..882fe5afeb8 100644 --- a/packages/cli/src/evaluation.ee/evaluation-collection.service.ts +++ b/packages/cli/src/evaluation.ee/evaluation-collection.service.ts @@ -25,8 +25,7 @@ import { In } from '@n8n/typeorm'; import { OperationalError, type IDataObject } from 'n8n-workflow'; import { nanoid } from 'nanoid'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { TestRunnerService } from '@/evaluation.ee/test-runner/test-runner.service.ee'; import { Telemetry } from '@/telemetry'; import { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; diff --git a/packages/cli/src/evaluation.ee/evaluation-collections.controller.ee.ts b/packages/cli/src/evaluation.ee/evaluation-collections.controller.ee.ts index 18c2590f40b..d99a491f519 100644 --- a/packages/cli/src/evaluation.ee/evaluation-collections.controller.ee.ts +++ b/packages/cli/src/evaluation.ee/evaluation-collections.controller.ee.ts @@ -8,8 +8,7 @@ import { Logger } from '@n8n/backend-common'; import type { AuthenticatedRequest, User } from '@n8n/db'; import { Body, Delete, Get, Patch, Post, ProjectScope, RestController } from '@n8n/decorators'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { PostHogClient } from '@/posthog'; import { EvaluationCollectionService } from './evaluation-collection.service'; diff --git a/packages/cli/src/evaluation.ee/evaluation-config.controller.ts b/packages/cli/src/evaluation.ee/evaluation-config.controller.ts index e10cf1dcac8..437c733b04d 100644 --- a/packages/cli/src/evaluation.ee/evaluation-config.controller.ts +++ b/packages/cli/src/evaluation.ee/evaluation-config.controller.ts @@ -2,8 +2,7 @@ import { addDatasetRowSchema, upsertEvaluationConfigSchema } from '@n8n/api-type import type { AuthenticatedRequest, User } from '@n8n/db'; import { Delete, Get, Post, Put, RestController } from '@n8n/decorators'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { EvaluationConfigService } from './evaluation-config.service'; diff --git a/packages/cli/src/evaluation.ee/evaluation-dataset.service.ts b/packages/cli/src/evaluation.ee/evaluation-dataset.service.ts index 8c10a627514..e329e625ae2 100644 --- a/packages/cli/src/evaluation.ee/evaluation-dataset.service.ts +++ b/packages/cli/src/evaluation.ee/evaluation-dataset.service.ts @@ -23,9 +23,7 @@ import type { JsonValue, } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ForbiddenError, NotFoundError } from '@n8n/errors'; import { ExecutionPersistence } from '@/executions/execution-persistence'; import type { DataTableColumn } from '@/modules/data-table/data-table-column.entity'; import { DataTableService } from '@/modules/data-table/data-table.service'; diff --git a/packages/cli/src/evaluation.ee/evaluation-test-run.service.ts b/packages/cli/src/evaluation.ee/evaluation-test-run.service.ts index 50893c713ca..d1ccf992bc2 100644 --- a/packages/cli/src/evaluation.ee/evaluation-test-run.service.ts +++ b/packages/cli/src/evaluation.ee/evaluation-test-run.service.ts @@ -4,7 +4,7 @@ import type { TestRun } from '@n8n/db'; import { TestCaseExecutionRepository, TestRunRepository, WorkflowRepository } from '@n8n/db'; import { Service } from '@n8n/di'; -import { PaymentRequiredError } from '@/errors/response-errors/payment-required.error'; +import { PaymentRequiredError } from '@n8n/errors'; type Pagination = { offset: number; limit: number }; diff --git a/packages/cli/src/evaluation.ee/insights/__tests__/eval-insights.controller.test.ts b/packages/cli/src/evaluation.ee/insights/__tests__/eval-insights.controller.test.ts index 1966b124d8f..eb5def89db9 100644 --- a/packages/cli/src/evaluation.ee/insights/__tests__/eval-insights.controller.test.ts +++ b/packages/cli/src/evaluation.ee/insights/__tests__/eval-insights.controller.test.ts @@ -7,7 +7,7 @@ import { Container } from '@n8n/di'; import type { Mocked } from 'vitest'; import { mock } from 'vitest-mock-extended'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { PostHogClient } from '@/posthog'; import { EvalInsightsController } from '../eval-insights.controller.ee'; diff --git a/packages/cli/src/evaluation.ee/insights/__tests__/eval-insights.service.test.ts b/packages/cli/src/evaluation.ee/insights/__tests__/eval-insights.service.test.ts index 95228ef9288..8f4db864178 100644 --- a/packages/cli/src/evaluation.ee/insights/__tests__/eval-insights.service.test.ts +++ b/packages/cli/src/evaluation.ee/insights/__tests__/eval-insights.service.test.ts @@ -10,9 +10,7 @@ import type { import type { Mocked } from 'vitest'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ForbiddenError, NotFoundError } from '@n8n/errors'; import type { Telemetry } from '@/telemetry'; import { DETERMINISTIC_MODEL_TAG, EvalInsightsService } from '../eval-insights.service'; diff --git a/packages/cli/src/evaluation.ee/insights/eval-insights.controller.ee.ts b/packages/cli/src/evaluation.ee/insights/eval-insights.controller.ee.ts index 9aab37f64b2..4112564abbc 100644 --- a/packages/cli/src/evaluation.ee/insights/eval-insights.controller.ee.ts +++ b/packages/cli/src/evaluation.ee/insights/eval-insights.controller.ee.ts @@ -3,7 +3,7 @@ import { Logger } from '@n8n/backend-common'; import type { AuthenticatedRequest, User } from '@n8n/db'; import { Body, Post, ProjectScope, RestController } from '@n8n/decorators'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { PostHogClient } from '@/posthog'; import { EvalInsightsService } from './eval-insights.service'; diff --git a/packages/cli/src/evaluation.ee/insights/eval-insights.service.ts b/packages/cli/src/evaluation.ee/insights/eval-insights.service.ts index 6c7f6b7d3b4..35fecc820be 100644 --- a/packages/cli/src/evaluation.ee/insights/eval-insights.service.ts +++ b/packages/cli/src/evaluation.ee/insights/eval-insights.service.ts @@ -12,9 +12,7 @@ import type { EvaluationConfig, TestRun, User } from '@n8n/db'; import { EvaluationCollectionRepository, EvaluationConfigRepository } from '@n8n/db'; import { Service } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ForbiddenError, NotFoundError } from '@n8n/errors'; import { Telemetry } from '@/telemetry'; import { runMetricScales } from '../metric-scales'; diff --git a/packages/cli/src/evaluation.ee/test-runs.controller.ee.ts b/packages/cli/src/evaluation.ee/test-runs.controller.ee.ts index e6b36e10209..8755fce4ab8 100644 --- a/packages/cli/src/evaluation.ee/test-runs.controller.ee.ts +++ b/packages/cli/src/evaluation.ee/test-runs.controller.ee.ts @@ -12,8 +12,7 @@ import { UnexpectedError } from 'n8n-workflow'; import { resolveConfigMetricScales, runMetricScales } from './metric-scales'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ConflictError, NotFoundError } from '@n8n/errors'; import { TestRunnerService } from '@/evaluation.ee/test-runner/test-runner.service.ee'; import { TestRunsRequest } from '@/evaluation.ee/test-runs.types.ee'; import { listQueryMiddleware } from '@/middlewares'; diff --git a/packages/cli/src/executions/__tests__/execution.service.test.ts b/packages/cli/src/executions/__tests__/execution.service.test.ts index ef2d84a5533..483c632edb0 100644 --- a/packages/cli/src/executions/__tests__/execution.service.test.ts +++ b/packages/cli/src/executions/__tests__/execution.service.test.ts @@ -22,8 +22,7 @@ import type { ActiveExecutions } from '@/active-executions'; import type { ConcurrencyControlService } from '@/concurrency/concurrency-control.service'; import { AbortedExecutionRetryError } from '@/errors/aborted-execution-retry.error'; import { MissingExecutionStopError } from '@/errors/missing-execution-stop.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { EngineV2ExecutionReader } from '@/executions/engine-v2-execution-reader.service'; import { MissingExecutionDataError } from '@/executions/execution-data/missing-execution-data.error'; diff --git a/packages/cli/src/executions/__tests__/executions.controller.test.ts b/packages/cli/src/executions/__tests__/executions.controller.test.ts index 5bc56747cc8..cd50287fb7a 100644 --- a/packages/cli/src/executions/__tests__/executions.controller.test.ts +++ b/packages/cli/src/executions/__tests__/executions.controller.test.ts @@ -2,9 +2,7 @@ import { DeleteExecutionsDto } from '@n8n/api-types'; import type { AuthenticatedRequest, ExecutionSummaries, User } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { NotImplementedError } from '@/errors/response-errors/not-implemented.error'; +import { BadRequestError, NotFoundError, NotImplementedError } from '@n8n/errors'; import type { ExecutionCursor } from '@/executions/execution-cursor'; import type { ExecutionListService } from '@/executions/execution-list.service'; import type { ExecutionService } from '@/executions/execution.service'; diff --git a/packages/cli/src/executions/execution-cursor.ts b/packages/cli/src/executions/execution-cursor.ts index 3ad64d0785f..5d3baddd656 100644 --- a/packages/cli/src/executions/execution-cursor.ts +++ b/packages/cli/src/executions/execution-cursor.ts @@ -1,7 +1,7 @@ import type { SerializedCursor } from '@n8n/api-types'; import { z } from 'zod'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; const timestamp = z.string().datetime({ offset: true }); diff --git a/packages/cli/src/executions/execution.service.ts b/packages/cli/src/executions/execution.service.ts index ecb8e1aaccb..b730e25ef8c 100644 --- a/packages/cli/src/executions/execution.service.ts +++ b/packages/cli/src/executions/execution.service.ts @@ -45,10 +45,7 @@ import { ConcurrencyControlService } from '@/concurrency/concurrency-control.ser import { AbortedExecutionRetryError } from '@/errors/aborted-execution-retry.error'; import { MissingExecutionStopError } from '@/errors/missing-execution-stop.error'; import { QueuedExecutionRetryError } from '@/errors/queued-execution-retry.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, InternalServerError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import type { IExecutionFlattedResponse } from '@/interfaces'; import { License } from '@/license'; diff --git a/packages/cli/src/executions/executions.controller.ts b/packages/cli/src/executions/executions.controller.ts index 0c3ae4e2ab8..cc3c1a681e5 100644 --- a/packages/cli/src/executions/executions.controller.ts +++ b/packages/cli/src/executions/executions.controller.ts @@ -4,9 +4,7 @@ import { Body, Get, Patch, Post, RestController } from '@n8n/decorators'; import type { Scope } from '@n8n/permissions'; import type { Response } from 'express'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { NotImplementedError } from '@/errors/response-errors/not-implemented.error'; +import { BadRequestError, NotFoundError, NotImplementedError } from '@n8n/errors'; import { License } from '@/license'; import { isPositiveInteger } from '@/utils'; import { WorkflowSharingService } from '@/workflows/workflow-sharing.service'; diff --git a/packages/cli/src/executions/parse-range-query.middleware.ts b/packages/cli/src/executions/parse-range-query.middleware.ts index 86353f42c00..ee1e595949b 100644 --- a/packages/cli/src/executions/parse-range-query.middleware.ts +++ b/packages/cli/src/executions/parse-range-query.middleware.ts @@ -4,7 +4,7 @@ import { validate } from 'jsonschema'; import type { JsonObject } from 'n8n-workflow'; import { jsonParse, UnexpectedError } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import * as ResponseHelper from '@/response-helper'; import { parseExecutionCursor } from './execution-cursor'; diff --git a/packages/cli/src/executions/validation.ts b/packages/cli/src/executions/validation.ts index 75071114a8c..1caafd9d141 100644 --- a/packages/cli/src/executions/validation.ts +++ b/packages/cli/src/executions/validation.ts @@ -1,6 +1,6 @@ import { z } from 'zod'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { ExecutionRequest } from '@/executions/execution.types'; const executionUpdateSchema = z.object({ diff --git a/packages/cli/src/generic-helpers.ts b/packages/cli/src/generic-helpers.ts index 050902623c4..4c02410f63e 100644 --- a/packages/cli/src/generic-helpers.ts +++ b/packages/cli/src/generic-helpers.ts @@ -8,7 +8,7 @@ import type { import { validate } from 'class-validator'; import type { PersonalizationSurveyAnswersV4 } from './controllers/survey-answers.dto'; -import { BadRequestError } from './errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; export async function validateEntity( entity: diff --git a/packages/cli/src/license/__tests__/license.service.test.ts b/packages/cli/src/license/__tests__/license.service.test.ts index 3b5f11cb2ca..bf9d41a37f5 100644 --- a/packages/cli/src/license/__tests__/license.service.test.ts +++ b/packages/cli/src/license/__tests__/license.service.test.ts @@ -6,7 +6,7 @@ import type { TEntitlement } from '@n8n_io/license-sdk'; import { AxiosError } from 'axios'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { License } from '@/license'; import { LicenseErrors, LicenseService } from '@/license/license.service'; diff --git a/packages/cli/src/license/license.controller.ts b/packages/cli/src/license/license.controller.ts index c05b0fd620d..c80305c1aaa 100644 --- a/packages/cli/src/license/license.controller.ts +++ b/packages/cli/src/license/license.controller.ts @@ -4,7 +4,7 @@ import { Get, Post, RestController, GlobalScope, Body } from '@n8n/decorators'; import type { AxiosError } from 'axios'; import { InstanceSettings } from 'n8n-core'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { LicenseRequest } from '@/requests'; import { UrlService } from '@n8n/backend-services'; diff --git a/packages/cli/src/license/license.service.ts b/packages/cli/src/license/license.service.ts index 557e14d73fe..78c1c23e306 100644 --- a/packages/cli/src/license/license.service.ts +++ b/packages/cli/src/license/license.service.ts @@ -6,7 +6,7 @@ import { WorkflowRepository } from '@n8n/db'; import { Service } from '@n8n/di'; import { ensureError } from '@n8n/utils/errors/ensure-error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { LicenseEulaRequiredError } from '@/errors/response-errors/license-eula-required.error'; import { EventService } from '@/events/event.service'; import { License } from '@/license'; diff --git a/packages/cli/src/middlewares/__tests__/body-parser.test.ts b/packages/cli/src/middlewares/__tests__/body-parser.test.ts index 510f47e79b5..321508eadae 100644 --- a/packages/cli/src/middlewares/__tests__/body-parser.test.ts +++ b/packages/cli/src/middlewares/__tests__/body-parser.test.ts @@ -2,7 +2,7 @@ import type { Request, Response } from 'express'; import { mock } from 'vitest-mock-extended'; import { Readable } from 'stream'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { rawBodyReader } from '../body-parser'; diff --git a/packages/cli/src/middlewares/body-parser.ts b/packages/cli/src/middlewares/body-parser.ts index f5b9844faf4..609c54b18a2 100644 --- a/packages/cli/src/middlewares/body-parser.ts +++ b/packages/cli/src/middlewares/body-parser.ts @@ -9,8 +9,7 @@ import { type Readable } from 'stream'; import { Parser as XmlParser } from 'xml2js'; import { createGunzip, createInflate } from 'zlib'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { BadRequestError, UnprocessableRequestError } from '@n8n/errors'; const xmlParser = new XmlParser({ async: true, diff --git a/packages/cli/src/modules/agent-evals/__tests__/agent-eval-case-generation.service.test.ts b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-case-generation.service.test.ts index 7f10ff1ab0f..952e65c1159 100644 --- a/packages/cli/src/modules/agent-evals/__tests__/agent-eval-case-generation.service.test.ts +++ b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-case-generation.service.test.ts @@ -5,8 +5,7 @@ import type { Mocked } from 'vitest'; import { mock } from 'vitest-mock-extended'; import type { CredentialsService } from '@/credentials/credentials.service'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import type { InstanceWriteAccessService } from '@/services/instance-write-access.service'; import type { AgentConfigService } from '../../agents/agent-config.service'; diff --git a/packages/cli/src/modules/agent-evals/__tests__/agent-eval-rating.service.test.ts b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-rating.service.test.ts index 2452dca3d4a..ef7ab2bfb09 100644 --- a/packages/cli/src/modules/agent-evals/__tests__/agent-eval-rating.service.test.ts +++ b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-rating.service.test.ts @@ -12,8 +12,7 @@ import type { import type { Mocked } from 'vitest'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import type { AgentRepository } from '@/modules/agents/repositories/agent.repository'; import { AgentEvalRatingService } from '../agent-eval-rating.service'; diff --git a/packages/cli/src/modules/agent-evals/__tests__/agent-eval-runner.service.test.ts b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-runner.service.test.ts index 7ae1f3c3375..ea6eb634ed8 100644 --- a/packages/cli/src/modules/agent-evals/__tests__/agent-eval-runner.service.test.ts +++ b/packages/cli/src/modules/agent-evals/__tests__/agent-eval-runner.service.test.ts @@ -12,7 +12,7 @@ import type { InstanceSettings } from 'n8n-core'; import { mock, type MockProxy } from 'vitest-mock-extended'; import type { ConcurrencyControlService } from '@/concurrency/concurrency-control.service'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { resolveEvaluationConcurrencyLimit } from '@/evaluation.ee/evaluation-concurrency.helper'; import type { License } from '@/license'; import type { Agent } from '@/modules/agents/entities/agent.entity'; diff --git a/packages/cli/src/modules/agent-evals/__tests__/agent-eval.service.test.ts b/packages/cli/src/modules/agent-evals/__tests__/agent-eval.service.test.ts index 36f2dc2b854..1e785a2bbeb 100644 --- a/packages/cli/src/modules/agent-evals/__tests__/agent-eval.service.test.ts +++ b/packages/cli/src/modules/agent-evals/__tests__/agent-eval.service.test.ts @@ -10,8 +10,7 @@ import type { } from '@n8n/db'; import { mock, type MockProxy } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import type { Agent } from '@/modules/agents/entities/agent.entity'; import type { AgentRepository } from '@/modules/agents/repositories/agent.repository'; diff --git a/packages/cli/src/modules/agent-evals/__tests__/agent-evals.controller.test.ts b/packages/cli/src/modules/agent-evals/__tests__/agent-evals.controller.test.ts index a3623f47225..333dc950584 100644 --- a/packages/cli/src/modules/agent-evals/__tests__/agent-evals.controller.test.ts +++ b/packages/cli/src/modules/agent-evals/__tests__/agent-evals.controller.test.ts @@ -9,8 +9,7 @@ import { ControllerRegistryMetadata } from '@n8n/decorators'; import { Container } from '@n8n/di'; import { mock, type MockProxy } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import type { AgentEvalRatingService } from '../agent-eval-rating.service'; import type { AgentEvalService } from '../agent-eval.service'; diff --git a/packages/cli/src/modules/agent-evals/agent-eval-case-generation.service.ts b/packages/cli/src/modules/agent-evals/agent-eval-case-generation.service.ts index a89bb49ad97..04be40d2097 100644 --- a/packages/cli/src/modules/agent-evals/agent-eval-case-generation.service.ts +++ b/packages/cli/src/modules/agent-evals/agent-eval-case-generation.service.ts @@ -14,7 +14,7 @@ import { Service } from '@n8n/di'; import { OperationalError, UserError } from 'n8n-workflow'; import { CredentialsService } from '@/credentials/credentials.service'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { InstanceWriteAccessService } from '@/services/instance-write-access.service'; import { AgentEvalsFlagGate } from './agent-evals-flag-gate'; diff --git a/packages/cli/src/modules/agent-evals/agent-eval-rating.service.ts b/packages/cli/src/modules/agent-evals/agent-eval-rating.service.ts index d1f6a859cd0..685f390f393 100644 --- a/packages/cli/src/modules/agent-evals/agent-eval-rating.service.ts +++ b/packages/cli/src/modules/agent-evals/agent-eval-rating.service.ts @@ -9,8 +9,7 @@ import { } from '@n8n/db'; import { Service } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { AgentRepository } from '@/modules/agents/repositories/agent.repository'; import { toRatingRecord } from './agent-eval-record-mappers'; diff --git a/packages/cli/src/modules/agent-evals/agent-eval-runner.service.ts b/packages/cli/src/modules/agent-evals/agent-eval-runner.service.ts index e2a862f5b0c..69ce19a4488 100644 --- a/packages/cli/src/modules/agent-evals/agent-eval-runner.service.ts +++ b/packages/cli/src/modules/agent-evals/agent-eval-runner.service.ts @@ -20,9 +20,7 @@ import { jsonParse, jsonStringify } from 'n8n-workflow'; import pLimit from 'p-limit'; import { ConcurrencyControlService } from '@/concurrency/concurrency-control.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ForbiddenError, NotFoundError } from '@n8n/errors'; import { resolveEvaluationConcurrencyLimit } from '@/evaluation.ee/evaluation-concurrency.helper'; import { License } from '@/license'; import { AgentRepository } from '@/modules/agents/repositories/agent.repository'; diff --git a/packages/cli/src/modules/agent-evals/agent-eval.service.ts b/packages/cli/src/modules/agent-evals/agent-eval.service.ts index 42a9b93134d..6806c89016d 100644 --- a/packages/cli/src/modules/agent-evals/agent-eval.service.ts +++ b/packages/cli/src/modules/agent-evals/agent-eval.service.ts @@ -19,8 +19,7 @@ import { } from '@n8n/db'; import { Service } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { AgentRepository } from '@/modules/agents/repositories/agent.repository'; import { AgentEvalCaseGenerationService } from './agent-eval-case-generation.service'; diff --git a/packages/cli/src/modules/agent-evals/agent-evals-flag-gate.ts b/packages/cli/src/modules/agent-evals/agent-evals-flag-gate.ts index 9fb8377b3bb..d702acd388c 100644 --- a/packages/cli/src/modules/agent-evals/agent-evals-flag-gate.ts +++ b/packages/cli/src/modules/agent-evals/agent-evals-flag-gate.ts @@ -2,7 +2,7 @@ import { AGENT_EVALS_FLAG } from '@n8n/api-types'; import type { User } from '@n8n/db'; import { Service } from '@n8n/di'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { PostHogClient } from '@/posthog'; /** diff --git a/packages/cli/src/modules/agent-evals/agent-evals-required-modules.ts b/packages/cli/src/modules/agent-evals/agent-evals-required-modules.ts index c1541eec77d..134b0f38730 100644 --- a/packages/cli/src/modules/agent-evals/agent-evals-required-modules.ts +++ b/packages/cli/src/modules/agent-evals/agent-evals-required-modules.ts @@ -1,6 +1,6 @@ import type { ModuleRegistry } from '@n8n/backend-common'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; const REQUIRED_MODULES = ['agents', 'data-table'] as const; diff --git a/packages/cli/src/modules/agent-evals/agent-evals.controller.ts b/packages/cli/src/modules/agent-evals/agent-evals.controller.ts index dab8aed25d0..5ebe5e972fc 100644 --- a/packages/cli/src/modules/agent-evals/agent-evals.controller.ts +++ b/packages/cli/src/modules/agent-evals/agent-evals.controller.ts @@ -26,7 +26,7 @@ import { RestController, } from '@n8n/decorators'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { AgentEvalRatingService } from './agent-eval-rating.service'; import { AgentEvalService } from './agent-eval.service'; diff --git a/packages/cli/src/modules/agents/__tests__/agent-chat-execution.service.test.ts b/packages/cli/src/modules/agents/__tests__/agent-chat-execution.service.test.ts index e68ae2013d4..10477d977c0 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-chat-execution.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-chat-execution.service.test.ts @@ -5,7 +5,7 @@ import { createDeferredPromise } from '@n8n/utils/promise/deferred-promise'; import type { InstanceSettings } from 'n8n-core'; import { mock } from 'vitest-mock-extended'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { Publisher } from '@/scaling/pubsub/publisher.service'; import { AgentChatExecutionService } from '../agent-chat-execution.service'; diff --git a/packages/cli/src/modules/agents/__tests__/agent-chat.controller.test.ts b/packages/cli/src/modules/agents/__tests__/agent-chat.controller.test.ts index 7b870de41ae..937ac31467a 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-chat.controller.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-chat.controller.test.ts @@ -7,7 +7,7 @@ import { mock } from 'vitest-mock-extended'; import { FileNotFoundError } from 'n8n-core'; import type { CredentialsService } from '@/credentials/credentials.service'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { AgentChatAttachmentService } from '../agent-chat-attachment.service'; import { AgentChatController } from '../agent-chat.controller'; diff --git a/packages/cli/src/modules/agents/__tests__/agent-custom-tools.service.test.ts b/packages/cli/src/modules/agents/__tests__/agent-custom-tools.service.test.ts index 9d24f7aaa6b..f80a673bd38 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-custom-tools.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-custom-tools.service.test.ts @@ -3,7 +3,7 @@ import { mockLogger } from '@n8n/backend-test-utils'; import { mock } from 'vitest-mock-extended'; import { UserError } from 'n8n-workflow'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { AgentModificationTelemetryService } from '../agent-modification-telemetry.service'; import type { AgentRuntimeCacheService } from '../agent-runtime-cache.service'; diff --git a/packages/cli/src/modules/agents/__tests__/agent-integration-management.service.test.ts b/packages/cli/src/modules/agents/__tests__/agent-integration-management.service.test.ts index 8a38b097835..17bf3fffe98 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-integration-management.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-integration-management.service.test.ts @@ -5,8 +5,7 @@ import { createDeferredPromise } from '@n8n/utils/promise/deferred-promise'; import { mock } from 'vitest-mock-extended'; import type { CredentialsService } from '@/credentials/credentials.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { AgentIntegrationManagementService } from '../agent-integration-management.service'; import type { AgentIntegrationPersistenceService } from '../agent-integration-persistence.service'; diff --git a/packages/cli/src/modules/agents/__tests__/agent-integrations.controller.test.ts b/packages/cli/src/modules/agents/__tests__/agent-integrations.controller.test.ts index e0af76b678d..48c86787a15 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-integrations.controller.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-integrations.controller.test.ts @@ -13,7 +13,7 @@ import type { ChatIntegrationService } from '../integrations/chat-integration.se import type { AgentChannelStatusRepository } from '../repositories/agent-channel-status.repository'; import type { AgentRepository } from '../repositories/agent.repository'; import type { CollaborationService } from '@/collaboration/collaboration.service'; -import { LockedError } from '@/errors/response-errors/locked.error'; +import { LockedError } from '@n8n/errors'; import { expectProjectScopedAgentRoutes, getRoutesByHandlerName, diff --git a/packages/cli/src/modules/agents/__tests__/agent-knowledge.controller.test.ts b/packages/cli/src/modules/agents/__tests__/agent-knowledge.controller.test.ts index c13b2a54fd3..0aba983d437 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-knowledge.controller.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-knowledge.controller.test.ts @@ -2,8 +2,7 @@ import type { Mocked } from 'vitest'; import { mock } from 'vitest-mock-extended'; import multer from 'multer'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { AgentKnowledgeController } from '../agent-knowledge.controller'; import type { AgentKnowledgeService } from '../agent-knowledge.service'; diff --git a/packages/cli/src/modules/agents/__tests__/agent-mcp-access.service.test.ts b/packages/cli/src/modules/agents/__tests__/agent-mcp-access.service.test.ts index 94e1d7e052d..242523ca22a 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-mcp-access.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-mcp-access.service.test.ts @@ -2,7 +2,7 @@ import { mockInstance } from '@n8n/backend-test-utils'; import { User } from '@n8n/db'; import { CollaborationService } from '@/collaboration/collaboration.service'; -import { LockedError } from '@/errors/response-errors/locked.error'; +import { LockedError } from '@n8n/errors'; import { ProjectScopeService } from '@/permissions.ee/project-scope.service'; import { AgentMcpAccessService } from '../agent-mcp-access.service'; diff --git a/packages/cli/src/modules/agents/__tests__/agent-publish.controller.test.ts b/packages/cli/src/modules/agents/__tests__/agent-publish.controller.test.ts index f03b314bd0a..98b9581b745 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-publish.controller.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-publish.controller.test.ts @@ -2,7 +2,7 @@ import type { Mocked } from 'vitest'; import { mock } from 'vitest-mock-extended'; import type { CredentialsService } from '@/credentials/credentials.service'; -import { LockedError } from '@/errors/response-errors/locked.error'; +import { LockedError } from '@n8n/errors'; import type { AgentPublishService } from '../agent-publish.service'; import { AgentPublishController } from '../agent-publish.controller'; diff --git a/packages/cli/src/modules/agents/__tests__/agent-publish.service.test.ts b/packages/cli/src/modules/agents/__tests__/agent-publish.service.test.ts index a2e58130365..f75cdc41f74 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-publish.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-publish.service.test.ts @@ -7,7 +7,7 @@ import { QueryFailedError } from '@n8n/typeorm'; import { mock } from 'vitest-mock-extended'; import type { CredentialsService } from '@/credentials/credentials.service'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { Telemetry } from '@/telemetry'; diff --git a/packages/cli/src/modules/agents/__tests__/agent-session-langsmith-export.service.test.ts b/packages/cli/src/modules/agents/__tests__/agent-session-langsmith-export.service.test.ts index 370a879ee8b..996dce23016 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-session-langsmith-export.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-session-langsmith-export.service.test.ts @@ -4,7 +4,7 @@ import type { User } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; import { N8N_VERSION } from '@/constants'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { AiService } from '@/services/ai.service'; import { AgentSessionLangSmithExportService } from '../agent-session-langsmith-export.service'; diff --git a/packages/cli/src/modules/agents/__tests__/agent-task.service.test.ts b/packages/cli/src/modules/agents/__tests__/agent-task.service.test.ts index 3ef35fa430f..7040d9cdf60 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-task.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-task.service.test.ts @@ -5,8 +5,7 @@ import type { User } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; import type { InstanceSettings, ScheduledTaskManager } from 'n8n-core'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import type { Publisher } from '@/scaling/pubsub/publisher.service'; import { AgentChangePublisher } from '../agent-change-publisher.service'; diff --git a/packages/cli/src/modules/agents/__tests__/agent-tasks.controller.test.ts b/packages/cli/src/modules/agents/__tests__/agent-tasks.controller.test.ts index 0e37a02af48..be317387610 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-tasks.controller.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-tasks.controller.test.ts @@ -1,7 +1,7 @@ import type { Mocked } from 'vitest'; import { mock } from 'vitest-mock-extended'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { AgentTaskService } from '../agent-task.service'; import { AgentTasksController } from '../agent-tasks.controller'; diff --git a/packages/cli/src/modules/agents/__tests__/agents-builder-tools.service.test.ts b/packages/cli/src/modules/agents/__tests__/agents-builder-tools.service.test.ts index 798e989377b..3f5f6d33829 100644 --- a/packages/cli/src/modules/agents/__tests__/agents-builder-tools.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agents-builder-tools.service.test.ts @@ -21,13 +21,12 @@ import { NodeConnectionTypes } from 'n8n-workflow'; import { mock } from 'vitest-mock-extended'; import type { CredentialTypes } from '@/credential-types'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError, LockedError } from '@n8n/errors'; import type { McpRegistryService } from '@/modules/mcp-registry/registry/mcp-registry.service'; import type { NodeTypes } from '@/node-types'; import type { AiGatewayService } from '@/services/ai-gateway.service'; import type { AiService } from '@/services/ai.service'; import type { CollaborationService } from '@/collaboration/collaboration.service'; -import { LockedError } from '@/errors/response-errors/locked.error'; import type { DynamicNodeParametersService } from '@/services/dynamic-node-parameters.service'; import type { FreeAiCreditsService } from '@/services/free-ai-credits.service'; import type { Telemetry } from '@/telemetry'; diff --git a/packages/cli/src/modules/agents/__tests__/agents-catalog.controller.test.ts b/packages/cli/src/modules/agents/__tests__/agents-catalog.controller.test.ts index 6db7179ecfe..987a238b1d8 100644 --- a/packages/cli/src/modules/agents/__tests__/agents-catalog.controller.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agents-catalog.controller.test.ts @@ -3,7 +3,7 @@ import type { AuthenticatedRequest } from '@n8n/db'; import type { Response } from 'express'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { AgentIntegrationPersistenceService } from '../agent-integration-persistence.service'; import type { AgentModelCatalogService } from '../agent-model-catalog.service'; diff --git a/packages/cli/src/modules/agents/__tests__/agents.service.test.ts b/packages/cli/src/modules/agents/__tests__/agents.service.test.ts index f04bc032268..afb6a3fa7dd 100644 --- a/packages/cli/src/modules/agents/__tests__/agents.service.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agents.service.test.ts @@ -7,8 +7,7 @@ import { Container } from '@n8n/di'; import { QueryFailedError } from '@n8n/typeorm'; import { mock } from 'vitest-mock-extended'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ConflictError, NotFoundError } from '@n8n/errors'; import type { AgentChatAttachmentService } from '../agent-chat-attachment.service'; import type { AgentKnowledgeService } from '../agent-knowledge.service'; diff --git a/packages/cli/src/modules/agents/__tests__/instance-ai-agent-context.adapter.test.ts b/packages/cli/src/modules/agents/__tests__/instance-ai-agent-context.adapter.test.ts index 987e943ff85..1d6d6e60653 100644 --- a/packages/cli/src/modules/agents/__tests__/instance-ai-agent-context.adapter.test.ts +++ b/packages/cli/src/modules/agents/__tests__/instance-ai-agent-context.adapter.test.ts @@ -6,8 +6,7 @@ import { UserError } from 'n8n-workflow'; import type { McpRegistryService } from '@/modules/mcp-registry/registry/mcp-registry.service'; import { userHasScopes } from '@/permissions.ee/check-access'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import type { AgentExecutionService, ThreadListItem } from '../agent-execution.service'; import type { AgentIntegrationPersistenceService } from '../agent-integration-persistence.service'; diff --git a/packages/cli/src/modules/agents/__tests__/instance-ai-builder-delegate.adapter.test.ts b/packages/cli/src/modules/agents/__tests__/instance-ai-builder-delegate.adapter.test.ts index 8084e3eef9d..8b1df44f93d 100644 --- a/packages/cli/src/modules/agents/__tests__/instance-ai-builder-delegate.adapter.test.ts +++ b/packages/cli/src/modules/agents/__tests__/instance-ai-builder-delegate.adapter.test.ts @@ -12,8 +12,7 @@ import { Like } from '@n8n/typeorm'; import { UserError } from 'n8n-workflow'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import * as checkAccess from '@/permissions.ee/check-access'; import type { AgentsService } from '../agents.service'; diff --git a/packages/cli/src/modules/agents/agent-chat-execution.service.ts b/packages/cli/src/modules/agents/agent-chat-execution.service.ts index f8575462c71..c881b701515 100644 --- a/packages/cli/src/modules/agents/agent-chat-execution.service.ts +++ b/packages/cli/src/modules/agents/agent-chat-execution.service.ts @@ -3,7 +3,7 @@ import { OnPubSubEvent } from '@n8n/decorators'; import { Service } from '@n8n/di'; import { InstanceSettings } from 'n8n-core'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { PubSubCommandMap } from '@/scaling/pubsub/pubsub.event-map'; import { Publisher } from '@/scaling/pubsub/publisher.service'; diff --git a/packages/cli/src/modules/agents/agent-chat.controller.ts b/packages/cli/src/modules/agents/agent-chat.controller.ts index fe1cfb613c8..fc6e22468cc 100644 --- a/packages/cli/src/modules/agents/agent-chat.controller.ts +++ b/packages/cli/src/modules/agents/agent-chat.controller.ts @@ -29,8 +29,7 @@ import { pipeline } from 'node:stream/promises'; import { randomUUID } from 'node:crypto'; import { CredentialsService } from '@/credentials/credentials.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { AgentsCredentialProvider } from './adapters/agents-credential-provider'; import { AgentChatAttachmentService } from './agent-chat-attachment.service'; diff --git a/packages/cli/src/modules/agents/agent-config.service.ts b/packages/cli/src/modules/agents/agent-config.service.ts index 0f9dcc818a5..e29754efda7 100644 --- a/packages/cli/src/modules/agents/agent-config.service.ts +++ b/packages/cli/src/modules/agents/agent-config.service.ts @@ -15,7 +15,7 @@ import { isRecord } from '@n8n/utils/is-record'; import { UserError } from 'n8n-workflow'; import { CredentialsService } from '@/credentials/credentials.service'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { diff --git a/packages/cli/src/modules/agents/agent-execution.service.ts b/packages/cli/src/modules/agents/agent-execution.service.ts index aab37be898f..defdff62679 100644 --- a/packages/cli/src/modules/agents/agent-execution.service.ts +++ b/packages/cli/src/modules/agents/agent-execution.service.ts @@ -13,7 +13,7 @@ import chunk from 'lodash/chunk'; import { ErrorReporter, StorageConfig } from 'n8n-core'; import { OperationalError, UnexpectedError } from 'n8n-workflow'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError } from '@n8n/errors'; import type { AgentRunTelemetryType, IAgentConfigurationTelemetryProperties } from '@/interfaces'; import { Telemetry } from '@/telemetry'; diff --git a/packages/cli/src/modules/agents/agent-integration-management.service.ts b/packages/cli/src/modules/agents/agent-integration-management.service.ts index cab9f97cb73..c5db24f2ba3 100644 --- a/packages/cli/src/modules/agents/agent-integration-management.service.ts +++ b/packages/cli/src/modules/agents/agent-integration-management.service.ts @@ -9,8 +9,7 @@ import { Service } from '@n8n/di'; import { runSerially } from '@n8n/utils/run-serially'; import { CredentialsService } from '@/credentials/credentials.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { AgentIntegrationPersistenceService, diff --git a/packages/cli/src/modules/agents/agent-integrations.controller.ts b/packages/cli/src/modules/agents/agent-integrations.controller.ts index 35b8ee58dbe..a819f3ead6f 100644 --- a/packages/cli/src/modules/agents/agent-integrations.controller.ts +++ b/packages/cli/src/modules/agents/agent-integrations.controller.ts @@ -19,7 +19,7 @@ import { AgentChannelStatusRepository } from './repositories/agent-channel-statu import { AgentRepository } from './repositories/agent.repository'; import { CollaborationService } from '@/collaboration/collaboration.service'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; @RestController('/projects/:projectId/agents/v2') export class AgentIntegrationsController { diff --git a/packages/cli/src/modules/agents/agent-knowledge-mirror.service.ts b/packages/cli/src/modules/agents/agent-knowledge-mirror.service.ts index cb22df59dd5..64948fd1864 100644 --- a/packages/cli/src/modules/agents/agent-knowledge-mirror.service.ts +++ b/packages/cli/src/modules/agents/agent-knowledge-mirror.service.ts @@ -7,8 +7,7 @@ import escapeRegExp from 'lodash/escapeRegExp'; import { OperationalError, safeRegex } from 'n8n-workflow'; import { nanoid } from 'nanoid'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { buildMirrorFinalizeCommand, diff --git a/packages/cli/src/modules/agents/agent-knowledge-retrieval.ts b/packages/cli/src/modules/agents/agent-knowledge-retrieval.ts index 29e691e11ab..377d20af379 100644 --- a/packages/cli/src/modules/agents/agent-knowledge-retrieval.ts +++ b/packages/cli/src/modules/agents/agent-knowledge-retrieval.ts @@ -1,7 +1,7 @@ import type { ExecutionDataStorageLocation } from '@n8n/db'; import { z } from 'zod'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { hasControlCharacter } from './agent-knowledge-storage'; diff --git a/packages/cli/src/modules/agents/agent-knowledge-storage.ts b/packages/cli/src/modules/agents/agent-knowledge-storage.ts index a24b85fe731..4dd8628da6f 100644 --- a/packages/cli/src/modules/agents/agent-knowledge-storage.ts +++ b/packages/cli/src/modules/agents/agent-knowledge-storage.ts @@ -2,7 +2,7 @@ import type { AgentFileDto } from '@n8n/api-types'; import { getPromptWorkspaceRoot, type SandboxProvider } from '@n8n/agents/sandbox'; import path from 'node:path'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { AgentFile } from './entities/agent-file.entity'; diff --git a/packages/cli/src/modules/agents/agent-knowledge.controller.ts b/packages/cli/src/modules/agents/agent-knowledge.controller.ts index 65bacc0210c..c45744aaafc 100644 --- a/packages/cli/src/modules/agents/agent-knowledge.controller.ts +++ b/packages/cli/src/modules/agents/agent-knowledge.controller.ts @@ -4,8 +4,7 @@ import { Delete, Get, Param, Post, ProjectScope, RestController } from '@n8n/dec import type { Response } from 'express'; import multer from 'multer'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { AgentKnowledgeService } from './agent-knowledge.service'; import { AgentRuntimeCacheService } from './agent-runtime-cache.service'; diff --git a/packages/cli/src/modules/agents/agent-knowledge.service.ts b/packages/cli/src/modules/agents/agent-knowledge.service.ts index 519be983492..b489006147b 100644 --- a/packages/cli/src/modules/agents/agent-knowledge.service.ts +++ b/packages/cli/src/modules/agents/agent-knowledge.service.ts @@ -12,7 +12,7 @@ import { createReadStream } from 'node:fs'; import { unlink } from 'node:fs/promises'; import path from 'node:path'; import type { Readable } from 'node:stream'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { getAgentOrThrow } from './utils/get-agent-or-throw'; import { diff --git a/packages/cli/src/modules/agents/agent-mcp-access.service.ts b/packages/cli/src/modules/agents/agent-mcp-access.service.ts index f9a150ec502..1b3823d3a44 100644 --- a/packages/cli/src/modules/agents/agent-mcp-access.service.ts +++ b/packages/cli/src/modules/agents/agent-mcp-access.service.ts @@ -3,7 +3,7 @@ import type { User } from '@n8n/db'; import { Service } from '@n8n/di'; import { CollaborationService } from '@/collaboration/collaboration.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { ProjectScopeService } from '@/permissions.ee/project-scope.service'; import type { Agent } from './entities/agent.entity'; diff --git a/packages/cli/src/modules/agents/agent-message-queue.service.ts b/packages/cli/src/modules/agents/agent-message-queue.service.ts index 8bd3060d30d..e9182d63c93 100644 --- a/packages/cli/src/modules/agents/agent-message-queue.service.ts +++ b/packages/cli/src/modules/agents/agent-message-queue.service.ts @@ -3,9 +3,7 @@ import { TransactionRunner, type OperationContext } from '@n8n/db'; import { Service } from '@n8n/di'; import { OperationalError, UserError } from 'n8n-workflow'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ConflictError, BadRequestError, NotFoundError } from '@n8n/errors'; import { AgentChatAttachmentService } from './agent-chat-attachment.service'; import { AgentExecutionUpdateBroadcaster } from './agent-execution-update-broadcaster'; diff --git a/packages/cli/src/modules/agents/agent-publish.service.ts b/packages/cli/src/modules/agents/agent-publish.service.ts index 257edabfe4a..d6d485ab6be 100644 --- a/packages/cli/src/modules/agents/agent-publish.service.ts +++ b/packages/cli/src/modules/agents/agent-publish.service.ts @@ -17,8 +17,7 @@ import { deepCopy, UserError } from 'n8n-workflow'; import { v4 as uuid } from 'uuid'; import { CredentialsService } from '@/credentials/credentials.service'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ConflictError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { getMissingSkillIds } from '@/modules/agents/utils/agent-missing-skill-ids'; import { Telemetry } from '@/telemetry'; diff --git a/packages/cli/src/modules/agents/agent-sandbox.controller.ts b/packages/cli/src/modules/agents/agent-sandbox.controller.ts index 6f78ecef066..e5e68744931 100644 --- a/packages/cli/src/modules/agents/agent-sandbox.controller.ts +++ b/packages/cli/src/modules/agents/agent-sandbox.controller.ts @@ -3,7 +3,7 @@ import type { AuthenticatedRequest } from '@n8n/db'; import { Param, Post, ProjectScope, RestController } from '@n8n/decorators'; import type { Response } from 'express'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { AgentKnowledgeService } from './agent-knowledge.service'; import { AgentSandboxRuntimeService } from './agent-sandbox-runtime.service'; diff --git a/packages/cli/src/modules/agents/agent-session-langsmith-export.service.ts b/packages/cli/src/modules/agents/agent-session-langsmith-export.service.ts index 0d9b974965a..ff4f6b57920 100644 --- a/packages/cli/src/modules/agents/agent-session-langsmith-export.service.ts +++ b/packages/cli/src/modules/agents/agent-session-langsmith-export.service.ts @@ -13,9 +13,7 @@ import { nanoid } from 'nanoid'; import { v5 as uuidv5 } from 'uuid'; import { N8N_VERSION } from '@/constants'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error'; +import { ConflictError, NotFoundError, ServiceUnavailableError } from '@n8n/errors'; import { AiService } from '@/services/ai.service'; import { ProxyTokenManager } from '@/services/proxy-token-manager'; import { createAiProxyFetch } from '@/utils/ai-proxy-fetch'; diff --git a/packages/cli/src/modules/agents/agent-skills.service.ts b/packages/cli/src/modules/agents/agent-skills.service.ts index fce6bb50c39..2bf1be3691e 100644 --- a/packages/cli/src/modules/agents/agent-skills.service.ts +++ b/packages/cli/src/modules/agents/agent-skills.service.ts @@ -9,8 +9,7 @@ import { Container, Service } from '@n8n/di'; import isEqual from 'lodash/isEqual'; import { UserError } from 'n8n-workflow'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ConflictError, NotFoundError } from '@n8n/errors'; import { AgentModificationTelemetryService, diff --git a/packages/cli/src/modules/agents/agent-task.service.ts b/packages/cli/src/modules/agents/agent-task.service.ts index 45b3ab56bf4..21005fddceb 100644 --- a/packages/cli/src/modules/agents/agent-task.service.ts +++ b/packages/cli/src/modules/agents/agent-task.service.ts @@ -10,8 +10,7 @@ import { randomUUID } from 'crypto'; import { DateTime } from 'luxon'; import { InstanceSettings, ScheduledTaskManager, type ScheduledTaskGroup } from 'n8n-core'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import type { PubSubCommandMap } from '@/scaling/pubsub/pubsub.event-map'; import { diff --git a/packages/cli/src/modules/agents/agent-threads.controller.ts b/packages/cli/src/modules/agents/agent-threads.controller.ts index f00d2698603..094881978d3 100644 --- a/packages/cli/src/modules/agents/agent-threads.controller.ts +++ b/packages/cli/src/modules/agents/agent-threads.controller.ts @@ -3,7 +3,7 @@ import type { AuthenticatedRequest } from '@n8n/db'; import { Delete, Get, Post, ProjectScope, Query, RestController } from '@n8n/decorators'; import type { Response } from 'express'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { AgentExecutionService } from './agent-execution.service'; import { AgentSessionLangSmithExportService } from './agent-session-langsmith-export.service'; diff --git a/packages/cli/src/modules/agents/agent-upload.middleware.ts b/packages/cli/src/modules/agents/agent-upload.middleware.ts index f2181e6e385..a624ce9a41b 100644 --- a/packages/cli/src/modules/agents/agent-upload.middleware.ts +++ b/packages/cli/src/modules/agents/agent-upload.middleware.ts @@ -9,7 +9,7 @@ import multer from 'multer'; import { unlink } from 'node:fs/promises'; import path from 'node:path'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; const allowedAgentFileExtensions = new Set(ALLOWED_AGENT_FILE_EXTENSIONS); diff --git a/packages/cli/src/modules/agents/agents-catalog.controller.ts b/packages/cli/src/modules/agents/agents-catalog.controller.ts index 09180041682..b5f97ed95e7 100644 --- a/packages/cli/src/modules/agents/agents-catalog.controller.ts +++ b/packages/cli/src/modules/agents/agents-catalog.controller.ts @@ -8,7 +8,7 @@ import type { AuthenticatedRequest } from '@n8n/db'; import { Get, Param, ProjectScope, Query, RestController } from '@n8n/decorators'; import type { Response } from 'express'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { AgentIntegrationPersistenceService } from './agent-integration-persistence.service'; import { AgentModelCatalogService } from './agent-model-catalog.service'; diff --git a/packages/cli/src/modules/agents/agents-config.controller.ts b/packages/cli/src/modules/agents/agents-config.controller.ts index b6e133ef461..38bc3da3b08 100644 --- a/packages/cli/src/modules/agents/agents-config.controller.ts +++ b/packages/cli/src/modules/agents/agents-config.controller.ts @@ -11,7 +11,7 @@ import { AgentRepository } from './repositories/agent.repository'; import { getAgentConfigHash } from './utils/agent-config-hash'; import { CollaborationService } from '@/collaboration/collaboration.service'; import { CredentialsService } from '@/credentials/credentials.service'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; @RestController('/projects/:projectId/agents/v2') export class AgentsConfigController { diff --git a/packages/cli/src/modules/agents/agents.controller.ts b/packages/cli/src/modules/agents/agents.controller.ts index b00ff42848d..97174d953d1 100644 --- a/packages/cli/src/modules/agents/agents.controller.ts +++ b/packages/cli/src/modules/agents/agents.controller.ts @@ -12,7 +12,7 @@ import { } from '@n8n/decorators'; import type { Response } from 'express'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { CollaborationService } from '@/collaboration/collaboration.service'; import { AgentRunnableStateService } from './agent-runnable-state.service'; diff --git a/packages/cli/src/modules/agents/agents.service.ts b/packages/cli/src/modules/agents/agents.service.ts index 273850ce73d..e36ec3daba9 100644 --- a/packages/cli/src/modules/agents/agents.service.ts +++ b/packages/cli/src/modules/agents/agents.service.ts @@ -22,7 +22,7 @@ import { v4 as uuid } from 'uuid'; // in this area (see `agents-credential-provider.ts`). Resolved lazily by DI. // eslint-disable-next-line import-x/no-cycle import { CredentialsService } from '@/credentials/credentials.service'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { getAgentOrThrow } from './utils/get-agent-or-throw'; diff --git a/packages/cli/src/modules/agents/builder/agents-builder-tools.service.ts b/packages/cli/src/modules/agents/builder/agents-builder-tools.service.ts index c31d9d84a2c..b5d381da65d 100644 --- a/packages/cli/src/modules/agents/builder/agents-builder-tools.service.ts +++ b/packages/cli/src/modules/agents/builder/agents-builder-tools.service.ts @@ -44,8 +44,7 @@ import { z } from 'zod'; import { CredentialTypes } from '@/credential-types'; import { CollaborationService } from '@/collaboration/collaboration.service'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { LockedError } from '@/errors/response-errors/locked.error'; +import { ConflictError, LockedError } from '@n8n/errors'; import { McpRegistryService } from '@/modules/mcp-registry/registry/mcp-registry.service'; import { NodeTypes } from '@/node-types'; import { OauthService } from '@/oauth/oauth.service'; diff --git a/packages/cli/src/modules/agents/builder/agents-builder.service.ts b/packages/cli/src/modules/agents/builder/agents-builder.service.ts index 9cfb9cbac04..178c186a6d6 100644 --- a/packages/cli/src/modules/agents/builder/agents-builder.service.ts +++ b/packages/cli/src/modules/agents/builder/agents-builder.service.ts @@ -27,7 +27,7 @@ import { type ReportRequiredArtifactInput, } from '@n8n/instance-ai'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { NodeCatalogService } from '@/node-catalog'; import { InstanceAiCreditService } from '../../instance-ai/instance-ai-credit.service'; diff --git a/packages/cli/src/modules/agents/instance-ai-agent-context.adapter.ts b/packages/cli/src/modules/agents/instance-ai-agent-context.adapter.ts index 7aa22be14ed..0b51074b40f 100644 --- a/packages/cli/src/modules/agents/instance-ai-agent-context.adapter.ts +++ b/packages/cli/src/modules/agents/instance-ai-agent-context.adapter.ts @@ -11,8 +11,7 @@ import { UserError } from 'n8n-workflow'; import { McpRegistryService } from '@/modules/mcp-registry/registry/mcp-registry.service'; import { userHasScopes } from '@/permissions.ee/check-access'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import { AgentExecutionService, diff --git a/packages/cli/src/modules/agents/instance-ai-builder-delegate.adapter.ts b/packages/cli/src/modules/agents/instance-ai-builder-delegate.adapter.ts index 63e128c8a7e..f75ee1fe1f1 100644 --- a/packages/cli/src/modules/agents/instance-ai-builder-delegate.adapter.ts +++ b/packages/cli/src/modules/agents/instance-ai-builder-delegate.adapter.ts @@ -13,7 +13,7 @@ import { type Scope } from '@n8n/permissions'; import { Like } from '@n8n/typeorm'; import { UserError } from 'n8n-workflow'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { userHasScopes } from '@/permissions.ee/check-access'; import { AgentConfigService } from './agent-config.service'; diff --git a/packages/cli/src/modules/agents/integrations/__tests__/discord-gateway-lifecycle.test.ts b/packages/cli/src/modules/agents/integrations/__tests__/discord-gateway-lifecycle.test.ts index a1ae14bdbb5..205a793e48f 100644 --- a/packages/cli/src/modules/agents/integrations/__tests__/discord-gateway-lifecycle.test.ts +++ b/packages/cli/src/modules/agents/integrations/__tests__/discord-gateway-lifecycle.test.ts @@ -3,7 +3,7 @@ import type { OutboundHttp } from '@n8n/backend-network'; import type { InstanceSettings } from 'n8n-core'; import { mock } from 'vitest-mock-extended'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError } from '@n8n/errors'; import type { AgentRepository } from '../../repositories/agent.repository'; import type { AgentChatIntegrationContext } from '../agent-chat-integration'; diff --git a/packages/cli/src/modules/agents/integrations/__tests__/discord-integration.test.ts b/packages/cli/src/modules/agents/integrations/__tests__/discord-integration.test.ts index 97c3d2d8847..c47c27451e0 100644 --- a/packages/cli/src/modules/agents/integrations/__tests__/discord-integration.test.ts +++ b/packages/cli/src/modules/agents/integrations/__tests__/discord-integration.test.ts @@ -3,8 +3,7 @@ import type { HttpRequestClient, OutboundHttp } from '@n8n/backend-network'; import type { InstanceSettings } from 'n8n-core'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { BadRequestError, ConflictError } from '@n8n/errors'; import type { Agent } from '../../entities/agent.entity'; import type { AgentRepository } from '../../repositories/agent.repository'; diff --git a/packages/cli/src/modules/agents/integrations/__tests__/slack-app-setup.service.test.ts b/packages/cli/src/modules/agents/integrations/__tests__/slack-app-setup.service.test.ts index 25c51b935e7..03a54ca041a 100644 --- a/packages/cli/src/modules/agents/integrations/__tests__/slack-app-setup.service.test.ts +++ b/packages/cli/src/modules/agents/integrations/__tests__/slack-app-setup.service.test.ts @@ -8,7 +8,7 @@ import type { Cipher } from 'n8n-core'; import type { CredentialsService } from '@/credentials/credentials.service'; import type { CredentialsFinderService } from '@/credentials/credentials-finder.service'; import type { CredentialsOverwrites } from '@/credentials-overwrites'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { CacheService } from '@/services/cache/cache.service'; import type { ProjectService } from '@/services/project.service.ee'; import type { UrlService } from '@n8n/backend-services'; diff --git a/packages/cli/src/modules/agents/integrations/__tests__/slack-integration.test.ts b/packages/cli/src/modules/agents/integrations/__tests__/slack-integration.test.ts index c28460baefb..e0b15f96b57 100644 --- a/packages/cli/src/modules/agents/integrations/__tests__/slack-integration.test.ts +++ b/packages/cli/src/modules/agents/integrations/__tests__/slack-integration.test.ts @@ -3,7 +3,7 @@ import type { AgentIntegrationConfig } from '@n8n/api-types'; import type { Thread } from 'chat'; import { mock } from 'vitest-mock-extended'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError } from '@n8n/errors'; import type { AgentRepository } from '../../repositories/agent.repository'; import type { AgentChatIntegrationContext } from '../agent-chat-integration'; diff --git a/packages/cli/src/modules/agents/integrations/__tests__/slack-manual-setup.service.test.ts b/packages/cli/src/modules/agents/integrations/__tests__/slack-manual-setup.service.test.ts index 1b5e9a1f5d0..1b3be78a270 100644 --- a/packages/cli/src/modules/agents/integrations/__tests__/slack-manual-setup.service.test.ts +++ b/packages/cli/src/modules/agents/integrations/__tests__/slack-manual-setup.service.test.ts @@ -3,7 +3,7 @@ import type { UserRepository } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; import type { CacheService } from '@/services/cache/cache.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { ProjectService } from '@/services/project.service.ee'; import { SlackManualSetupService } from '../platforms/slack/slack-manual-setup.service'; diff --git a/packages/cli/src/modules/agents/integrations/__tests__/teams-integration.test.ts b/packages/cli/src/modules/agents/integrations/__tests__/teams-integration.test.ts index bd2552770b1..63164db1e94 100644 --- a/packages/cli/src/modules/agents/integrations/__tests__/teams-integration.test.ts +++ b/packages/cli/src/modules/agents/integrations/__tests__/teams-integration.test.ts @@ -2,7 +2,7 @@ import type { Logger } from '@n8n/backend-common'; import { UserError } from 'n8n-workflow'; import { mock } from 'vitest-mock-extended'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError } from '@n8n/errors'; import type { Agent } from '../../entities/agent.entity'; import type { AgentRepository } from '../../repositories/agent.repository'; diff --git a/packages/cli/src/modules/agents/integrations/credential-claim.ts b/packages/cli/src/modules/agents/integrations/credential-claim.ts index 11975d228b2..7fbf738afd9 100644 --- a/packages/cli/src/modules/agents/integrations/credential-claim.ts +++ b/packages/cli/src/modules/agents/integrations/credential-claim.ts @@ -1,4 +1,4 @@ -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError } from '@n8n/errors'; import type { AgentChannelPreconditionContext } from './agent-chat-integration'; import type { AgentRepository } from '../repositories/agent.repository'; diff --git a/packages/cli/src/modules/agents/integrations/leader-channel-relay.service.ts b/packages/cli/src/modules/agents/integrations/leader-channel-relay.service.ts index eff67c479cb..fe56aac8a73 100644 --- a/packages/cli/src/modules/agents/integrations/leader-channel-relay.service.ts +++ b/packages/cli/src/modules/agents/integrations/leader-channel-relay.service.ts @@ -5,7 +5,7 @@ import type { DistributiveOmit } from '@n8n/utils/types'; import { InstanceSettings } from 'n8n-core'; import { nanoid } from 'nanoid'; -import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error'; +import { ServiceUnavailableError } from '@n8n/errors'; import { Publisher } from '@/scaling/pubsub/publisher.service'; import type { PubSubCommandMap } from '@/scaling/pubsub/pubsub.event-map'; diff --git a/packages/cli/src/modules/agents/integrations/platforms/__tests__/telegram/integration.test.ts b/packages/cli/src/modules/agents/integrations/platforms/__tests__/telegram/integration.test.ts index dd738d8ca38..68a268656dd 100644 --- a/packages/cli/src/modules/agents/integrations/platforms/__tests__/telegram/integration.test.ts +++ b/packages/cli/src/modules/agents/integrations/platforms/__tests__/telegram/integration.test.ts @@ -7,8 +7,7 @@ import { createHmac } from 'crypto'; import { mock } from 'vitest-mock-extended'; import type { InstanceSettings } from 'n8n-core'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { BadRequestError, ConflictError } from '@n8n/errors'; import type { UrlService } from '@n8n/backend-services'; import type { Agent } from '../../../../entities/agent.entity'; diff --git a/packages/cli/src/modules/agents/integrations/platforms/discord-integration.ts b/packages/cli/src/modules/agents/integrations/platforms/discord-integration.ts index 2ef436fd8c3..9d77d5de7b2 100644 --- a/packages/cli/src/modules/agents/integrations/platforms/discord-integration.ts +++ b/packages/cli/src/modules/agents/integrations/platforms/discord-integration.ts @@ -8,8 +8,7 @@ import type { Message, Thread } from 'chat'; import escapeRegExp from 'lodash/escapeRegExp'; import { InstanceSettings } from 'n8n-core'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { BadRequestError, ConflictError } from '@n8n/errors'; import { AgentRepository } from '../../repositories/agent.repository'; import { diff --git a/packages/cli/src/modules/agents/integrations/platforms/slack/slack-managed-setup.service.ts b/packages/cli/src/modules/agents/integrations/platforms/slack/slack-managed-setup.service.ts index 15d68f4339a..4ea87219d4e 100644 --- a/packages/cli/src/modules/agents/integrations/platforms/slack/slack-managed-setup.service.ts +++ b/packages/cli/src/modules/agents/integrations/platforms/slack/slack-managed-setup.service.ts @@ -19,8 +19,7 @@ import { join } from 'node:path'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; import { CredentialsService } from '@/credentials/credentials.service'; import { CredentialsOverwrites } from '@/credentials-overwrites'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { CacheService } from '@/services/cache/cache.service'; import { SlackMethodsService } from './slack-methods.service'; diff --git a/packages/cli/src/modules/agents/integrations/platforms/slack/slack-manual-setup.service.ts b/packages/cli/src/modules/agents/integrations/platforms/slack/slack-manual-setup.service.ts index 4952e30fec2..4eae3546d55 100644 --- a/packages/cli/src/modules/agents/integrations/platforms/slack/slack-manual-setup.service.ts +++ b/packages/cli/src/modules/agents/integrations/platforms/slack/slack-manual-setup.service.ts @@ -4,9 +4,7 @@ import { UserRepository } from '@n8n/db'; import { Service } from '@n8n/di'; import { randomBytes } from 'node:crypto'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ForbiddenError, NotFoundError } from '@n8n/errors'; import { CacheService } from '@/services/cache/cache.service'; import { ProjectService } from '@/services/project.service.ee'; diff --git a/packages/cli/src/modules/agents/integrations/platforms/slack/slack-methods.service.ts b/packages/cli/src/modules/agents/integrations/platforms/slack/slack-methods.service.ts index ab1f854dc8f..22bc97541e5 100644 --- a/packages/cli/src/modules/agents/integrations/platforms/slack/slack-methods.service.ts +++ b/packages/cli/src/modules/agents/integrations/platforms/slack/slack-methods.service.ts @@ -12,7 +12,7 @@ import { Cipher } from 'n8n-core'; import { jsonParse } from 'n8n-workflow'; import { CredentialsService } from '@/credentials/credentials.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { CacheService } from '@/services/cache/cache.service'; import { UrlService } from '@n8n/backend-services'; diff --git a/packages/cli/src/modules/agents/integrations/platforms/slack/slack-setup.types.ts b/packages/cli/src/modules/agents/integrations/platforms/slack/slack-setup.types.ts index 6a1a7ffc82b..94c1371794d 100644 --- a/packages/cli/src/modules/agents/integrations/platforms/slack/slack-setup.types.ts +++ b/packages/cli/src/modules/agents/integrations/platforms/slack/slack-setup.types.ts @@ -1,6 +1,6 @@ import { isRecord } from '@n8n/utils/is-record'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { stringProperty } from '../../integration-helpers'; diff --git a/packages/cli/src/modules/agents/integrations/platforms/teams/teams-setup.service.ts b/packages/cli/src/modules/agents/integrations/platforms/teams/teams-setup.service.ts index a615ede7523..c5d42bf9c22 100644 --- a/packages/cli/src/modules/agents/integrations/platforms/teams/teams-setup.service.ts +++ b/packages/cli/src/modules/agents/integrations/platforms/teams/teams-setup.service.ts @@ -2,8 +2,7 @@ import type { AgentTeamsIntegrationSettings, TeamsAgentSetupState } from '@n8n/a import type { User } from '@n8n/db'; import { Service } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { UrlService } from '@n8n/backend-services'; import { TeamsArmTemplateService } from './teams-arm-template.service'; diff --git a/packages/cli/src/modules/agents/integrations/platforms/telegram-integration.ts b/packages/cli/src/modules/agents/integrations/platforms/telegram-integration.ts index ddfd9c10689..b41ffd6d009 100644 --- a/packages/cli/src/modules/agents/integrations/platforms/telegram-integration.ts +++ b/packages/cli/src/modules/agents/integrations/platforms/telegram-integration.ts @@ -9,7 +9,7 @@ import { createHmac } from 'crypto'; import { InstanceSettings } from 'n8n-core'; import { UnexpectedError } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { UrlService } from '@n8n/backend-services'; import { AgentRepository } from '../../repositories/agent.repository'; diff --git a/packages/cli/src/modules/agents/sub-agents/__tests__/delegate-sub-agent-tool.test.ts b/packages/cli/src/modules/agents/sub-agents/__tests__/delegate-sub-agent-tool.test.ts index 0cc763bca4e..3d8594f9d58 100644 --- a/packages/cli/src/modules/agents/sub-agents/__tests__/delegate-sub-agent-tool.test.ts +++ b/packages/cli/src/modules/agents/sub-agents/__tests__/delegate-sub-agent-tool.test.ts @@ -10,7 +10,7 @@ import type { Mocked } from 'vitest'; import { mock } from 'vitest-mock-extended'; import { OperationalError, UserError } from 'n8n-workflow'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { AgentExecutionRecordingError } from '../../agent-execution-recording.error'; import { diff --git a/packages/cli/src/modules/agents/sub-agents/delegate-sub-agent-tool.ts b/packages/cli/src/modules/agents/sub-agents/delegate-sub-agent-tool.ts index 7f11fc5adc7..a538d7ebe8d 100644 --- a/packages/cli/src/modules/agents/sub-agents/delegate-sub-agent-tool.ts +++ b/packages/cli/src/modules/agents/sub-agents/delegate-sub-agent-tool.ts @@ -9,7 +9,7 @@ import { import type { SubAgentRunPolicy, SubAgentSource } from '@n8n/api-types'; import { OperationalError, UserError } from 'n8n-workflow'; -import { ResponseError } from '@/errors/response-errors/abstract/response.error'; +import { ResponseError } from '@n8n/errors'; import { AgentExecutionRecordingError } from '../agent-execution-recording.error'; import { decodeAgentSandboxHostMetadata } from '../agent-sandbox-principal'; diff --git a/packages/cli/src/modules/agents/sub-agents/sub-agent-source-resolver.ts b/packages/cli/src/modules/agents/sub-agents/sub-agent-source-resolver.ts index 108d7fc554e..9c92d290ffa 100644 --- a/packages/cli/src/modules/agents/sub-agents/sub-agent-source-resolver.ts +++ b/packages/cli/src/modules/agents/sub-agents/sub-agent-source-resolver.ts @@ -7,7 +7,7 @@ import { import { Service } from '@n8n/di'; import { UserError } from 'n8n-workflow'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { getAgentOrThrow } from '../utils/get-agent-or-throw'; import { AgentHistoryRepository } from '../repositories/agent-history.repository'; diff --git a/packages/cli/src/modules/agents/utils/agent-draft.utils.ts b/packages/cli/src/modules/agents/utils/agent-draft.utils.ts index cdf87293498..ecdced9246d 100644 --- a/packages/cli/src/modules/agents/utils/agent-draft.utils.ts +++ b/packages/cli/src/modules/agents/utils/agent-draft.utils.ts @@ -1,6 +1,6 @@ import { v4 as uuid } from 'uuid'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError } from '@n8n/errors'; import type { Agent } from '../entities/agent.entity'; import type { AgentRepository } from '../repositories/agent.repository'; diff --git a/packages/cli/src/modules/agents/utils/get-agent-or-throw.ts b/packages/cli/src/modules/agents/utils/get-agent-or-throw.ts index d4b3fb34f80..01caa6d3988 100644 --- a/packages/cli/src/modules/agents/utils/get-agent-or-throw.ts +++ b/packages/cli/src/modules/agents/utils/get-agent-or-throw.ts @@ -1,4 +1,4 @@ -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { Agent } from '../entities/agent.entity'; import type { AgentRepository } from '../repositories/agent.repository'; diff --git a/packages/cli/src/modules/breaking-changes/breaking-changes.controller.ts b/packages/cli/src/modules/breaking-changes/breaking-changes.controller.ts index 7332b2b5eb3..aa430091d06 100644 --- a/packages/cli/src/modules/breaking-changes/breaking-changes.controller.ts +++ b/packages/cli/src/modules/breaking-changes/breaking-changes.controller.ts @@ -10,7 +10,7 @@ import { AuthenticatedRequest } from '@n8n/db'; import { Get, RestController, GlobalScope, Query, Post, Param } from '@n8n/decorators'; import { Response } from 'express'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { BreakingChangeMigrationService } from './breaking-changes.migration.service'; import { BreakingChangeService } from './breaking-changes.service'; diff --git a/packages/cli/src/modules/breaking-changes/breaking-changes.migration.service.ts b/packages/cli/src/modules/breaking-changes/breaking-changes.migration.service.ts index c49f7a42232..57804559a17 100644 --- a/packages/cli/src/modules/breaking-changes/breaking-changes.migration.service.ts +++ b/packages/cli/src/modules/breaking-changes/breaking-changes.migration.service.ts @@ -5,8 +5,7 @@ import { Service } from '@n8n/di'; import { calculateWorkflowChecksum } from 'n8n-workflow'; import type { INode } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { NodeTypes } from '@/node-types'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { WorkflowValidationService } from '@/workflows/workflow-validation.service'; diff --git a/packages/cli/src/modules/breaking-changes/migrations/node-migration.ts b/packages/cli/src/modules/breaking-changes/migrations/node-migration.ts index d1c8a7cb64a..5e11ac5ef25 100644 --- a/packages/cli/src/modules/breaking-changes/migrations/node-migration.ts +++ b/packages/cli/src/modules/breaking-changes/migrations/node-migration.ts @@ -1,6 +1,6 @@ import type { IConnections, INode } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; /** * A migration refused a specific node. Carries the node identity in `meta` so the diff --git a/packages/cli/src/modules/chat-hub/__tests__/chat-hub-agent.service.test.ts b/packages/cli/src/modules/chat-hub/__tests__/chat-hub-agent.service.test.ts index f27e0e6bd69..09abac33f19 100644 --- a/packages/cli/src/modules/chat-hub/__tests__/chat-hub-agent.service.test.ts +++ b/packages/cli/src/modules/chat-hub/__tests__/chat-hub-agent.service.test.ts @@ -5,7 +5,7 @@ import type { IRunExecutionData, IWorkflowBase } from 'n8n-workflow'; import { v4 as uuid } from 'uuid'; import { mock } from 'vitest-mock-extended'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { DynamicNodeParametersService } from '@/services/dynamic-node-parameters.service'; import type { WorkflowExecutionService } from '@/workflows/workflow-execution.service'; diff --git a/packages/cli/src/modules/chat-hub/__tests__/chat-hub-credentials.service.test.ts b/packages/cli/src/modules/chat-hub/__tests__/chat-hub-credentials.service.test.ts index a25d1275aa2..7437c1bc729 100644 --- a/packages/cli/src/modules/chat-hub/__tests__/chat-hub-credentials.service.test.ts +++ b/packages/cli/src/modules/chat-hub/__tests__/chat-hub-credentials.service.test.ts @@ -11,8 +11,7 @@ import { mock } from 'vitest-mock-extended'; import type { CredentialsFinderService } from '@/credentials/credentials-finder.service'; import type { CredentialsService } from '@/credentials/credentials.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { ChatHubCredentialsService } from '../chat-hub-credentials.service'; diff --git a/packages/cli/src/modules/chat-hub/__tests__/chat-hub-settings.service.test.ts b/packages/cli/src/modules/chat-hub/__tests__/chat-hub-settings.service.test.ts index 2151675c6ae..5eb5d60dc40 100644 --- a/packages/cli/src/modules/chat-hub/__tests__/chat-hub-settings.service.test.ts +++ b/packages/cli/src/modules/chat-hub/__tests__/chat-hub-settings.service.test.ts @@ -3,7 +3,7 @@ import type { Settings, SettingsRepository } from '@n8n/db'; import type { EntityManager } from '@n8n/typeorm'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { ChatHubSettingsService } from '../chat-hub.settings.service'; diff --git a/packages/cli/src/modules/chat-hub/__tests__/chat-hub-tool.service.test.ts b/packages/cli/src/modules/chat-hub/__tests__/chat-hub-tool.service.test.ts index 09996a77200..23e0b88b24a 100644 --- a/packages/cli/src/modules/chat-hub/__tests__/chat-hub-tool.service.test.ts +++ b/packages/cli/src/modules/chat-hub/__tests__/chat-hub-tool.service.test.ts @@ -5,8 +5,7 @@ import type { INode, INodeType } from 'n8n-workflow'; import { v4 as uuid } from 'uuid'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import type { NodeTypes } from '@/node-types'; import type { ChatHubTool } from '../chat-hub-tool.entity'; diff --git a/packages/cli/src/modules/chat-hub/__tests__/chat-hub.attachment.service.test.ts b/packages/cli/src/modules/chat-hub/__tests__/chat-hub.attachment.service.test.ts index 4d43605c66d..e12d82d1187 100644 --- a/packages/cli/src/modules/chat-hub/__tests__/chat-hub.attachment.service.test.ts +++ b/packages/cli/src/modules/chat-hub/__tests__/chat-hub.attachment.service.test.ts @@ -2,7 +2,7 @@ import type { ChatAttachment } from '@n8n/api-types'; import type { BinaryDataService } from 'n8n-core'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { ChatHubAttachmentService } from '../chat-hub.attachment.service'; import type { ChatHubMessageRepository } from '../chat-message.repository'; diff --git a/packages/cli/src/modules/chat-hub/__tests__/chat-hub.controller.test.ts b/packages/cli/src/modules/chat-hub/__tests__/chat-hub.controller.test.ts index 790dd6a0ec6..fd04514a378 100644 --- a/packages/cli/src/modules/chat-hub/__tests__/chat-hub.controller.test.ts +++ b/packages/cli/src/modules/chat-hub/__tests__/chat-hub.controller.test.ts @@ -3,7 +3,7 @@ import type { ModuleSettings } from '@n8n/decorators'; import type { NextFunction, Request, Response } from 'express'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { sendErrorResponse } from '@/response-helper'; import { ChatHubController } from '../chat-hub.controller'; diff --git a/packages/cli/src/modules/chat-hub/chat-hub-agent.service.ts b/packages/cli/src/modules/chat-hub/chat-hub-agent.service.ts index 0a40e59d2e8..49e8580ed04 100644 --- a/packages/cli/src/modules/chat-hub/chat-hub-agent.service.ts +++ b/packages/cli/src/modules/chat-hub/chat-hub-agent.service.ts @@ -14,8 +14,7 @@ import { type IBinaryData } from 'n8n-workflow'; import { nanoid } from 'nanoid'; import { v4 as uuidv4 } from 'uuid'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { DynamicNodeParametersService } from '@/services/dynamic-node-parameters.service'; import { getBase } from '@/workflow-execute-additional-data'; import { WorkflowExecutionService } from '@/workflows/workflow-execution.service'; diff --git a/packages/cli/src/modules/chat-hub/chat-hub-credentials.service.ts b/packages/cli/src/modules/chat-hub/chat-hub-credentials.service.ts index 44ced674b80..f5831c2027d 100644 --- a/packages/cli/src/modules/chat-hub/chat-hub-credentials.service.ts +++ b/packages/cli/src/modules/chat-hub/chat-hub-credentials.service.ts @@ -10,8 +10,7 @@ import type { INodeCredentials } from 'n8n-workflow'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; import { CredentialsService } from '@/credentials/credentials.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; @Service() export class ChatHubCredentialsService { diff --git a/packages/cli/src/modules/chat-hub/chat-hub-execution.service.ts b/packages/cli/src/modules/chat-hub/chat-hub-execution.service.ts index 0497a305b01..149a58ff6e5 100644 --- a/packages/cli/src/modules/chat-hub/chat-hub-execution.service.ts +++ b/packages/cli/src/modules/chat-hub/chat-hub-execution.service.ts @@ -28,8 +28,7 @@ import { v4 as uuidv4 } from 'uuid'; import { ActiveExecutions } from '@/active-executions'; import { ChatExecutionManager } from '@/chat/chat-execution-manager'; import { ExecutionNotFoundError } from '@/errors/execution-not-found-error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; +import { BadRequestError, InternalServerError } from '@n8n/errors'; import { ExecutionPersistence } from '@/executions/execution-persistence'; import { ExecutionService } from '@/executions/execution.service'; import { WorkflowExecutionService } from '@/workflows/workflow-execution.service'; diff --git a/packages/cli/src/modules/chat-hub/chat-hub-title.service.ts b/packages/cli/src/modules/chat-hub/chat-hub-title.service.ts index aece99602ed..b0fbab8c127 100644 --- a/packages/cli/src/modules/chat-hub/chat-hub-title.service.ts +++ b/packages/cli/src/modules/chat-hub/chat-hub-title.service.ts @@ -22,7 +22,7 @@ import { UserError, } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { ExecutionPersistence } from '@/executions/execution-persistence'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; diff --git a/packages/cli/src/modules/chat-hub/chat-hub-tool.service.ts b/packages/cli/src/modules/chat-hub/chat-hub-tool.service.ts index f5f13417777..aa53bb1f6b6 100644 --- a/packages/cli/src/modules/chat-hub/chat-hub-tool.service.ts +++ b/packages/cli/src/modules/chat-hub/chat-hub-tool.service.ts @@ -9,8 +9,7 @@ import { Service } from '@n8n/di'; import type { INode } from 'n8n-workflow'; import { collectExpressionDefaults, findDisallowedChatToolExpressions } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { NodeTypes } from '@/node-types'; import type { ChatHubTool } from './chat-hub-tool.entity'; diff --git a/packages/cli/src/modules/chat-hub/chat-hub-workflow.service.ts b/packages/cli/src/modules/chat-hub/chat-hub-workflow.service.ts index e4abbfbb97d..ce6b4938f99 100644 --- a/packages/cli/src/modules/chat-hub/chat-hub-workflow.service.ts +++ b/packages/cli/src/modules/chat-hub/chat-hub-workflow.service.ts @@ -49,7 +49,7 @@ import { } from 'n8n-workflow'; import { v4 as uuidv4 } from 'uuid'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { PolicyEnforcementService } from '@/policy/policy-enforcement.service'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; diff --git a/packages/cli/src/modules/chat-hub/chat-hub.attachment.service.ts b/packages/cli/src/modules/chat-hub/chat-hub.attachment.service.ts index 266cadead83..ea8a22db6cb 100644 --- a/packages/cli/src/modules/chat-hub/chat-hub.attachment.service.ts +++ b/packages/cli/src/modules/chat-hub/chat-hub.attachment.service.ts @@ -7,8 +7,7 @@ import { BinaryDataService, FileLocation, TEMP_EXECUTION_ID } from 'n8n-core'; import { BINARY_ENCODING, type IBinaryData } from 'n8n-workflow'; import type Stream from 'node:stream'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { ChatHubMessageRepository } from './chat-message.repository'; diff --git a/packages/cli/src/modules/chat-hub/chat-hub.controller.ts b/packages/cli/src/modules/chat-hub/chat-hub.controller.ts index 417fafb0d11..d77ed8bf615 100644 --- a/packages/cli/src/modules/chat-hub/chat-hub.controller.ts +++ b/packages/cli/src/modules/chat-hub/chat-hub.controller.ts @@ -43,8 +43,7 @@ import { sanitizeFilename } from '@n8n/utils/files/sanitize-filename'; import type { NextFunction, Request, Response } from 'express'; import multer from 'multer'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { sendErrorResponse } from '@/response-helper'; import { ChatHubAgentService } from './chat-hub-agent.service'; diff --git a/packages/cli/src/modules/chat-hub/chat-hub.service.ts b/packages/cli/src/modules/chat-hub/chat-hub.service.ts index a40280d091c..e3111197e1b 100644 --- a/packages/cli/src/modules/chat-hub/chat-hub.service.ts +++ b/packages/cli/src/modules/chat-hub/chat-hub.service.ts @@ -27,8 +27,7 @@ import { } from 'n8n-workflow'; import { ChatExecutionManager } from '@/chat/chat-execution-manager'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { ExecutionPersistence } from '@/executions/execution-persistence'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; diff --git a/packages/cli/src/modules/chat-hub/chat-hub.settings.controller.ts b/packages/cli/src/modules/chat-hub/chat-hub.settings.controller.ts index 811aeefa00c..457d10af78b 100644 --- a/packages/cli/src/modules/chat-hub/chat-hub.settings.controller.ts +++ b/packages/cli/src/modules/chat-hub/chat-hub.settings.controller.ts @@ -9,7 +9,7 @@ import { ModuleRegistry, Logger } from '@n8n/backend-common'; import { type AuthenticatedRequest } from '@n8n/db'; import { Body, Get, Post, Put, RestController, GlobalScope, Param } from '@n8n/decorators'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { ChatHubSettingsService } from './chat-hub.settings.service'; diff --git a/packages/cli/src/modules/chat-hub/chat-hub.settings.service.ts b/packages/cli/src/modules/chat-hub/chat-hub.settings.service.ts index 4ddfe22d63d..32efe54f1b6 100644 --- a/packages/cli/src/modules/chat-hub/chat-hub.settings.service.ts +++ b/packages/cli/src/modules/chat-hub/chat-hub.settings.service.ts @@ -12,7 +12,7 @@ import type { EntityManager } from '@n8n/db'; import { Service } from '@n8n/di'; import { jsonParse } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { VECTOR_STORE_NODE_TYPE_MAP } from './chat-hub.constants'; import type { SemanticSearchOptions } from './chat-hub.types'; diff --git a/packages/cli/src/modules/chat-hub/chat-session.repository.ts b/packages/cli/src/modules/chat-hub/chat-session.repository.ts index 4be6ce91b5e..f007f81594a 100644 --- a/packages/cli/src/modules/chat-hub/chat-session.repository.ts +++ b/packages/cli/src/modules/chat-hub/chat-session.repository.ts @@ -2,7 +2,7 @@ import { BaseRepository, TransactionRunner } from '@n8n/db'; import { Service } from '@n8n/di'; import { DataSource, EntityManager } from '@n8n/typeorm'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { ChatHubSession, IChatHubSession } from './chat-hub-session.entity'; diff --git a/packages/cli/src/modules/community-packages/__tests__/community-packages.lifecycle.service.test.ts b/packages/cli/src/modules/community-packages/__tests__/community-packages.lifecycle.service.test.ts index 82b660b54c5..bb664280a8b 100644 --- a/packages/cli/src/modules/community-packages/__tests__/community-packages.lifecycle.service.test.ts +++ b/packages/cli/src/modules/community-packages/__tests__/community-packages.lifecycle.service.test.ts @@ -4,7 +4,7 @@ import type { InstanceSettingsLoaderConfig } from '@n8n/config'; import { mock } from 'vitest-mock-extended'; import { IncompatibleNodesApiVersionError } from '@/errors/response-errors/incompatible-nodes-api-version.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { Push } from '@/push'; diff --git a/packages/cli/src/modules/community-packages/community-packages.lifecycle.service.ts b/packages/cli/src/modules/community-packages/community-packages.lifecycle.service.ts index e24c8dda4d4..70689849fa4 100644 --- a/packages/cli/src/modules/community-packages/community-packages.lifecycle.service.ts +++ b/packages/cli/src/modules/community-packages/community-packages.lifecycle.service.ts @@ -10,10 +10,8 @@ import { STARTER_TEMPLATE_NAME, UNKNOWN_FAILURE_REASON, } from '@/constants'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError, InternalServerError, NotFoundError } from '@n8n/errors'; import { IncompatibleNodesApiVersionError } from '@/errors/response-errors/incompatible-nodes-api-version.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { EventService } from '@/events/event.service'; import type { UserLike } from '@/events/maps/relay.event-map'; import { Push } from '@/push'; diff --git a/packages/cli/src/modules/data-table/__tests__/multer-upload-middleware.test.ts b/packages/cli/src/modules/data-table/__tests__/multer-upload-middleware.test.ts index 0a39ef8181b..91ff72e0601 100644 --- a/packages/cli/src/modules/data-table/__tests__/multer-upload-middleware.test.ts +++ b/packages/cli/src/modules/data-table/__tests__/multer-upload-middleware.test.ts @@ -33,7 +33,7 @@ vi.mock('multer', () => { return { __esModule: true, default: multerMock }; }); -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { DataTableSizeValidator } from '../data-table-size-validator.service'; import type { DataTableRepository } from '../data-table.repository'; diff --git a/packages/cli/src/modules/data-table/data-table-permissions.ts b/packages/cli/src/modules/data-table/data-table-permissions.ts index a190b10ff55..bd0196ff00e 100644 --- a/packages/cli/src/modules/data-table/data-table-permissions.ts +++ b/packages/cli/src/modules/data-table/data-table-permissions.ts @@ -1,7 +1,7 @@ import type { User } from '@n8n/db'; import { RESPONSE_ERROR_MESSAGES } from '@/constants'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { userHasScopes } from '@/permissions.ee/check-access'; /** diff --git a/packages/cli/src/modules/data-table/data-table-proxy.service.ts b/packages/cli/src/modules/data-table/data-table-proxy.service.ts index 68783256d9a..d9e5f8080fb 100644 --- a/packages/cli/src/modules/data-table/data-table-proxy.service.ts +++ b/packages/cli/src/modules/data-table/data-table-proxy.service.ts @@ -25,7 +25,7 @@ import { NodeOperationError, } from 'n8n-workflow'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { userHasScopes } from '@/permissions.ee/check-access'; import { InstanceWriteAccessService } from '@/services/instance-write-access.service'; import { OwnershipService } from '@/services/ownership.service'; diff --git a/packages/cli/src/modules/data-table/data-table-uploads.controller.ts b/packages/cli/src/modules/data-table/data-table-uploads.controller.ts index 5b5005784e0..ea907056e61 100644 --- a/packages/cli/src/modules/data-table/data-table-uploads.controller.ts +++ b/packages/cli/src/modules/data-table/data-table-uploads.controller.ts @@ -2,7 +2,7 @@ import { Post, RestController } from '@n8n/decorators'; import { Container } from '@n8n/di'; import multer from 'multer'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { CsvParserService } from './csv-parser.service'; import { MulterUploadMiddleware } from './multer-upload-middleware'; diff --git a/packages/cli/src/modules/data-table/data-table.controller.ts b/packages/cli/src/modules/data-table/data-table.controller.ts index 5022fb4bc48..335e89123fc 100644 --- a/packages/cli/src/modules/data-table/data-table.controller.ts +++ b/packages/cli/src/modules/data-table/data-table.controller.ts @@ -29,12 +29,14 @@ import { import { NextFunction, Response } from 'express'; import { DataTableRowReturn } from 'n8n-workflow'; -import { ResponseError } from '@/errors/response-errors/abstract/response.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { + ResponseError, + BadRequestError, + ConflictError, + ForbiddenError, + InternalServerError, + NotFoundError, +} from '@n8n/errors'; import { InstanceWriteAccessService } from '@/services/instance-write-access.service'; import { ProjectService } from '@/services/project.service.ee'; diff --git a/packages/cli/src/modules/data-table/errors/data-table-column-not-found.error.ts b/packages/cli/src/modules/data-table/errors/data-table-column-not-found.error.ts index 8c59716dfc6..2ef9d29deca 100644 --- a/packages/cli/src/modules/data-table/errors/data-table-column-not-found.error.ts +++ b/packages/cli/src/modules/data-table/errors/data-table-column-not-found.error.ts @@ -1,4 +1,4 @@ -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; export class DataTableColumnNotFoundError extends NotFoundError { constructor(dataTableId: string, columnId: string) { diff --git a/packages/cli/src/modules/data-table/errors/data-table-not-found.error.ts b/packages/cli/src/modules/data-table/errors/data-table-not-found.error.ts index 83589c09573..f9e08338f5b 100644 --- a/packages/cli/src/modules/data-table/errors/data-table-not-found.error.ts +++ b/packages/cli/src/modules/data-table/errors/data-table-not-found.error.ts @@ -1,4 +1,4 @@ -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; export class DataTableNotFoundError extends NotFoundError { constructor(dataTableId: string) { diff --git a/packages/cli/src/modules/data-table/middleware/branch-write-access-middleware.ts b/packages/cli/src/modules/data-table/middleware/branch-write-access-middleware.ts index e83c364cd40..08eef3c6113 100644 --- a/packages/cli/src/modules/data-table/middleware/branch-write-access-middleware.ts +++ b/packages/cli/src/modules/data-table/middleware/branch-write-access-middleware.ts @@ -1,7 +1,7 @@ import { Container } from '@n8n/di'; import type { RequestHandler } from 'express'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { InstanceWriteAccessService } from '@/services/instance-write-access.service'; /** diff --git a/packages/cli/src/modules/data-table/multer-upload-middleware.ts b/packages/cli/src/modules/data-table/multer-upload-middleware.ts index 758da5e581d..55b8dc06c0c 100644 --- a/packages/cli/src/modules/data-table/multer-upload-middleware.ts +++ b/packages/cli/src/modules/data-table/multer-upload-middleware.ts @@ -9,7 +9,7 @@ import multer from 'multer'; import { nanoid } from 'nanoid'; import { extname } from 'path'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { DataTableSizeValidator } from './data-table-size-validator.service'; import { DataTableRepository } from './data-table.repository'; diff --git a/packages/cli/src/modules/data-table/utils/sql-utils.ts b/packages/cli/src/modules/data-table/utils/sql-utils.ts index 176156daa31..20dffcdc639 100644 --- a/packages/cli/src/modules/data-table/utils/sql-utils.ts +++ b/packages/cli/src/modules/data-table/utils/sql-utils.ts @@ -17,7 +17,7 @@ import type { } from 'n8n-workflow'; import { DATA_TABLE_SYSTEM_COLUMN_TYPE_MAP, UnexpectedError } from 'n8n-workflow'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { DataTableColumn } from '../data-table-column.entity'; import type { DataTableUserTableName } from '../data-table.types'; diff --git a/packages/cli/src/modules/dynamic-credentials.ee/__tests__/credential-resolvers.controller.test.ts b/packages/cli/src/modules/dynamic-credentials.ee/__tests__/credential-resolvers.controller.test.ts index 4f89e42fafb..6e857f46bc6 100644 --- a/packages/cli/src/modules/dynamic-credentials.ee/__tests__/credential-resolvers.controller.test.ts +++ b/packages/cli/src/modules/dynamic-credentials.ee/__tests__/credential-resolvers.controller.test.ts @@ -4,9 +4,7 @@ import type { Response } from 'express'; import { CREDENTIAL_BLANKING_VALUE } from 'n8n-workflow'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, InternalServerError, NotFoundError } from '@n8n/errors'; import { SYSTEM_RESOLVER_ID } from '../constants'; import { IdentifierValidationError } from '../credential-resolvers/identifiers/identifier-interface'; diff --git a/packages/cli/src/modules/dynamic-credentials.ee/__tests__/workflow-status.controller.test.ts b/packages/cli/src/modules/dynamic-credentials.ee/__tests__/workflow-status.controller.test.ts index 419cc8c01ef..0c7df837f2a 100644 --- a/packages/cli/src/modules/dynamic-credentials.ee/__tests__/workflow-status.controller.test.ts +++ b/packages/cli/src/modules/dynamic-credentials.ee/__tests__/workflow-status.controller.test.ts @@ -2,8 +2,7 @@ import type { Mocked } from 'vitest'; import { mock } from 'vitest-mock-extended'; import type { WorkflowEntity } from '@n8n/db'; import type { Request, Response } from 'express'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import type { DynamicCredentialCorsService } from '../services/dynamic-credential-cors.service'; import type { DynamicCredentialWebService } from '../services/dynamic-credential-web.service'; import { WorkflowStatusController } from '../workflow-status.controller'; diff --git a/packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers.controller.ts b/packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers.controller.ts index 8879734e900..4d05d2a47c8 100644 --- a/packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers.controller.ts +++ b/packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers.controller.ts @@ -25,9 +25,7 @@ import { } from '@n8n/decorators'; import { Response } from 'express'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, InternalServerError, NotFoundError } from '@n8n/errors'; import { collectSecretFieldNames, redactSecretConfig } from './config-redaction'; import { CredentialResolutionError } from './errors/credential-resolution.error'; diff --git a/packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/identifiers/__tests__/n8n-identifier.test.ts b/packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/identifiers/__tests__/n8n-identifier.test.ts index b9e50c91658..a36cc09ba30 100644 --- a/packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/identifiers/__tests__/n8n-identifier.test.ts +++ b/packages/cli/src/modules/dynamic-credentials.ee/credential-resolvers/identifiers/__tests__/n8n-identifier.test.ts @@ -5,7 +5,7 @@ import { CredentialResolverError } from '@n8n/decorators'; import { mock } from 'vitest-mock-extended'; import type { AuthService } from '@/auth/auth.service'; -import { AuthError } from '@/errors/response-errors/auth.error'; +import { AuthError } from '@n8n/errors'; import type { OAuthTokenVerifierProxy } from '@/services/oauth-token-verifier-proxy.service'; import { diff --git a/packages/cli/src/modules/dynamic-credentials.ee/dynamic-credentials.controller.ts b/packages/cli/src/modules/dynamic-credentials.ee/dynamic-credentials.controller.ts index 050c4ab044d..d15fb7d3adc 100644 --- a/packages/cli/src/modules/dynamic-credentials.ee/dynamic-credentials.controller.ts +++ b/packages/cli/src/modules/dynamic-credentials.ee/dynamic-credentials.controller.ts @@ -9,8 +9,7 @@ import { type ICredentialContext, jsonParse } from 'n8n-workflow'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; import { EnterpriseCredentialsService } from '@/credentials/credentials.service.ee'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { CreateCsrfStateData, OauthService } from '@/oauth/oauth.service'; import { UrlService } from '@n8n/backend-services'; diff --git a/packages/cli/src/modules/dynamic-credentials.ee/services/credential-resolver-workflow.service.ts b/packages/cli/src/modules/dynamic-credentials.ee/services/credential-resolver-workflow.service.ts index 8cc66b031d1..4aacd070bf0 100644 --- a/packages/cli/src/modules/dynamic-credentials.ee/services/credential-resolver-workflow.service.ts +++ b/packages/cli/src/modules/dynamic-credentials.ee/services/credential-resolver-workflow.service.ts @@ -5,8 +5,7 @@ import { Cipher } from 'n8n-core'; import { ICredentialContext, INode, isNodeWithWorkflowSelector, jsonParse } from 'n8n-workflow'; import { DynamicCredentialsProxy } from '@/credentials/dynamic-credentials-proxy'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { DynamicCredentialResolverRegistry } from './credential-resolver-registry.service'; diff --git a/packages/cli/src/modules/dynamic-credentials.ee/services/dynamic-credential-web.service.ts b/packages/cli/src/modules/dynamic-credentials.ee/services/dynamic-credential-web.service.ts index 83c4bc5a3c6..2fa969af325 100644 --- a/packages/cli/src/modules/dynamic-credentials.ee/services/dynamic-credential-web.service.ts +++ b/packages/cli/src/modules/dynamic-credentials.ee/services/dynamic-credential-web.service.ts @@ -6,7 +6,7 @@ import { ICredentialContext } from 'n8n-workflow'; import { z } from 'zod'; import { AuthService } from '@/auth/auth.service'; -import { UnauthenticatedError } from '@/errors/response-errors/unauthenticated.error'; +import { UnauthenticatedError } from '@n8n/errors'; class AuthSourceQuerySchema extends Z.class({ authSource: z.enum(['bearer', 'cookie']).optional(), diff --git a/packages/cli/src/modules/dynamic-credentials.ee/workflow-status.controller.ts b/packages/cli/src/modules/dynamic-credentials.ee/workflow-status.controller.ts index 417b598a9da..9a660e1a85b 100644 --- a/packages/cli/src/modules/dynamic-credentials.ee/workflow-status.controller.ts +++ b/packages/cli/src/modules/dynamic-credentials.ee/workflow-status.controller.ts @@ -6,8 +6,7 @@ import { Get, Options, RestController } from '@n8n/decorators'; import { Container } from '@n8n/di'; import { Request, Response } from 'express'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { UrlService } from '@n8n/backend-services'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; diff --git a/packages/cli/src/modules/encryption-key-manager/__tests__/encryption-key.controller.test.ts b/packages/cli/src/modules/encryption-key-manager/__tests__/encryption-key.controller.test.ts index 8bcdf5ec3f6..e319c477242 100644 --- a/packages/cli/src/modules/encryption-key-manager/__tests__/encryption-key.controller.test.ts +++ b/packages/cli/src/modules/encryption-key-manager/__tests__/encryption-key.controller.test.ts @@ -2,7 +2,7 @@ import { mockInstance } from '@n8n/backend-test-utils'; import type { DeploymentKey } from '@n8n/db'; import { Container } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { EncryptionKeyController } from '@/modules/encryption-key-manager/encryption-key.controller'; import { KeyManagerService } from '@/encryption/key-manager.service'; diff --git a/packages/cli/src/modules/encryption-key-manager/encryption-key.controller.ts b/packages/cli/src/modules/encryption-key-manager/encryption-key.controller.ts index 797d947210a..4a921248a3e 100644 --- a/packages/cli/src/modules/encryption-key-manager/encryption-key.controller.ts +++ b/packages/cli/src/modules/encryption-key-manager/encryption-key.controller.ts @@ -7,7 +7,7 @@ import { import { type DeploymentKey } from '@n8n/db'; import { Body, Get, GlobalScope, Post, Query, RestController } from '@n8n/decorators'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { KeyManagerService } from '@/encryption/key-manager.service'; diff --git a/packages/cli/src/modules/engine-v2/__tests__/engine-credentials.controller.test.ts b/packages/cli/src/modules/engine-v2/__tests__/engine-credentials.controller.test.ts index 9d511f15e11..6e0e9c5599f 100644 --- a/packages/cli/src/modules/engine-v2/__tests__/engine-credentials.controller.test.ts +++ b/packages/cli/src/modules/engine-v2/__tests__/engine-credentials.controller.test.ts @@ -2,8 +2,7 @@ import type { Request, Response } from 'express'; import type { Mocked } from 'vitest'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import type { ResolveCredentialRequest } from '../engine-credentials.contract'; import { EngineCredentialsController } from '../engine-credentials.controller'; diff --git a/packages/cli/src/modules/engine-v2/__tests__/engine-credentials.service.test.ts b/packages/cli/src/modules/engine-v2/__tests__/engine-credentials.service.test.ts index 695ea28620e..273aac57f9b 100644 --- a/packages/cli/src/modules/engine-v2/__tests__/engine-credentials.service.test.ts +++ b/packages/cli/src/modules/engine-v2/__tests__/engine-credentials.service.test.ts @@ -4,8 +4,7 @@ import { mock } from 'vitest-mock-extended'; import type { CredentialsHelper } from '@/credentials-helper'; import { CredentialNotFoundError } from '@/errors/credential-not-found.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import type { CredentialsPermissionChecker } from '@/executions/pre-execution-checks'; import type { ResolveCredentialRequest } from '../engine-credentials.contract'; diff --git a/packages/cli/src/modules/engine-v2/__tests__/engine-lifecycle-event.controller.test.ts b/packages/cli/src/modules/engine-v2/__tests__/engine-lifecycle-event.controller.test.ts index 750717366c7..69e51d12885 100644 --- a/packages/cli/src/modules/engine-v2/__tests__/engine-lifecycle-event.controller.test.ts +++ b/packages/cli/src/modules/engine-v2/__tests__/engine-lifecycle-event.controller.test.ts @@ -3,7 +3,7 @@ import type { Request, Response } from 'express'; import type { Mocked } from 'vitest'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { EngineLifecycleEventPushRelay } from '../engine-lifecycle-event-push-relay'; import { EngineLifecycleEventController } from '../engine-lifecycle-event.controller'; diff --git a/packages/cli/src/modules/engine-v2/engine-control-plane-auth.middleware.ts b/packages/cli/src/modules/engine-v2/engine-control-plane-auth.middleware.ts index b73933447a4..e0d171abcc2 100644 --- a/packages/cli/src/modules/engine-v2/engine-control-plane-auth.middleware.ts +++ b/packages/cli/src/modules/engine-v2/engine-control-plane-auth.middleware.ts @@ -4,7 +4,7 @@ import type { ActionScope } from '@n8n/engine'; import { InvalidActionTokenError, verifyActionToken } from '@n8n/engine'; import type { NextFunction, Request, Response } from 'express'; -import { UnauthenticatedError } from '@/errors/response-errors/unauthenticated.error'; +import { UnauthenticatedError } from '@n8n/errors'; const BEARER_PREFIX = /^bearer /i; diff --git a/packages/cli/src/modules/engine-v2/engine-credentials.controller.ts b/packages/cli/src/modules/engine-v2/engine-credentials.controller.ts index 3f05835ddb1..86b81893f19 100644 --- a/packages/cli/src/modules/engine-v2/engine-credentials.controller.ts +++ b/packages/cli/src/modules/engine-v2/engine-credentials.controller.ts @@ -1,7 +1,7 @@ import { Service } from '@n8n/di'; import type { Request, Response } from 'express'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { ResolveCredentialResponse } from './engine-credentials.contract'; import { resolveCredentialRequestSchema } from './engine-credentials.contract'; diff --git a/packages/cli/src/modules/engine-v2/engine-credentials.service.ts b/packages/cli/src/modules/engine-v2/engine-credentials.service.ts index 79fdfb9bb65..31cdaef8b3e 100644 --- a/packages/cli/src/modules/engine-v2/engine-credentials.service.ts +++ b/packages/cli/src/modules/engine-v2/engine-credentials.service.ts @@ -4,8 +4,7 @@ import type { ICredentialDataDecryptedObject, IExecuteData } from 'n8n-workflow' import { CredentialsHelper } from '@/credentials-helper'; import { CredentialNotFoundError } from '@/errors/credential-not-found.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import { CredentialsPermissionChecker } from '@/executions/pre-execution-checks'; import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data'; diff --git a/packages/cli/src/modules/engine-v2/engine-lifecycle-event.controller.ts b/packages/cli/src/modules/engine-v2/engine-lifecycle-event.controller.ts index e62c52ea073..83fa93c5852 100644 --- a/packages/cli/src/modules/engine-v2/engine-lifecycle-event.controller.ts +++ b/packages/cli/src/modules/engine-v2/engine-lifecycle-event.controller.ts @@ -2,7 +2,7 @@ import { Service } from '@n8n/di'; import { lifecycleEventBatchSchema } from '@n8n/engine'; import type { Request, Response } from 'express'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { EngineLifecycleEventPushRelay } from './engine-lifecycle-event-push-relay'; diff --git a/packages/cli/src/modules/external-secrets.ee/__tests__/secret-provider-access-check.service.ee.test.ts b/packages/cli/src/modules/external-secrets.ee/__tests__/secret-provider-access-check.service.ee.test.ts index 8419fde0b06..67086e15539 100644 --- a/packages/cli/src/modules/external-secrets.ee/__tests__/secret-provider-access-check.service.ee.test.ts +++ b/packages/cli/src/modules/external-secrets.ee/__tests__/secret-provider-access-check.service.ee.test.ts @@ -6,8 +6,7 @@ import type { import type { Scope } from '@n8n/permissions'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import type { ProjectService } from '@/services/project.service.ee'; import type { RoleService } from '@/services/role.service'; diff --git a/packages/cli/src/modules/external-secrets.ee/__tests__/secrets-providers-connections.controller.ee.test.ts b/packages/cli/src/modules/external-secrets.ee/__tests__/secrets-providers-connections.controller.ee.test.ts index 965e59dea30..47e269b45dd 100644 --- a/packages/cli/src/modules/external-secrets.ee/__tests__/secrets-providers-connections.controller.ee.test.ts +++ b/packages/cli/src/modules/external-secrets.ee/__tests__/secrets-providers-connections.controller.ee.test.ts @@ -2,7 +2,7 @@ import type { Logger } from '@n8n/backend-common'; import type { NextFunction, Request, Response } from 'express'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import * as responseHelper from '@/response-helper'; import { ExternalSecretsConfig } from '../external-secrets.config'; diff --git a/packages/cli/src/modules/external-secrets.ee/__tests__/secrets-providers-connections.service.ee.test.ts b/packages/cli/src/modules/external-secrets.ee/__tests__/secrets-providers-connections.service.ee.test.ts index 45d1f1749b0..820f7d714e2 100644 --- a/packages/cli/src/modules/external-secrets.ee/__tests__/secrets-providers-connections.service.ee.test.ts +++ b/packages/cli/src/modules/external-secrets.ee/__tests__/secrets-providers-connections.service.ee.test.ts @@ -10,7 +10,7 @@ import { CREDENTIAL_BLANKING_VALUE, type IDataObject, type INodeProperties } fro import { mock } from 'vitest-mock-extended'; import type { CredentialDependencyService } from '@/credentials/credential-dependency.service'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { ExternalSecretsManager } from '@/modules/external-secrets.ee/external-secrets-manager.ee'; import type { ExternalSecretsProviderRegistry } from '@/modules/external-secrets.ee/provider-registry.service'; diff --git a/packages/cli/src/modules/external-secrets.ee/external-secrets-manager.ee.ts b/packages/cli/src/modules/external-secrets.ee/external-secrets-manager.ee.ts index 1513896fae3..7bdc08b0a88 100644 --- a/packages/cli/src/modules/external-secrets.ee/external-secrets-manager.ee.ts +++ b/packages/cli/src/modules/external-secrets.ee/external-secrets-manager.ee.ts @@ -6,7 +6,7 @@ import { Service } from '@n8n/di'; import { Cipher, type IExternalSecretsManager } from 'n8n-core'; import { jsonParse, UnexpectedError, type IDataObject, type INodeProperties } from 'n8n-workflow'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { Publisher } from '@/scaling/pubsub/publisher.service'; diff --git a/packages/cli/src/modules/external-secrets.ee/external-secrets-settings.controller.ee.ts b/packages/cli/src/modules/external-secrets.ee/external-secrets-settings.controller.ee.ts index 38c700de0fe..2541ee8313e 100644 --- a/packages/cli/src/modules/external-secrets.ee/external-secrets-settings.controller.ee.ts +++ b/packages/cli/src/modules/external-secrets.ee/external-secrets-settings.controller.ee.ts @@ -4,7 +4,7 @@ import type { AuthenticatedRequest } from '@n8n/db'; import { Body, GlobalScope, Middleware, Post, RestController } from '@n8n/decorators'; import type { NextFunction, Request, Response } from 'express'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { sendErrorResponse } from '@/response-helper'; diff --git a/packages/cli/src/modules/external-secrets.ee/external-secrets.controller.ee.ts b/packages/cli/src/modules/external-secrets.ee/external-secrets.controller.ee.ts index e366ed1e459..d551246d02a 100644 --- a/packages/cli/src/modules/external-secrets.ee/external-secrets.controller.ee.ts +++ b/packages/cli/src/modules/external-secrets.ee/external-secrets.controller.ee.ts @@ -2,7 +2,7 @@ import { Logger } from '@n8n/backend-common'; import { Get, Post, RestController, GlobalScope, Middleware } from '@n8n/decorators'; import { Request, Response, NextFunction } from 'express'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { ExternalSecretsProviders } from './external-secrets-providers.ee'; import { ExternalSecretsService } from './external-secrets.service.ee'; diff --git a/packages/cli/src/modules/external-secrets.ee/secret-provider-access-check.service.ee.ts b/packages/cli/src/modules/external-secrets.ee/secret-provider-access-check.service.ee.ts index 76003613cdc..59f96075132 100644 --- a/packages/cli/src/modules/external-secrets.ee/secret-provider-access-check.service.ee.ts +++ b/packages/cli/src/modules/external-secrets.ee/secret-provider-access-check.service.ee.ts @@ -7,8 +7,7 @@ import { Service } from '@n8n/di'; import type { Scope } from '@n8n/permissions'; import { combineScopes, getAuthPrincipalScopes, hasGlobalScope } from '@n8n/permissions'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import { ProjectService } from '@/services/project.service.ee'; import { RoleService } from '@/services/role.service'; diff --git a/packages/cli/src/modules/external-secrets.ee/secrets-providers-completions.controller.ee.ts b/packages/cli/src/modules/external-secrets.ee/secrets-providers-completions.controller.ee.ts index 80f831dccaf..84a855c64d4 100644 --- a/packages/cli/src/modules/external-secrets.ee/secrets-providers-completions.controller.ee.ts +++ b/packages/cli/src/modules/external-secrets.ee/secrets-providers-completions.controller.ee.ts @@ -4,7 +4,7 @@ import type { AuthenticatedRequest } from '@n8n/db'; import { Get, GlobalScope, Middleware, Param, ProjectScope, RestController } from '@n8n/decorators'; import type { NextFunction, Request, Response } from 'express'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { sendErrorResponse } from '@/response-helper'; import { ExternalSecretsConfig } from './external-secrets.config'; diff --git a/packages/cli/src/modules/external-secrets.ee/secrets-providers-connections.controller.ee.ts b/packages/cli/src/modules/external-secrets.ee/secrets-providers-connections.controller.ee.ts index 49edfc8bacf..8dc92bf8ba3 100644 --- a/packages/cli/src/modules/external-secrets.ee/secrets-providers-connections.controller.ee.ts +++ b/packages/cli/src/modules/external-secrets.ee/secrets-providers-connections.controller.ee.ts @@ -21,7 +21,7 @@ import { } from '@n8n/decorators'; import type { NextFunction, Request, Response } from 'express'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { sendErrorResponse } from '@/response-helper'; import { ExternalSecretsConfig } from './external-secrets.config'; diff --git a/packages/cli/src/modules/external-secrets.ee/secrets-providers-connections.service.ee.ts b/packages/cli/src/modules/external-secrets.ee/secrets-providers-connections.service.ee.ts index ce53472e60e..95b881487db 100644 --- a/packages/cli/src/modules/external-secrets.ee/secrets-providers-connections.service.ee.ts +++ b/packages/cli/src/modules/external-secrets.ee/secrets-providers-connections.service.ee.ts @@ -25,8 +25,7 @@ import { CredentialDependencyService, EXTERNAL_SECRET_PROVIDER_DEPENDENCY_TYPE, } from '@/credentials/credential-dependency.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import type { ProjectSummary } from '@/events/maps/relay.event-map'; import { ExternalSecretsManager } from '@/modules/external-secrets.ee/external-secrets-manager.ee'; diff --git a/packages/cli/src/modules/external-secrets.ee/secrets-providers-project.controller.ee.ts b/packages/cli/src/modules/external-secrets.ee/secrets-providers-project.controller.ee.ts index 03f8ff427b9..0909c0e2f1e 100644 --- a/packages/cli/src/modules/external-secrets.ee/secrets-providers-project.controller.ee.ts +++ b/packages/cli/src/modules/external-secrets.ee/secrets-providers-project.controller.ee.ts @@ -20,7 +20,7 @@ import { } from '@n8n/decorators'; import type { NextFunction, Request, Response } from 'express'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { sendErrorResponse } from '@/response-helper'; import { ExternalSecretsConfig } from './external-secrets.config'; diff --git a/packages/cli/src/modules/external-secrets.ee/secrets-providers-types.controller.ee.ts b/packages/cli/src/modules/external-secrets.ee/secrets-providers-types.controller.ee.ts index 754716ac022..8f032139513 100644 --- a/packages/cli/src/modules/external-secrets.ee/secrets-providers-types.controller.ee.ts +++ b/packages/cli/src/modules/external-secrets.ee/secrets-providers-types.controller.ee.ts @@ -4,8 +4,7 @@ import type { AuthenticatedRequest } from '@n8n/db'; import { Get, Middleware, Param, RestController } from '@n8n/decorators'; import type { NextFunction, Request, Response } from 'express'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import { sendErrorResponse } from '@/response-helper'; import { ExternalSecretsProviders } from './external-secrets-providers.ee'; diff --git a/packages/cli/src/modules/favorites/__tests__/favorites.controller.test.ts b/packages/cli/src/modules/favorites/__tests__/favorites.controller.test.ts index c23a6a54822..e7ba2fdf110 100644 --- a/packages/cli/src/modules/favorites/__tests__/favorites.controller.test.ts +++ b/packages/cli/src/modules/favorites/__tests__/favorites.controller.test.ts @@ -1,7 +1,7 @@ import type { AuthenticatedRequest } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { UserFavorite } from '../database/entities/user-favorite.entity'; import { FavoritesController } from '../favorites.controller'; diff --git a/packages/cli/src/modules/favorites/__tests__/favorites.service.test.ts b/packages/cli/src/modules/favorites/__tests__/favorites.service.test.ts index ca39a15eb2a..30abe23df55 100644 --- a/packages/cli/src/modules/favorites/__tests__/favorites.service.test.ts +++ b/packages/cli/src/modules/favorites/__tests__/favorites.service.test.ts @@ -9,8 +9,7 @@ import { } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import type { UserFavorite } from '../database/entities/user-favorite.entity'; import type { UserFavoriteRepository } from '../database/repositories/user-favorite.repository'; diff --git a/packages/cli/src/modules/favorites/favorites.controller.ts b/packages/cli/src/modules/favorites/favorites.controller.ts index 8ef9f8f23f9..652104ae6cf 100644 --- a/packages/cli/src/modules/favorites/favorites.controller.ts +++ b/packages/cli/src/modules/favorites/favorites.controller.ts @@ -2,7 +2,7 @@ import { FAVORITE_RESOURCE_TYPES, type FavoriteResourceType } from '@n8n/api-typ import type { AuthenticatedRequest } from '@n8n/db'; import { Body, Delete, Get, Param, Post, RestController } from '@n8n/decorators'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { AddFavoriteDto } from './dto/add-favorite.dto'; import { FavoritesService } from './favorites.service'; diff --git a/packages/cli/src/modules/favorites/favorites.service.ts b/packages/cli/src/modules/favorites/favorites.service.ts index a7de5200a80..7ba958e591c 100644 --- a/packages/cli/src/modules/favorites/favorites.service.ts +++ b/packages/cli/src/modules/favorites/favorites.service.ts @@ -11,8 +11,7 @@ import { Service } from '@n8n/di'; import { hasGlobalScope } from '@n8n/permissions'; import { In } from '@n8n/typeorm'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { UserFavoriteRepository } from './database/repositories/user-favorite.repository'; import type { diff --git a/packages/cli/src/modules/insights/__tests__/insights.controller.test.ts b/packages/cli/src/modules/insights/__tests__/insights.controller.test.ts index 0023e60789c..55e4fd8365c 100644 --- a/packages/cli/src/modules/insights/__tests__/insights.controller.test.ts +++ b/packages/cli/src/modules/insights/__tests__/insights.controller.test.ts @@ -5,8 +5,7 @@ import { Container } from '@n8n/di'; import { DateTime } from 'luxon'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { WorkflowSharingService } from '@/workflows/workflow-sharing.service'; import { TypeToNumber, type TypeUnitNumber } from '../database/entities/insights-shared'; diff --git a/packages/cli/src/modules/insights/__tests__/insights.service.integration.test.ts b/packages/cli/src/modules/insights/__tests__/insights.service.integration.test.ts index 4f9a96edc2a..0d7eb16d860 100644 --- a/packages/cli/src/modules/insights/__tests__/insights.service.integration.test.ts +++ b/packages/cli/src/modules/insights/__tests__/insights.service.integration.test.ts @@ -18,7 +18,7 @@ import type { MockInstance, Mocked } from 'vitest'; import type { MockProxy } from 'vitest-mock-extended'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { WorkflowSharingService } from '@/workflows/workflow-sharing.service'; import { createMember } from '@test-integration/db/users'; diff --git a/packages/cli/src/modules/insights/insights.controller.ts b/packages/cli/src/modules/insights/insights.controller.ts index 057a1fa92ab..08eedf454ab 100644 --- a/packages/cli/src/modules/insights/insights.controller.ts +++ b/packages/cli/src/modules/insights/insights.controller.ts @@ -11,9 +11,7 @@ import { DateTime } from 'luxon'; import { UserError } from 'n8n-workflow'; import { z } from 'zod'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; +import { BadRequestError, ForbiddenError, InternalServerError } from '@n8n/errors'; import { InsightsService } from './insights.service'; diff --git a/packages/cli/src/modules/insights/insights.service.ts b/packages/cli/src/modules/insights/insights.service.ts index 71d051fc275..14869f389f5 100644 --- a/packages/cli/src/modules/insights/insights.service.ts +++ b/packages/cli/src/modules/insights/insights.service.ts @@ -10,7 +10,7 @@ import { DateTime } from 'luxon'; import { InstanceSettings } from 'n8n-core'; import { UserError } from 'n8n-workflow'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { userHasScopes } from '@/permissions.ee/check-access'; import { WorkflowSharingService } from '@/workflows/workflow-sharing.service'; diff --git a/packages/cli/src/modules/instance-ai/__tests__/instance-ai-pending-agent.service.test.ts b/packages/cli/src/modules/instance-ai/__tests__/instance-ai-pending-agent.service.test.ts index 573b39c22e8..62620cc1f71 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/instance-ai-pending-agent.service.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/instance-ai-pending-agent.service.test.ts @@ -4,8 +4,7 @@ import type { User } from '@n8n/db'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import { AgentDefaultModelResolverService } from '@/modules/agents/agent-default-model-resolver.service'; import { AgentRunnableStateService } from '@/modules/agents/agent-runnable-state.service'; import { AgentsService } from '@/modules/agents/agents.service'; diff --git a/packages/cli/src/modules/instance-ai/__tests__/instance-ai-settings.service.test.ts b/packages/cli/src/modules/instance-ai/__tests__/instance-ai-settings.service.test.ts index e14f7c7ba02..82509f75e53 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/instance-ai-settings.service.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/instance-ai-settings.service.test.ts @@ -12,7 +12,7 @@ import type { import { Container } from '@n8n/di'; import { mock } from 'vitest-mock-extended'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { UnprocessableRequestError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { AiService } from '@/services/ai.service'; import { diff --git a/packages/cli/src/modules/instance-ai/__tests__/instance-ai-test.controller.test.ts b/packages/cli/src/modules/instance-ai/__tests__/instance-ai-test.controller.test.ts index 412a1b7b31d..e3151ed8609 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/instance-ai-test.controller.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/instance-ai-test.controller.test.ts @@ -15,7 +15,7 @@ import type { ProjectRepository, UserRepository, WorkflowRepository } from '@n8n import type { Request, Response } from 'express'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { InstanceAiMemoryService } from '../instance-ai-memory.service'; import { InstanceAiTestController } from '../instance-ai-test.controller'; diff --git a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.test.ts b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.test.ts index e8e282db8c1..cc1c5ac8c32 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.test.ts @@ -1817,12 +1817,14 @@ import { } from '@n8n/instance-ai/errors'; import type { WorkflowService } from '@/workflows/workflow.service'; import { AiPreferenceScopeFullError } from '@/errors/response-errors/ai-preference-scope-full.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; -import { LockedError } from '@/errors/response-errors/locked.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { + BadRequestError, + ConflictError, + ForbiddenError, + InternalServerError, + LockedError, + NotFoundError, +} from '@n8n/errors'; import type { License } from '@/license'; import type { AiPreferenceService } from '@/services/ai-preference.service'; import type { RoleService } from '@/services/role.service'; diff --git a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.controller.test.ts b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.controller.test.ts index 82a3b97823b..2adfa28ddc8 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.controller.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.controller.test.ts @@ -72,10 +72,7 @@ import type { Request, Response } from 'express'; import { UserError } from 'n8n-workflow'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from '@n8n/errors'; import type { CredentialsService } from '@/credentials/credentials.service'; import type { Push } from '@/push'; import type { Publisher } from '@/scaling/pubsub/publisher.service'; diff --git a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.service.test.ts b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.service.test.ts index 2efe3257042..8dd5e76b4c1 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.service.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.service.test.ts @@ -288,7 +288,7 @@ import { UserError } from 'n8n-workflow'; import type { Mock, MockedFunction } from 'vitest'; import { InstanceAiBuilderDelegateAdapterService } from '@/modules/agents/instance-ai-builder-delegate.adapter'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { userHasScopes } from '@/permissions.ee/check-access'; import { AI_PREFERENCES_CLEARED_BLOCK, diff --git a/packages/cli/src/modules/instance-ai/__tests__/onboarding.test.ts b/packages/cli/src/modules/instance-ai/__tests__/onboarding.test.ts index 1995bc411df..b0281da2d97 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/onboarding.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/onboarding.test.ts @@ -4,7 +4,7 @@ import { TELEMETRY_EVENT } from '@n8n/telemetry'; import { describe, expect, it } from 'vitest'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { Telemetry } from '@/telemetry'; import type { InstanceAiPendingConfirmation } from '../entities/instance-ai-pending-confirmation.entity'; diff --git a/packages/cli/src/modules/instance-ai/eval/__tests__/thread-restore.service.test.ts b/packages/cli/src/modules/instance-ai/eval/__tests__/thread-restore.service.test.ts index 15c0a9a4860..ed34ea19ec5 100644 --- a/packages/cli/src/modules/instance-ai/eval/__tests__/thread-restore.service.test.ts +++ b/packages/cli/src/modules/instance-ai/eval/__tests__/thread-restore.service.test.ts @@ -15,7 +15,7 @@ import type { PolicyCleared, PolicyViolation } from '@n8n/decorators'; import type { EntityManager } from '@n8n/typeorm'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { AgentsService } from '@/modules/agents/agents.service'; import type { DataTable } from '@/modules/data-table/data-table.entity'; import type { DataTableService } from '@/modules/data-table/data-table.service'; diff --git a/packages/cli/src/modules/instance-ai/eval/thread-restore.service.ts b/packages/cli/src/modules/instance-ai/eval/thread-restore.service.ts index 3ec99f55bef..677fe6e4480 100644 --- a/packages/cli/src/modules/instance-ai/eval/thread-restore.service.ts +++ b/packages/cli/src/modules/instance-ai/eval/thread-restore.service.ts @@ -26,7 +26,7 @@ import { } from 'n8n-workflow'; import { randomUUID } from 'node:crypto'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { AgentsService } from '@/modules/agents/agents.service'; import { DataTableService } from '@/modules/data-table/data-table.service'; import { PolicyEnforcementService } from '@/policy/policy-enforcement.service'; diff --git a/packages/cli/src/modules/instance-ai/instance-ai-memory.service.ts b/packages/cli/src/modules/instance-ai/instance-ai-memory.service.ts index 59769a445c2..195c67d51d6 100644 --- a/packages/cli/src/modules/instance-ai/instance-ai-memory.service.ts +++ b/packages/cli/src/modules/instance-ai/instance-ai-memory.service.ts @@ -26,9 +26,7 @@ import { type AgentTreeSnapshot, } from '@n8n/instance-ai'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ForbiddenError, NotFoundError } from '@n8n/errors'; import type { InstanceAiCheckpoint } from './entities/instance-ai-checkpoint.entity'; import { DurableLogMetrics } from './event-bus/durable-log-metrics'; diff --git a/packages/cli/src/modules/instance-ai/instance-ai-pending-agent.service.ts b/packages/cli/src/modules/instance-ai/instance-ai-pending-agent.service.ts index 4efb284cc1e..459a7afd766 100644 --- a/packages/cli/src/modules/instance-ai/instance-ai-pending-agent.service.ts +++ b/packages/cli/src/modules/instance-ai/instance-ai-pending-agent.service.ts @@ -4,8 +4,7 @@ import { Container, Service } from '@n8n/di'; import { threadAuthorizesAgentAdoption } from '@n8n/instance-ai'; import type { Scope } from '@n8n/permissions'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import { AgentDefaultModelResolverService } from '@/modules/agents/agent-default-model-resolver.service'; import { AgentRunnableStateService } from '@/modules/agents/agent-runnable-state.service'; import { AgentsService } from '@/modules/agents/agents.service'; diff --git a/packages/cli/src/modules/instance-ai/instance-ai-run-limit.error.ts b/packages/cli/src/modules/instance-ai/instance-ai-run-limit.error.ts index 023d428155d..51614439321 100644 --- a/packages/cli/src/modules/instance-ai/instance-ai-run-limit.error.ts +++ b/packages/cli/src/modules/instance-ai/instance-ai-run-limit.error.ts @@ -1,6 +1,6 @@ import type { InstanceAiRunLimitMeta } from '@n8n/api-types'; -import { TooManyRequestsError } from '@/errors/response-errors/too-many-requests.error'; +import { TooManyRequestsError } from '@n8n/errors'; /** * A new user turn was refused because a concurrency cap was already full. diff --git a/packages/cli/src/modules/instance-ai/instance-ai-settings.service.ts b/packages/cli/src/modules/instance-ai/instance-ai-settings.service.ts index b70483cdf9c..c55a0fedf63 100644 --- a/packages/cli/src/modules/instance-ai/instance-ai-settings.service.ts +++ b/packages/cli/src/modules/instance-ai/instance-ai-settings.service.ts @@ -42,9 +42,7 @@ import { type InstanceCredentialUse, type ResolvedInstanceCredential, } from '@/credentials/instance-credential-broker'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { ConflictError, ForbiddenError, UnprocessableRequestError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { AiService } from '@/services/ai.service'; import { diff --git a/packages/cli/src/modules/instance-ai/instance-ai-test.controller.ts b/packages/cli/src/modules/instance-ai/instance-ai-test.controller.ts index b1f75f37eb6..107770cc418 100644 --- a/packages/cli/src/modules/instance-ai/instance-ai-test.controller.ts +++ b/packages/cli/src/modules/instance-ai/instance-ai-test.controller.ts @@ -3,7 +3,7 @@ import { Body, Delete, Get, Param, Post, RestController } from '@n8n/decorators' import type { Request, Response } from 'express'; import { v4 as uuidv4 } from 'uuid'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { InstanceAiMemoryService } from './instance-ai-memory.service'; import { InstanceAiService } from './instance-ai.service'; diff --git a/packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts b/packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts index 5e32ce679d5..f576b2b637b 100644 --- a/packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts +++ b/packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts @@ -153,9 +153,7 @@ import { CollaborationService } from '@/collaboration/collaboration.service'; import { CredentialsOverwrites } from '@/credentials-overwrites'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; import { CredentialsService } from '@/credentials/credentials.service'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { LockedError } from '@/errors/response-errors/locked.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ConflictError, LockedError, NotFoundError } from '@n8n/errors'; import { EvaluationConfigService } from '@/evaluation.ee/evaluation-config.service'; import { LlmJudgeProviderRegistry } from '@/evaluation.ee/llm-judge-provider-registry'; import { EventService } from '@/events/event.service'; diff --git a/packages/cli/src/modules/instance-ai/instance-ai.controller.ts b/packages/cli/src/modules/instance-ai/instance-ai.controller.ts index da7d3692ff7..c11607f5432 100644 --- a/packages/cli/src/modules/instance-ai/instance-ai.controller.ts +++ b/packages/cli/src/modules/instance-ai/instance-ai.controller.ts @@ -86,10 +86,7 @@ import { InstanceAiService } from './instance-ai.service'; import { InstanceAiOnboardingService, startsOnboardingFirstTurn } from './onboarding'; import { CredentialsService } from '@/credentials/credentials.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from '@n8n/errors'; import { Push } from '@/push'; import { Publisher } from '@/scaling/pubsub/publisher.service'; import { ProjectService } from '@/services/project.service.ee'; diff --git a/packages/cli/src/modules/instance-ai/instance-ai.service.ts b/packages/cli/src/modules/instance-ai/instance-ai.service.ts index f2afe6b4da7..0fa91d07293 100644 --- a/packages/cli/src/modules/instance-ai/instance-ai.service.ts +++ b/packages/cli/src/modules/instance-ai/instance-ai.service.ts @@ -152,8 +152,7 @@ import { OperationalError, UnexpectedError, UserError } from 'n8n-workflow'; import { nanoid } from 'nanoid'; import { N8N_VERSION, WORKFLOW_SDK_VERSION } from '@/constants'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { InstanceAiBuilderDelegateAdapterService } from '@/modules/agents/instance-ai-builder-delegate.adapter'; import { InstanceAiAgentContextAdapterService } from '@/modules/agents/instance-ai-agent-context.adapter'; diff --git a/packages/cli/src/modules/instance-ai/mcp/__tests__/instance-ai-mcp-connection.controller.test.ts b/packages/cli/src/modules/instance-ai/mcp/__tests__/instance-ai-mcp-connection.controller.test.ts index 2bd9b7c3467..f69b2a25a8a 100644 --- a/packages/cli/src/modules/instance-ai/mcp/__tests__/instance-ai-mcp-connection.controller.test.ts +++ b/packages/cli/src/modules/instance-ai/mcp/__tests__/instance-ai-mcp-connection.controller.test.ts @@ -2,7 +2,7 @@ import type { AuthenticatedRequest, CredentialsEntity, User } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; import type { CredentialsFinderService } from '@/credentials/credentials-finder.service'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { McpRegistryService } from '@/modules/mcp-registry/registry/mcp-registry.service'; import type { McpRegistryServer } from '@/modules/mcp-registry/registry/mcp-registry.types'; diff --git a/packages/cli/src/modules/instance-ai/mcp/__tests__/instance-ai-mcp-registry.service.test.ts b/packages/cli/src/modules/instance-ai/mcp/__tests__/instance-ai-mcp-registry.service.test.ts index 39360e6f445..260003c4978 100644 --- a/packages/cli/src/modules/instance-ai/mcp/__tests__/instance-ai-mcp-registry.service.test.ts +++ b/packages/cli/src/modules/instance-ai/mcp/__tests__/instance-ai-mcp-registry.service.test.ts @@ -8,9 +8,7 @@ import { mock } from 'vitest-mock-extended'; import type { CredentialsFinderService } from '@/credentials/credentials-finder.service'; import type { CredentialsService } from '@/credentials/credentials.service'; import type { CredentialTypes } from '@/credential-types'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, NotFoundError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { McpRegistryService } from '@/modules/mcp-registry/registry/mcp-registry.service'; import type { McpRegistryServer } from '@/modules/mcp-registry/registry/mcp-registry.types'; diff --git a/packages/cli/src/modules/instance-ai/mcp/instance-ai-mcp-connection.controller.ts b/packages/cli/src/modules/instance-ai/mcp/instance-ai-mcp-connection.controller.ts index e9dbd34adac..9e7da7a869a 100644 --- a/packages/cli/src/modules/instance-ai/mcp/instance-ai-mcp-connection.controller.ts +++ b/packages/cli/src/modules/instance-ai/mcp/instance-ai-mcp-connection.controller.ts @@ -20,7 +20,7 @@ import { import type { Response } from 'express'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { McpRegistryService } from '@/modules/mcp-registry/registry/mcp-registry.service'; import type { McpRegistryServer } from '@/modules/mcp-registry/registry/mcp-registry.types'; diff --git a/packages/cli/src/modules/instance-ai/mcp/instance-ai-mcp-registry.service.ts b/packages/cli/src/modules/instance-ai/mcp/instance-ai-mcp-registry.service.ts index 2b3bb93eaa2..520e66cd748 100644 --- a/packages/cli/src/modules/instance-ai/mcp/instance-ai-mcp-registry.service.ts +++ b/packages/cli/src/modules/instance-ai/mcp/instance-ai-mcp-registry.service.ts @@ -18,9 +18,7 @@ import { randomUUID } from 'node:crypto'; import { CredentialTypes } from '@/credential-types'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; import { CredentialsService } from '@/credentials/credentials.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { isSupportedMcpRegistryCredentialType, diff --git a/packages/cli/src/modules/instance-ai/onboarding.ts b/packages/cli/src/modules/instance-ai/onboarding.ts index 2a66f962295..46e2bbb8832 100644 --- a/packages/cli/src/modules/instance-ai/onboarding.ts +++ b/packages/cli/src/modules/instance-ai/onboarding.ts @@ -17,7 +17,7 @@ import { nanoid } from 'nanoid'; import { randomUUID } from 'node:crypto'; import { z } from 'zod'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { Telemetry } from '@/telemetry'; import { DurableEventLog } from './event-bus/durable-event-log'; diff --git a/packages/cli/src/modules/instance-ai/storage/typeorm-agent-memory.ts b/packages/cli/src/modules/instance-ai/storage/typeorm-agent-memory.ts index 9af030203aa..47dcf58cb4a 100644 --- a/packages/cli/src/modules/instance-ai/storage/typeorm-agent-memory.ts +++ b/packages/cli/src/modules/instance-ai/storage/typeorm-agent-memory.ts @@ -31,8 +31,7 @@ import { In, LessThan, Like } from '@n8n/typeorm'; import { UnexpectedError } from 'n8n-workflow'; import { z } from 'zod'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ConflictError, ForbiddenError } from '@n8n/errors'; import { TypeORMObservationLogStore } from './typeorm-observation-log-store'; import type { InstanceAiMessage } from '../entities/instance-ai-message.entity'; diff --git a/packages/cli/src/modules/ldap.ee/ldap.controller.ee.ts b/packages/cli/src/modules/ldap.ee/ldap.controller.ee.ts index bbe359859d7..f2709752867 100644 --- a/packages/cli/src/modules/ldap.ee/ldap.controller.ee.ts +++ b/packages/cli/src/modules/ldap.ee/ldap.controller.ee.ts @@ -3,7 +3,7 @@ import { Get, Post, Put, RestController, GlobalScope, Licensed } from '@n8n/deco import pick from 'lodash/pick'; import { CREDENTIAL_BLANKING_VALUE } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { NON_SENSIBLE_LDAP_CONFIG_PROPERTIES } from './constants'; diff --git a/packages/cli/src/modules/ldap.ee/ldap.service.ee.ts b/packages/cli/src/modules/ldap.ee/ldap.service.ee.ts index 3b923dbc5c4..6b11d86ca25 100644 --- a/packages/cli/src/modules/ldap.ee/ldap.service.ee.ts +++ b/packages/cli/src/modules/ldap.ee/ldap.service.ee.ts @@ -13,7 +13,7 @@ import { Cipher } from 'n8n-core'; import { CREDENTIAL_BLANKING_VALUE, jsonParse, UnexpectedError } from 'n8n-workflow'; import type { ConnectionOptions } from 'tls'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { assertAuthenticationMethodCanBeEnabled, diff --git a/packages/cli/src/modules/log-streaming.ee/__tests__/log-streaming.controller.test.ts b/packages/cli/src/modules/log-streaming.ee/__tests__/log-streaming.controller.test.ts index bf71c561197..8cae8ec197d 100644 --- a/packages/cli/src/modules/log-streaming.ee/__tests__/log-streaming.controller.test.ts +++ b/packages/cli/src/modules/log-streaming.ee/__tests__/log-streaming.controller.test.ts @@ -9,8 +9,7 @@ import type { import { mock } from 'vitest-mock-extended'; import type { CredentialsFinderService } from '@/credentials/credentials-finder.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import type { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus'; import type { MessageEventBusDestination } from '../destinations/message-event-bus-destination.ee'; diff --git a/packages/cli/src/modules/log-streaming.ee/destinations/__tests__/destination-credentials-access.test.ts b/packages/cli/src/modules/log-streaming.ee/destinations/__tests__/destination-credentials-access.test.ts index 0070f1d54bf..c4e3d129733 100644 --- a/packages/cli/src/modules/log-streaming.ee/destinations/__tests__/destination-credentials-access.test.ts +++ b/packages/cli/src/modules/log-streaming.ee/destinations/__tests__/destination-credentials-access.test.ts @@ -3,7 +3,7 @@ import type { MessageEventBusDestinationOptions } from 'n8n-workflow'; import { mock } from 'vitest-mock-extended'; import type { CredentialsFinderService } from '@/credentials/credentials-finder.service'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { assertUserCanUseDestinationCredentials } from '../destination-credentials-access'; diff --git a/packages/cli/src/modules/log-streaming.ee/destinations/destination-credentials-access.ts b/packages/cli/src/modules/log-streaming.ee/destinations/destination-credentials-access.ts index 91674143fe7..58a2f981813 100644 --- a/packages/cli/src/modules/log-streaming.ee/destinations/destination-credentials-access.ts +++ b/packages/cli/src/modules/log-streaming.ee/destinations/destination-credentials-access.ts @@ -2,7 +2,7 @@ import type { User } from '@n8n/db'; import type { MessageEventBusDestinationOptions } from 'n8n-workflow'; import type { CredentialsFinderService } from '@/credentials/credentials-finder.service'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; /** * Verifies the requesting user has access to every credential referenced by a diff --git a/packages/cli/src/modules/log-streaming.ee/log-streaming.controller.ts b/packages/cli/src/modules/log-streaming.ee/log-streaming.controller.ts index 17c50df76fc..a732de4f234 100644 --- a/packages/cli/src/modules/log-streaming.ee/log-streaming.controller.ts +++ b/packages/cli/src/modules/log-streaming.ee/log-streaming.controller.ts @@ -11,8 +11,7 @@ import { Delete, Get, GlobalScope, Licensed, Post, Query, RestController } from import type { MessageEventBusDestinationOptions } from 'n8n-workflow'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { eventNamesAll } from '@/eventbus/event-message-classes'; import { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus'; diff --git a/packages/cli/src/modules/mcp-registry/__tests__/mcp-registry-test.controller.test.ts b/packages/cli/src/modules/mcp-registry/__tests__/mcp-registry-test.controller.test.ts index dfee86df35b..e443b128d66 100644 --- a/packages/cli/src/modules/mcp-registry/__tests__/mcp-registry-test.controller.test.ts +++ b/packages/cli/src/modules/mcp-registry/__tests__/mcp-registry-test.controller.test.ts @@ -1,6 +1,6 @@ import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { McpRegistryTestController } from '../mcp-registry-test.controller'; import { McpRegistryServerEntity } from '../registry/mcp-registry-server.entity'; diff --git a/packages/cli/src/modules/mcp-registry/mcp-registry-test.controller.ts b/packages/cli/src/modules/mcp-registry/mcp-registry-test.controller.ts index 1c9cae8eca7..4d81ca65e6a 100644 --- a/packages/cli/src/modules/mcp-registry/mcp-registry-test.controller.ts +++ b/packages/cli/src/modules/mcp-registry/mcp-registry-test.controller.ts @@ -1,6 +1,6 @@ import { Post, RestController } from '@n8n/decorators'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { McpRegistryServerEntity } from './registry/mcp-registry-server.entity'; import { McpRegistryServerRepository } from './registry/mcp-registry-server.repository'; diff --git a/packages/cli/src/modules/mcp/__tests__/agent-tools.service.test.ts b/packages/cli/src/modules/mcp/__tests__/agent-tools.service.test.ts index a03e09c7c77..7542fc054ab 100644 --- a/packages/cli/src/modules/mcp/__tests__/agent-tools.service.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/agent-tools.service.test.ts @@ -27,7 +27,7 @@ vi.mock('@/modules/agents/json-config/mcp-client-factory', () => ({ import { CredentialsService } from '@/credentials/credentials.service'; import type { EventService } from '@/events/event.service'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError } from '@n8n/errors'; import { AgentConfigService } from '@/modules/agents/agent-config.service'; import { AgentCustomToolsService } from '@/modules/agents/agent-custom-tools.service'; import { AgentIntegrationManagementService } from '@/modules/agents/agent-integration-management.service'; diff --git a/packages/cli/src/modules/mcp/__tests__/create-workflow-from-code.tool.test.ts b/packages/cli/src/modules/mcp/__tests__/create-workflow-from-code.tool.test.ts index 9aca092ab15..0d9a106e053 100644 --- a/packages/cli/src/modules/mcp/__tests__/create-workflow-from-code.tool.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/create-workflow-from-code.tool.test.ts @@ -10,7 +10,7 @@ import { McpPostSaveMetricsService } from '../mcp-post-save-metrics.service'; import { createCreateWorkflowFromCodeTool } from '../tools/workflow-builder/create-workflow-from-code.tool'; import { CredentialsService } from '@/credentials/credentials.service'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { NodeTypes } from '@/node-types'; import type { AiGatewayService } from '@/services/ai-gateway.service'; import { UrlService } from '@n8n/backend-services'; diff --git a/packages/cli/src/modules/mcp/__tests__/credential-validation.test.ts b/packages/cli/src/modules/mcp/__tests__/credential-validation.test.ts index 387a3f56f13..9f6907500dd 100644 --- a/packages/cli/src/modules/mcp/__tests__/credential-validation.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/credential-validation.test.ts @@ -4,7 +4,7 @@ import type { INode, INodeTypeDescription, INodeCredentialDescription } from 'n8 import { validateWorkflowCredentialReferences } from '../tools/workflow-builder/credential-validation'; import type { CredentialsService } from '@/credentials/credentials.service'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { NodeTypes } from '@/node-types'; const user = { id: 'user-1' } as User; diff --git a/packages/cli/src/modules/mcp/__tests__/mcp.settings.controller.test.ts b/packages/cli/src/modules/mcp/__tests__/mcp.settings.controller.test.ts index b4dad64a84d..6aa8fd93f33 100644 --- a/packages/cli/src/modules/mcp/__tests__/mcp.settings.controller.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/mcp.settings.controller.test.ts @@ -5,7 +5,7 @@ import { Container } from '@n8n/di'; import type { Response } from 'express'; import { mock, mockDeep } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import type { ListQuery } from '@/requests'; import { WorkflowService } from '@/workflows/workflow.service'; diff --git a/packages/cli/src/modules/mcp/__tests__/mcp.settings.service.test.ts b/packages/cli/src/modules/mcp/__tests__/mcp.settings.service.test.ts index 266a6d0b2d0..f73fe03344d 100644 --- a/packages/cli/src/modules/mcp/__tests__/mcp.settings.service.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/mcp.settings.service.test.ts @@ -8,7 +8,7 @@ import type { Mock } from 'vitest'; import { mock } from 'vitest-mock-extended'; import type { CollaborationService } from '@/collaboration/collaboration.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { CacheService } from '@/services/cache/cache.service'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; diff --git a/packages/cli/src/modules/mcp/__tests__/save-user-preference.tool.test.ts b/packages/cli/src/modules/mcp/__tests__/save-user-preference.tool.test.ts index a98341d8f7f..1d5c48e523d 100644 --- a/packages/cli/src/modules/mcp/__tests__/save-user-preference.tool.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/save-user-preference.tool.test.ts @@ -7,9 +7,7 @@ import { CLIENT_CAPABILITIES_META_KEY } from '@modelcontextprotocol/server'; import type { InputRequiredResult } from '@modelcontextprotocol/server'; import { AiPreferenceScopeFullError } from '@/errors/response-errors/ai-preference-scope-full.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ConflictError, ForbiddenError } from '@n8n/errors'; import { AiPreferenceService } from '@/services/ai-preference.service'; import { UrlService } from '@n8n/backend-services'; import { Telemetry } from '@/telemetry'; diff --git a/packages/cli/src/modules/mcp/__tests__/undo-user-preference.tool.test.ts b/packages/cli/src/modules/mcp/__tests__/undo-user-preference.tool.test.ts index 346dea77e84..87e657cd5f6 100644 --- a/packages/cli/src/modules/mcp/__tests__/undo-user-preference.tool.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/undo-user-preference.tool.test.ts @@ -3,8 +3,7 @@ import { mockInstance, mockLogger } from '@n8n/backend-test-utils'; import { User } from '@n8n/db'; import { TELEMETRY_EVENT } from '@n8n/telemetry'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import { AiPreferenceService } from '@/services/ai-preference.service'; import { Telemetry } from '@/telemetry'; diff --git a/packages/cli/src/modules/mcp/__tests__/update-user-preference.tool.test.ts b/packages/cli/src/modules/mcp/__tests__/update-user-preference.tool.test.ts index ded8ba474ac..ae193f84560 100644 --- a/packages/cli/src/modules/mcp/__tests__/update-user-preference.tool.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/update-user-preference.tool.test.ts @@ -4,8 +4,7 @@ import { mockInstance, mockLogger } from '@n8n/backend-test-utils'; import { User } from '@n8n/db'; import { TELEMETRY_EVENT } from '@n8n/telemetry'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ConflictError, NotFoundError } from '@n8n/errors'; import { AiPreferenceService } from '@/services/ai-preference.service'; import { UrlService } from '@n8n/backend-services'; import { Telemetry } from '@/telemetry'; diff --git a/packages/cli/src/modules/mcp/__tests__/update-workflow.tool.test.ts b/packages/cli/src/modules/mcp/__tests__/update-workflow.tool.test.ts index 7da17f941e6..309e3f1324a 100644 --- a/packages/cli/src/modules/mcp/__tests__/update-workflow.tool.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/update-workflow.tool.test.ts @@ -18,7 +18,7 @@ import { NON_FATAL_OPERATION_TYPES } from '../tools/workflow-builder/workflow-op import { CollaborationService } from '@/collaboration/collaboration.service'; import { CredentialsService } from '@/credentials/credentials.service'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { SubworkflowPolicyDenialError } from '@/errors/subworkflow-policy-denial.error'; import { SubworkflowPolicyChecker } from '@/executions/pre-execution-checks/subworkflow-policy-checker'; import { NodeTypes } from '@/node-types'; diff --git a/packages/cli/src/modules/mcp/mcp-server-middleware.service.ts b/packages/cli/src/modules/mcp/mcp-server-middleware.service.ts index e38247e7b34..3688e459a49 100644 --- a/packages/cli/src/modules/mcp/mcp-server-middleware.service.ts +++ b/packages/cli/src/modules/mcp/mcp-server-middleware.service.ts @@ -3,7 +3,7 @@ import { Service } from '@n8n/di'; import { NextFunction, Response, Request } from 'express'; import { ensureError } from '@n8n/utils/errors/ensure-error'; -import { AuthError } from '@/errors/response-errors/auth.error'; +import { AuthError } from '@n8n/errors'; import { JwtService } from '@/services/jwt.service'; import { OAuthTokenVerifierProxy, diff --git a/packages/cli/src/modules/mcp/mcp.settings.controller.ts b/packages/cli/src/modules/mcp/mcp.settings.controller.ts index 1c5385a7c39..f889e12ff6b 100644 --- a/packages/cli/src/modules/mcp/mcp.settings.controller.ts +++ b/packages/cli/src/modules/mcp/mcp.settings.controller.ts @@ -4,7 +4,7 @@ import { type AuthenticatedRequest } from '@n8n/db'; import { Body, Post, Get, Patch, RestController, GlobalScope } from '@n8n/decorators'; import type { Response } from 'express'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { listQueryMiddleware } from '@/middlewares'; import type { ListQuery } from '@/requests'; diff --git a/packages/cli/src/modules/mcp/mcp.settings.service.ts b/packages/cli/src/modules/mcp/mcp.settings.service.ts index c164d3b7e53..8780622e359 100644 --- a/packages/cli/src/modules/mcp/mcp.settings.service.ts +++ b/packages/cli/src/modules/mcp/mcp.settings.service.ts @@ -17,7 +17,7 @@ import { } from 'n8n-workflow'; import { CollaborationService } from '@/collaboration/collaboration.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { CacheService } from '@/services/cache/cache.service'; import { removeDefaultValues } from '@/workflow-helpers'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; diff --git a/packages/cli/src/modules/mcp/tools/agents/agent-tools.service.ts b/packages/cli/src/modules/mcp/tools/agents/agent-tools.service.ts index 1a033309e17..be8d8ba51fe 100644 --- a/packages/cli/src/modules/mcp/tools/agents/agent-tools.service.ts +++ b/packages/cli/src/modules/mcp/tools/agents/agent-tools.service.ts @@ -28,8 +28,7 @@ import { UserError } from 'n8n-workflow'; import { z } from 'zod'; import { CredentialsService } from '@/credentials/credentials.service'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ConflictError, ForbiddenError } from '@n8n/errors'; import { AgentConfigService } from '@/modules/agents/agent-config.service'; import { AgentCustomToolsService } from '@/modules/agents/agent-custom-tools.service'; import { AgentIntegrationManagementService } from '@/modules/agents/agent-integration-management.service'; diff --git a/packages/cli/src/modules/mcp/tools/user-preference-tool.utils.ts b/packages/cli/src/modules/mcp/tools/user-preference-tool.utils.ts index 1529df31240..f58043a9a7b 100644 --- a/packages/cli/src/modules/mcp/tools/user-preference-tool.utils.ts +++ b/packages/cli/src/modules/mcp/tools/user-preference-tool.utils.ts @@ -3,7 +3,7 @@ import { aiPreferenceScopeOf } from '@n8n/api-types'; import type { InferTelemetryProps, TELEMETRY_EVENT } from '@n8n/telemetry'; import z from 'zod'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { AiPreferenceWriteRejection } from '@/services/ai-preference-write'; import { secondsSinceSaved, toAiPreferenceWriteRejection } from '@/services/ai-preference-write'; import type { UrlService } from '@n8n/backend-services'; diff --git a/packages/cli/src/modules/mcp/tools/workflow-builder/__tests__/error-code.utils.test.ts b/packages/cli/src/modules/mcp/tools/workflow-builder/__tests__/error-code.utils.test.ts index 3dbfca3a2d5..fc67a6c67d0 100644 --- a/packages/cli/src/modules/mcp/tools/workflow-builder/__tests__/error-code.utils.test.ts +++ b/packages/cli/src/modules/mcp/tools/workflow-builder/__tests__/error-code.utils.test.ts @@ -1,4 +1,4 @@ -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { getErrorCode } from '../error-code.utils'; diff --git a/packages/cli/src/modules/mcp/tools/workflow-builder/create-workflow-from-code.tool.ts b/packages/cli/src/modules/mcp/tools/workflow-builder/create-workflow-from-code.tool.ts index df39b16c9be..7d1825f2460 100644 --- a/packages/cli/src/modules/mcp/tools/workflow-builder/create-workflow-from-code.tool.ts +++ b/packages/cli/src/modules/mcp/tools/workflow-builder/create-workflow-from-code.tool.ts @@ -47,7 +47,7 @@ import { USER_CALLED_MCP_TOOL_EVENT } from '../../mcp.constants'; import type { ToolDefinition, UserCalledMCPToolEventPayload } from '../../mcp.types'; import { getSdkReferenceHint } from '../workflow-validation.utils'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { Telemetry } from '@/telemetry'; import { dropInvalidWorkflowGroups, diff --git a/packages/cli/src/modules/mcp/tools/workflow-builder/credential-validation.ts b/packages/cli/src/modules/mcp/tools/workflow-builder/credential-validation.ts index ab9416150db..20c0a4e1b49 100644 --- a/packages/cli/src/modules/mcp/tools/workflow-builder/credential-validation.ts +++ b/packages/cli/src/modules/mcp/tools/workflow-builder/credential-validation.ts @@ -3,7 +3,7 @@ import type { INode, INodeTypeDescription, IWorkflowBase } from 'n8n-workflow'; import { NodeHelpers } from 'n8n-workflow'; import type { CredentialsService } from '@/credentials/credentials.service'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { NodeTypes } from '@/node-types'; import type { PartialUpdateOperation } from './workflow-operations'; diff --git a/packages/cli/src/modules/n8n-packages/__tests__/import-data-tables.integration.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/import-data-tables.integration.test.ts index e0447dd3f16..2b919112cb4 100644 --- a/packages/cli/src/modules/n8n-packages/__tests__/import-data-tables.integration.test.ts +++ b/packages/cli/src/modules/n8n-packages/__tests__/import-data-tables.integration.test.ts @@ -7,7 +7,7 @@ import type { INode, INodeParameterResourceLocator, Workflow } from 'n8n-workflo import { NodeOperationError } from 'n8n-workflow'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import type { RelayEventMap } from '@/events/maps/relay.event-map'; import { mockDataTableSizeValidator } from '@/modules/data-table/__tests__/test-helpers'; diff --git a/packages/cli/src/modules/n8n-packages/__tests__/import-folders.integration.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/import-folders.integration.test.ts index 4253fe6ef8a..0e4cd7026da 100644 --- a/packages/cli/src/modules/n8n-packages/__tests__/import-folders.integration.test.ts +++ b/packages/cli/src/modules/n8n-packages/__tests__/import-folders.integration.test.ts @@ -4,9 +4,7 @@ import type { Project, User } from '@n8n/db'; import { FolderRepository, WorkflowRepository } from '@n8n/db'; import { Container } from '@n8n/di'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { ConflictError, ForbiddenError, UnprocessableRequestError } from '@n8n/errors'; import { createFolder } from '@test-integration/db/folders'; import { createOwner } from '@test-integration/db/users'; import { LicenseMocker } from '@test-integration/license'; diff --git a/packages/cli/src/modules/n8n-packages/__tests__/import-package-policy.integration.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/import-package-policy.integration.test.ts index de7a606a349..a16df41aa20 100644 --- a/packages/cli/src/modules/n8n-packages/__tests__/import-package-policy.integration.test.ts +++ b/packages/cli/src/modules/n8n-packages/__tests__/import-package-policy.integration.test.ts @@ -27,7 +27,7 @@ import { tmpdir } from 'node:os'; import path from 'node:path'; import { ActiveWorkflowManager } from '@/active-workflow-manager'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { UnprocessableRequestError } from '@n8n/errors'; import { PolicyDecisionService } from '@/modules/policy-infrastructure/policy-decision.service'; import { PolicyEnforcementService } from '@/policy/policy-enforcement.service'; import { createOwner } from '@test-integration/db/users'; diff --git a/packages/cli/src/modules/n8n-packages/__tests__/import-package-selection.integration.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/import-package-selection.integration.test.ts index 3b2dfa08365..4e8488a8a2e 100644 --- a/packages/cli/src/modules/n8n-packages/__tests__/import-package-selection.integration.test.ts +++ b/packages/cli/src/modules/n8n-packages/__tests__/import-package-selection.integration.test.ts @@ -15,9 +15,7 @@ import { tmpdir } from 'node:os'; import path from 'node:path'; import { ActiveWorkflowManager } from '@/active-workflow-manager'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { BadRequestError, ConflictError, UnprocessableRequestError } from '@n8n/errors'; import { createCustomRoleWithScopeSlugs } from '@test-integration/db/roles'; import { createMember, createOwner } from '@test-integration/db/users'; import { LicenseMocker } from '@test-integration/license'; diff --git a/packages/cli/src/modules/n8n-packages/__tests__/import-package.integration.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/import-package.integration.test.ts index 639651681fd..29b87ab77cf 100644 --- a/packages/cli/src/modules/n8n-packages/__tests__/import-package.integration.test.ts +++ b/packages/cli/src/modules/n8n-packages/__tests__/import-package.integration.test.ts @@ -21,8 +21,7 @@ import { Container } from '@n8n/di'; import { ActiveWorkflowManager } from '@/active-workflow-manager'; import { CredentialTypes } from '@/credential-types'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { BadRequestError, UnprocessableRequestError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import type { RelayEventMap } from '@/events/maps/relay.event-map'; import { diff --git a/packages/cli/src/modules/n8n-packages/__tests__/import-projects.integration.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/import-projects.integration.test.ts index f8570d18b05..5987af89cab 100644 --- a/packages/cli/src/modules/n8n-packages/__tests__/import-projects.integration.test.ts +++ b/packages/cli/src/modules/n8n-packages/__tests__/import-projects.integration.test.ts @@ -23,9 +23,7 @@ import { pickVariableForProject } from 'n8n-workflow'; import { ActiveWorkflowManager } from '@/active-workflow-manager'; import { VariablesService } from '@/environments.ee/variables/variables.service.ee'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { ConflictError, ForbiddenError, UnprocessableRequestError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import type { RelayEventMap } from '@/events/maps/relay.event-map'; import { createFolder } from '@test-integration/db/folders'; diff --git a/packages/cli/src/modules/n8n-packages/__tests__/import-workflow-with-tags.integration.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/import-workflow-with-tags.integration.test.ts index 42fac879db7..c8715661de0 100644 --- a/packages/cli/src/modules/n8n-packages/__tests__/import-workflow-with-tags.integration.test.ts +++ b/packages/cli/src/modules/n8n-packages/__tests__/import-workflow-with-tags.integration.test.ts @@ -10,8 +10,7 @@ import { } from '@n8n/db'; import { Container } from '@n8n/di'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ConflictError, ForbiddenError } from '@n8n/errors'; import { createFolder } from '@test-integration/db/folders'; import { assignTagToWorkflow, createTag, updateTag } from '@test-integration/db/tags'; import { createOwner } from '@test-integration/db/users'; diff --git a/packages/cli/src/modules/n8n-packages/__tests__/import-workflow-with-variables.integration.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/import-workflow-with-variables.integration.test.ts index e31ff545dcf..bfd6c78ba94 100644 --- a/packages/cli/src/modules/n8n-packages/__tests__/import-workflow-with-variables.integration.test.ts +++ b/packages/cli/src/modules/n8n-packages/__tests__/import-workflow-with-variables.integration.test.ts @@ -11,8 +11,7 @@ import { VariablesRepository, WorkflowRepository } from '@n8n/db'; import { Container } from '@n8n/di'; import { VariablesService } from '@/environments.ee/variables/variables.service.ee'; -import type { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { type ConflictError, ForbiddenError } from '@n8n/errors'; import { VariableCountLimitReachedError } from '@/errors/variable-count-limit-reached.error'; import { WorkflowCreationService } from '@/workflows/workflow-creation.service'; import { createMember, createOwner } from '@test-integration/db/users'; diff --git a/packages/cli/src/modules/n8n-packages/__tests__/package-failure-classifier.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/package-failure-classifier.test.ts index 5a47472565b..b67e02a85a2 100644 --- a/packages/cli/src/modules/n8n-packages/__tests__/package-failure-classifier.test.ts +++ b/packages/cli/src/modules/n8n-packages/__tests__/package-failure-classifier.test.ts @@ -1,10 +1,12 @@ import { UserError } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { + BadRequestError, + ConflictError, + ForbiddenError, + NotFoundError, + UnprocessableRequestError, +} from '@n8n/errors'; import { PackageEntityAccessDeniedError, diff --git a/packages/cli/src/modules/n8n-packages/__tests__/utils/import-package-upload.test.ts b/packages/cli/src/modules/n8n-packages/__tests__/utils/import-package-upload.test.ts index aea7946b1af..7bbee87977e 100644 --- a/packages/cli/src/modules/n8n-packages/__tests__/utils/import-package-upload.test.ts +++ b/packages/cli/src/modules/n8n-packages/__tests__/utils/import-package-upload.test.ts @@ -3,7 +3,7 @@ import { GlobalConfig } from '@n8n/config'; import { Container } from '@n8n/di'; import multer from 'multer'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { createN8nPackageMulterOptions, diff --git a/packages/cli/src/modules/n8n-packages/engine/__tests__/import-blocked.error.test.ts b/packages/cli/src/modules/n8n-packages/engine/__tests__/import-blocked.error.test.ts index 49bc65c97c8..c0f75b549d1 100644 --- a/packages/cli/src/modules/n8n-packages/engine/__tests__/import-blocked.error.test.ts +++ b/packages/cli/src/modules/n8n-packages/engine/__tests__/import-blocked.error.test.ts @@ -1,5 +1,4 @@ -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { ConflictError, UnprocessableRequestError } from '@n8n/errors'; import type { BlockingIssue } from '../../n8n-packages.types'; import { toImportBlockedError } from '../import-blocked.error'; diff --git a/packages/cli/src/modules/n8n-packages/engine/__tests__/import-gates.test.ts b/packages/cli/src/modules/n8n-packages/engine/__tests__/import-gates.test.ts index 6572914795a..771ace05138 100644 --- a/packages/cli/src/modules/n8n-packages/engine/__tests__/import-gates.test.ts +++ b/packages/cli/src/modules/n8n-packages/engine/__tests__/import-gates.test.ts @@ -1,6 +1,6 @@ import type { WorkflowEntity } from '@n8n/db'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { WorkflowImportPlan } from '../../entities/workflow/workflow-import.types'; import { assertArchiveTransitionsAllowed } from '../import-gates'; diff --git a/packages/cli/src/modules/n8n-packages/engine/import-blocked.error.ts b/packages/cli/src/modules/n8n-packages/engine/import-blocked.error.ts index 8e1441c3f9c..17306f7193b 100644 --- a/packages/cli/src/modules/n8n-packages/engine/import-blocked.error.ts +++ b/packages/cli/src/modules/n8n-packages/engine/import-blocked.error.ts @@ -1,5 +1,4 @@ -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { ConflictError, UnprocessableRequestError } from '@n8n/errors'; import type { BlockingIssue } from '../n8n-packages.types'; diff --git a/packages/cli/src/modules/n8n-packages/engine/import-gates.ts b/packages/cli/src/modules/n8n-packages/engine/import-gates.ts index 38c8d44fc1c..43636810a2f 100644 --- a/packages/cli/src/modules/n8n-packages/engine/import-gates.ts +++ b/packages/cli/src/modules/n8n-packages/engine/import-gates.ts @@ -1,6 +1,6 @@ import type { LicenseState } from '@n8n/backend-common'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { TagImportPlan } from '../entities/tag/tag.types'; import type { WorkflowImportPlan } from '../entities/workflow/workflow-import.types'; diff --git a/packages/cli/src/modules/n8n-packages/engine/n8n-package-parser.ts b/packages/cli/src/modules/n8n-packages/engine/n8n-package-parser.ts index 9604b4b1613..ca3cc369ccd 100644 --- a/packages/cli/src/modules/n8n-packages/engine/n8n-package-parser.ts +++ b/packages/cli/src/modules/n8n-packages/engine/n8n-package-parser.ts @@ -4,7 +4,7 @@ import { Service } from '@n8n/di'; import { jsonParse, UserError } from 'n8n-workflow'; import { ZodError } from 'zod'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { NodeTypes } from '@/node-types'; import * as WorkflowHelpers from '@/workflow-helpers'; diff --git a/packages/cli/src/modules/n8n-packages/engine/project-package-importer.ts b/packages/cli/src/modules/n8n-packages/engine/project-package-importer.ts index b2d392bb51e..77bc7fd623f 100644 --- a/packages/cli/src/modules/n8n-packages/engine/project-package-importer.ts +++ b/packages/cli/src/modules/n8n-packages/engine/project-package-importer.ts @@ -1,7 +1,7 @@ import { LicenseState } from '@n8n/backend-common'; import { Service } from '@n8n/di'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { CredentialBindingRequest } from '../entities/credential/credential.types'; import { removesUnpackagedWorkflows } from '../entities/folder/folder-conflict-policy'; diff --git a/packages/cli/src/modules/n8n-packages/engine/workflow-package-importer.ts b/packages/cli/src/modules/n8n-packages/engine/workflow-package-importer.ts index c1d9a53d23a..2023dafe160 100644 --- a/packages/cli/src/modules/n8n-packages/engine/workflow-package-importer.ts +++ b/packages/cli/src/modules/n8n-packages/engine/workflow-package-importer.ts @@ -4,8 +4,7 @@ import { Service } from '@n8n/di'; import type { Scope } from '@n8n/permissions'; import { UserError } from 'n8n-workflow'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import { FolderService } from '@/services/folder.service'; import { ProjectService } from '@/services/project.service.ee'; diff --git a/packages/cli/src/modules/n8n-packages/entities/credential/__tests__/credential-importer.test.ts b/packages/cli/src/modules/n8n-packages/entities/credential/__tests__/credential-importer.test.ts index 14f07053a14..9ae23e9c52c 100644 --- a/packages/cli/src/modules/n8n-packages/entities/credential/__tests__/credential-importer.test.ts +++ b/packages/cli/src/modules/n8n-packages/entities/credential/__tests__/credential-importer.test.ts @@ -2,7 +2,7 @@ import type { Project, User } from '@n8n/db'; import { Container } from '@n8n/di'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { CredentialTypes } from '@/credential-types'; import type { CredentialsService } from '@/credentials/credentials.service'; diff --git a/packages/cli/src/modules/n8n-packages/entities/credential/__tests__/credential-matcher.test.ts b/packages/cli/src/modules/n8n-packages/entities/credential/__tests__/credential-matcher.test.ts index 03d56b87563..12c5606f45f 100644 --- a/packages/cli/src/modules/n8n-packages/entities/credential/__tests__/credential-matcher.test.ts +++ b/packages/cli/src/modules/n8n-packages/entities/credential/__tests__/credential-matcher.test.ts @@ -4,7 +4,7 @@ import { mock } from 'vitest-mock-extended'; import type { CredentialTypes } from '@/credential-types'; import type { CredentialsService } from '@/credentials/credentials.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { CredentialMatcherContext, UsableCredential } from '../credential-matcher'; import { CredentialMatcherFactory } from '../credential-matcher-factory'; diff --git a/packages/cli/src/modules/n8n-packages/entities/credential/credential-matcher-factory.ts b/packages/cli/src/modules/n8n-packages/entities/credential/credential-matcher-factory.ts index b52816af90e..7cb976519b6 100644 --- a/packages/cli/src/modules/n8n-packages/entities/credential/credential-matcher-factory.ts +++ b/packages/cli/src/modules/n8n-packages/entities/credential/credential-matcher-factory.ts @@ -1,6 +1,6 @@ import { Service } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { CredentialMatcher } from './credential-matcher'; import { IdBasedCredentialMatcher } from './id-based-credential-matcher'; diff --git a/packages/cli/src/modules/n8n-packages/entities/data-table/data-table-importer.ts b/packages/cli/src/modules/n8n-packages/entities/data-table/data-table-importer.ts index 45384189a14..c951485bc91 100644 --- a/packages/cli/src/modules/n8n-packages/entities/data-table/data-table-importer.ts +++ b/packages/cli/src/modules/n8n-packages/entities/data-table/data-table-importer.ts @@ -2,7 +2,7 @@ import { ModuleRegistry } from '@n8n/backend-common'; import { Service } from '@n8n/di'; import { UserError } from 'n8n-workflow'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { DataTable } from '@/modules/data-table/data-table.entity'; import { DataTableService } from '@/modules/data-table/data-table.service'; import { userHasScopes } from '@/permissions.ee/check-access'; diff --git a/packages/cli/src/modules/n8n-packages/entities/project/project-importer.ts b/packages/cli/src/modules/n8n-packages/entities/project/project-importer.ts index 16cfbc6b287..11795f1b073 100644 --- a/packages/cli/src/modules/n8n-packages/entities/project/project-importer.ts +++ b/packages/cli/src/modules/n8n-packages/entities/project/project-importer.ts @@ -4,7 +4,7 @@ import { Service } from '@n8n/di'; import { hasGlobalScope } from '@n8n/permissions'; import { UnexpectedError } from 'n8n-workflow'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { ProjectService } from '@/services/project.service.ee'; import { decideMatchedProject } from './project-conflict-policy'; diff --git a/packages/cli/src/modules/n8n-packages/entities/tag/__tests__/tag-importer.test.ts b/packages/cli/src/modules/n8n-packages/entities/tag/__tests__/tag-importer.test.ts index b0803e1ba5d..776db5d9ebb 100644 --- a/packages/cli/src/modules/n8n-packages/entities/tag/__tests__/tag-importer.test.ts +++ b/packages/cli/src/modules/n8n-packages/entities/tag/__tests__/tag-importer.test.ts @@ -2,7 +2,7 @@ import type { GlobalConfig } from '@n8n/config'; import type { TagEntity, User } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { TagService } from '@/services/tag.service'; import { TagImporter } from '../tag-importer'; diff --git a/packages/cli/src/modules/n8n-packages/entities/tag/tag-importer.ts b/packages/cli/src/modules/n8n-packages/entities/tag/tag-importer.ts index 72e1f8a7f1e..d9b8f91bf64 100644 --- a/packages/cli/src/modules/n8n-packages/entities/tag/tag-importer.ts +++ b/packages/cli/src/modules/n8n-packages/entities/tag/tag-importer.ts @@ -3,7 +3,7 @@ import { Service } from '@n8n/di'; import { hasGlobalScope } from '@n8n/permissions'; import { UserError } from 'n8n-workflow'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { TagService } from '@/services/tag.service'; import { decideTagImportAction } from './tag-import-decision'; diff --git a/packages/cli/src/modules/n8n-packages/entities/variable/variable-importer.ts b/packages/cli/src/modules/n8n-packages/entities/variable/variable-importer.ts index 2b0479b6644..23697a6c4e3 100644 --- a/packages/cli/src/modules/n8n-packages/entities/variable/variable-importer.ts +++ b/packages/cli/src/modules/n8n-packages/entities/variable/variable-importer.ts @@ -3,7 +3,7 @@ import { hasGlobalScope } from '@n8n/permissions'; import { pickVariableForProject } from 'n8n-workflow'; import { VariablesService } from '@/environments.ee/variables/variables.service.ee'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { VariableCountLimitReachedError } from '@/errors/variable-count-limit-reached.error'; import { userHasScopes } from '@/permissions.ee/check-access'; diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-publisher.test.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-publisher.test.ts index dfd989ac7ec..78a6dd7bc87 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-publisher.test.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-publisher.test.ts @@ -3,8 +3,7 @@ import type { Logger } from '@n8n/backend-common'; import type { Project, User, WorkflowEntity } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import type { ProjectService } from '@/services/project.service.ee'; import type { WebhookService } from '@/webhooks/webhook.service'; import type { WorkflowService } from '@/workflows/workflow.service'; diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-remover.test.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-remover.test.ts index 50ddac57252..6e2301f2dfb 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-remover.test.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/__tests__/workflow-remover.test.ts @@ -1,7 +1,7 @@ import type { User, WorkflowEntity } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError } from '@n8n/errors'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import type { WorkflowService } from '@/workflows/workflow.service'; diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-importer.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-importer.ts index 5d46d9df2eb..f3d6ba55b69 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-importer.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-importer.ts @@ -1,7 +1,7 @@ import { WorkflowEntity } from '@n8n/db'; import { Service } from '@n8n/di'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { WorkflowCreationService, type WorkflowCreateBatchContext, diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-publisher.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-publisher.ts index f362f3458f1..5e6f8aeade6 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-publisher.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-publisher.ts @@ -4,8 +4,7 @@ import { ProjectRepository } from '@n8n/db'; import { Service } from '@n8n/di'; import { ensureError } from '@n8n/utils/errors/ensure-error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import { ProjectService } from '@/services/project.service.ee'; import { WEBHOOK_CONFLICT_MESSAGE } from '@/webhooks/constants'; import { WebhookService } from '@/webhooks/webhook.service'; diff --git a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-remover.ts b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-remover.ts index d0f896f09e7..7c47c1bba3d 100644 --- a/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-remover.ts +++ b/packages/cli/src/modules/n8n-packages/entities/workflow/workflow-remover.ts @@ -1,6 +1,6 @@ import { Service } from '@n8n/di'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError } from '@n8n/errors'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { WorkflowService } from '@/workflows/workflow.service'; diff --git a/packages/cli/src/modules/n8n-packages/io/__tests__/directory-package-reader.test.ts b/packages/cli/src/modules/n8n-packages/io/__tests__/directory-package-reader.test.ts index cbf72067803..d5f5b499083 100644 --- a/packages/cli/src/modules/n8n-packages/io/__tests__/directory-package-reader.test.ts +++ b/packages/cli/src/modules/n8n-packages/io/__tests__/directory-package-reader.test.ts @@ -2,7 +2,7 @@ import { mkdir, mkdtemp, rm, symlink, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import path from 'node:path'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { DirectoryPackageReader } from '../directory/directory-package-reader'; diff --git a/packages/cli/src/modules/n8n-packages/io/__tests__/tar-package-reader.test.ts b/packages/cli/src/modules/n8n-packages/io/__tests__/tar-package-reader.test.ts index d54d385d4ec..117cf125b58 100644 --- a/packages/cli/src/modules/n8n-packages/io/__tests__/tar-package-reader.test.ts +++ b/packages/cli/src/modules/n8n-packages/io/__tests__/tar-package-reader.test.ts @@ -1,4 +1,4 @@ -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { streamToBuffer } from '../../__tests__/utils/tar-support'; import { TarPackageReader, type TarReaderLimits } from '../tar/tar-package-reader'; diff --git a/packages/cli/src/modules/n8n-packages/io/directory/directory-package-reader.ts b/packages/cli/src/modules/n8n-packages/io/directory/directory-package-reader.ts index 49b4e5e54d1..402bb55b8e1 100644 --- a/packages/cli/src/modules/n8n-packages/io/directory/directory-package-reader.ts +++ b/packages/cli/src/modules/n8n-packages/io/directory/directory-package-reader.ts @@ -3,7 +3,7 @@ import type { Stats } from 'node:fs'; import { lstat, readdir, readFile } from 'node:fs/promises'; import path from 'node:path'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { PackageManifest } from '../../spec/manifest.schema'; import type { PackageReader } from '../package-reader'; diff --git a/packages/cli/src/modules/n8n-packages/io/tar/tar-package-reader.ts b/packages/cli/src/modules/n8n-packages/io/tar/tar-package-reader.ts index fc91308d02f..c38faa3889c 100644 --- a/packages/cli/src/modules/n8n-packages/io/tar/tar-package-reader.ts +++ b/packages/cli/src/modules/n8n-packages/io/tar/tar-package-reader.ts @@ -2,7 +2,7 @@ import { jsonParse } from 'n8n-workflow'; import path from 'node:path'; import { Parser, type ReadEntry } from 'tar'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { PackageManifest } from '../../spec/manifest.schema'; import type { PackageReader } from '../package-reader'; diff --git a/packages/cli/src/modules/n8n-packages/n8n-packages.service.ts b/packages/cli/src/modules/n8n-packages/n8n-packages.service.ts index 748405498ef..6c6d0ff2be9 100644 --- a/packages/cli/src/modules/n8n-packages/n8n-packages.service.ts +++ b/packages/cli/src/modules/n8n-packages/n8n-packages.service.ts @@ -3,8 +3,7 @@ import { Service } from '@n8n/di'; import { InstanceSettings } from 'n8n-core'; import { N8N_VERSION } from '@/constants'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { buildImportResult, toPackageSummary } from './engine/import-result'; diff --git a/packages/cli/src/modules/n8n-packages/package-failure-classifier.ts b/packages/cli/src/modules/n8n-packages/package-failure-classifier.ts index 8a9353632d5..6c25e50578a 100644 --- a/packages/cli/src/modules/n8n-packages/package-failure-classifier.ts +++ b/packages/cli/src/modules/n8n-packages/package-failure-classifier.ts @@ -1,10 +1,12 @@ import { UserError } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { + BadRequestError, + ConflictError, + ForbiddenError, + NotFoundError, + UnprocessableRequestError, +} from '@n8n/errors'; import { PackageEntityAccessDeniedError, diff --git a/packages/cli/src/modules/n8n-packages/utils/import-package-upload.ts b/packages/cli/src/modules/n8n-packages/utils/import-package-upload.ts index 452dfb78fa2..47b0214d51f 100644 --- a/packages/cli/src/modules/n8n-packages/utils/import-package-upload.ts +++ b/packages/cli/src/modules/n8n-packages/utils/import-package-upload.ts @@ -5,7 +5,7 @@ import { import type { GlobalConfig } from '@n8n/config'; import multer from 'multer'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; /** * Allowed keys on `req.body` after multipart parsing. Includes `package` because diff --git a/packages/cli/src/modules/oauth-server/__tests__/oauth-consent.service.test.ts b/packages/cli/src/modules/oauth-server/__tests__/oauth-consent.service.test.ts index 1b662fbaa04..8d5686731c6 100644 --- a/packages/cli/src/modules/oauth-server/__tests__/oauth-consent.service.test.ts +++ b/packages/cli/src/modules/oauth-server/__tests__/oauth-consent.service.test.ts @@ -16,7 +16,7 @@ import { } from '@/services/protected-resource.registry'; import { UrlService } from '@n8n/backend-services'; import type { User } from '@n8n/db'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; const issuer = 'https://n8n.example.com'; diff --git a/packages/cli/src/modules/oauth-server/oauth-clients.controller.ts b/packages/cli/src/modules/oauth-server/oauth-clients.controller.ts index 2277f1238e4..4c8573d2210 100644 --- a/packages/cli/src/modules/oauth-server/oauth-clients.controller.ts +++ b/packages/cli/src/modules/oauth-server/oauth-clients.controller.ts @@ -12,8 +12,7 @@ import { Delete, Get, GlobalScope, Param, Query, RestController } from '@n8n/dec import { hasGlobalScope } from '@n8n/permissions'; import type { Response } from 'express'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import { OAuthServerService } from './oauth-server.service'; diff --git a/packages/cli/src/modules/oauth-server/oauth-consent.controller.ts b/packages/cli/src/modules/oauth-server/oauth-consent.controller.ts index 96cca337058..fce3e5259e0 100644 --- a/packages/cli/src/modules/oauth-server/oauth-consent.controller.ts +++ b/packages/cli/src/modules/oauth-server/oauth-consent.controller.ts @@ -7,7 +7,7 @@ import { UserError } from 'n8n-workflow'; import { ApproveConsentRequestDto } from './dto/approve-consent-request.dto'; import { OAuthConsentService } from './oauth-consent.service'; import { OAuthSessionService } from './oauth-session.service'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; @RestController('/consent') export class OAuthConsentController { diff --git a/packages/cli/src/modules/oauth-server/oauth-consent.service.ts b/packages/cli/src/modules/oauth-server/oauth-consent.service.ts index 5829df3bf3d..77c2cec1817 100644 --- a/packages/cli/src/modules/oauth-server/oauth-consent.service.ts +++ b/packages/cli/src/modules/oauth-server/oauth-consent.service.ts @@ -9,7 +9,7 @@ import { UserConsentRepository } from './database/repositories/oauth-user-consen import { OAuthAuthorizationCodeService } from './oauth-authorization-code.service'; import { OAuthSessionService, type OAuthSessionPayload } from './oauth-session.service'; import { OAuthHelpers } from './oauth.helpers'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { ProtectedResourceRegistry, type ProtectedResource, diff --git a/packages/cli/src/modules/oauth-server/oauth-server.service.ts b/packages/cli/src/modules/oauth-server/oauth-server.service.ts index ec2cd83fdff..8ebd56c3692 100644 --- a/packages/cli/src/modules/oauth-server/oauth-server.service.ts +++ b/packages/cli/src/modules/oauth-server/oauth-server.service.ts @@ -24,7 +24,7 @@ import { hasGlobalScope } from '@n8n/permissions'; import type { Response } from 'express'; import { AuthService } from '@/auth/auth.service'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { ProtectedResourceRegistry, diff --git a/packages/cli/src/modules/oauth-server/oauth.errors.ts b/packages/cli/src/modules/oauth-server/oauth.errors.ts index 1a2a8464e85..a75b4beafc0 100644 --- a/packages/cli/src/modules/oauth-server/oauth.errors.ts +++ b/packages/cli/src/modules/oauth-server/oauth.errors.ts @@ -1,6 +1,6 @@ import { ServerError } from '@modelcontextprotocol/sdk/server/auth/errors.js'; -import { AuthError } from '@/errors/response-errors/auth.error'; +import { AuthError } from '@n8n/errors'; // The wording references MCP because the instance MCP server is currently the // only protected resource; clients registered via its DCR endpoint see this diff --git a/packages/cli/src/modules/promotions.ee/__tests__/promotion-config.resolver.integration.test.ts b/packages/cli/src/modules/promotions.ee/__tests__/promotion-config.resolver.integration.test.ts index 13617199737..2d99b951105 100644 --- a/packages/cli/src/modules/promotions.ee/__tests__/promotion-config.resolver.integration.test.ts +++ b/packages/cli/src/modules/promotions.ee/__tests__/promotion-config.resolver.integration.test.ts @@ -9,7 +9,7 @@ import type { User } from '@n8n/db'; import { ProjectRepository } from '@n8n/db'; import { Container } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { createOwner } from '@test-integration/db/users'; import { PromotionConfigRepository } from '../database/repositories/promotion-config.repository'; diff --git a/packages/cli/src/modules/promotions.ee/__tests__/promotion-connections.service.test.ts b/packages/cli/src/modules/promotions.ee/__tests__/promotion-connections.service.test.ts index 276f6c351ab..a6aa0cdf1ad 100644 --- a/packages/cli/src/modules/promotions.ee/__tests__/promotion-connections.service.test.ts +++ b/packages/cli/src/modules/promotions.ee/__tests__/promotion-connections.service.test.ts @@ -8,9 +8,7 @@ import path from 'node:path'; import type { MockedFunction } from 'vitest'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ConflictError, ForbiddenError } from '@n8n/errors'; import { userHasScopes } from '@/permissions.ee/check-access'; import type { PromotionConnection } from '../database/entities/promotion-connection.entity'; diff --git a/packages/cli/src/modules/promotions.ee/__tests__/promotion-providers.service.test.ts b/packages/cli/src/modules/promotions.ee/__tests__/promotion-providers.service.test.ts index 3179fdee9b2..5b98ec25a2a 100644 --- a/packages/cli/src/modules/promotions.ee/__tests__/promotion-providers.service.test.ts +++ b/packages/cli/src/modules/promotions.ee/__tests__/promotion-providers.service.test.ts @@ -2,9 +2,7 @@ import type { CreatePromotionProviderDto, UpdatePromotionProviderDto } from '@n8 import type { Cipher } from 'n8n-core'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, NotFoundError } from '@n8n/errors'; import type { PromotionProvider } from '../database/entities/promotion-provider.entity'; import type { PromotionConnectionRepository } from '../database/repositories/promotion-connection.repository'; diff --git a/packages/cli/src/modules/promotions.ee/__tests__/promotions-git.service.test.ts b/packages/cli/src/modules/promotions.ee/__tests__/promotions-git.service.test.ts index 3c9f8af6eaf..a6467d0bdbd 100644 --- a/packages/cli/src/modules/promotions.ee/__tests__/promotions-git.service.test.ts +++ b/packages/cli/src/modules/promotions.ee/__tests__/promotions-git.service.test.ts @@ -6,8 +6,7 @@ import { tmpdir } from 'node:os'; import path from 'node:path'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error'; +import { BadRequestError, ServiceUnavailableError } from '@n8n/errors'; import { PromotionsGitService } from '../promotions-git.service'; diff --git a/packages/cli/src/modules/promotions.ee/__tests__/promotions-package.integration.test.ts b/packages/cli/src/modules/promotions.ee/__tests__/promotions-package.integration.test.ts index 7d65cf54a6f..7b9a409d3c5 100644 --- a/packages/cli/src/modules/promotions.ee/__tests__/promotions-package.integration.test.ts +++ b/packages/cli/src/modules/promotions.ee/__tests__/promotions-package.integration.test.ts @@ -37,7 +37,7 @@ import { mock } from 'vitest-mock-extended'; import { CredentialTypes } from '@/credential-types'; import { ActiveWorkflowManager } from '@/active-workflow-manager'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { mockDataTableSizeValidator } from '@/modules/data-table/__tests__/test-helpers'; import { DataTableRepository } from '@/modules/data-table/data-table.repository'; import { DataTableColumnRepository } from '@/modules/data-table/data-table-column.repository'; diff --git a/packages/cli/src/modules/promotions.ee/__tests__/promotions.service.test.ts b/packages/cli/src/modules/promotions.ee/__tests__/promotions.service.test.ts index 7b93c3b7d02..4fa1e057a8f 100644 --- a/packages/cli/src/modules/promotions.ee/__tests__/promotions.service.test.ts +++ b/packages/cli/src/modules/promotions.ee/__tests__/promotions.service.test.ts @@ -7,9 +7,7 @@ import { tmpdir } from 'node:os'; import path from 'node:path'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error'; +import { BadRequestError, NotFoundError, ServiceUnavailableError } from '@n8n/errors'; import type { N8nPackagesService } from '@/modules/n8n-packages/n8n-packages.service'; import { MissingWorkflowDependencyPolicy, diff --git a/packages/cli/src/modules/promotions.ee/promotion-change.service.ts b/packages/cli/src/modules/promotions.ee/promotion-change.service.ts index 2ab654087bb..240ddb472a7 100644 --- a/packages/cli/src/modules/promotions.ee/promotion-change.service.ts +++ b/packages/cli/src/modules/promotions.ee/promotion-change.service.ts @@ -11,8 +11,7 @@ import { hasGlobalScope, type Scope } from '@n8n/permissions'; import { jsonParse } from 'n8n-workflow'; import { randomUUID } from 'node:crypto'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { HashingPackageWriter } from '@/modules/n8n-packages/io/hashing-package-writer'; import { PACKAGE_ENTITY_LAYOUT, diff --git a/packages/cli/src/modules/promotions.ee/promotion-changes.controller.ts b/packages/cli/src/modules/promotions.ee/promotion-changes.controller.ts index 5ed5615decb..9da545e5e57 100644 --- a/packages/cli/src/modules/promotions.ee/promotion-changes.controller.ts +++ b/packages/cli/src/modules/promotions.ee/promotion-changes.controller.ts @@ -8,7 +8,7 @@ import { AuthenticatedRequest } from '@n8n/db'; import { Get, GlobalScope, Licensed, Param, Query, RestController } from '@n8n/decorators'; import type { Response } from 'express'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { PromotionChangeService } from './promotion-change.service'; diff --git a/packages/cli/src/modules/promotions.ee/promotion-config.resolver.ts b/packages/cli/src/modules/promotions.ee/promotion-config.resolver.ts index a541284ee9a..ed4c285ca47 100644 --- a/packages/cli/src/modules/promotions.ee/promotion-config.resolver.ts +++ b/packages/cli/src/modules/promotions.ee/promotion-config.resolver.ts @@ -3,8 +3,7 @@ import { promotionConnectionTargetSchema, promotionProviderTypeSchema } from '@n import { ProjectRepository } from '@n8n/db'; import { Service } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import type { PromotionConfig } from './database/entities/promotion-config.entity'; import { PromotionConfigRepository } from './database/repositories/promotion-config.repository'; diff --git a/packages/cli/src/modules/promotions.ee/promotion-conflicts.ts b/packages/cli/src/modules/promotions.ee/promotion-conflicts.ts index 51a01c8a006..1d19886e88a 100644 --- a/packages/cli/src/modules/promotions.ee/promotion-conflicts.ts +++ b/packages/cli/src/modules/promotions.ee/promotion-conflicts.ts @@ -1,4 +1,4 @@ -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError } from '@n8n/errors'; import { PromotionConflictError } from './database/promotion-conflict.error'; diff --git a/packages/cli/src/modules/promotions.ee/promotion-connections.service.ts b/packages/cli/src/modules/promotions.ee/promotion-connections.service.ts index 75389ea50e5..51d124764fd 100644 --- a/packages/cli/src/modules/promotions.ee/promotion-connections.service.ts +++ b/packages/cli/src/modules/promotions.ee/promotion-connections.service.ts @@ -19,10 +19,7 @@ import { Logger } from '@n8n/backend-common'; import { ProjectRepository, TransactionRunner, type OperationContext, type User } from '@n8n/db'; import { Service } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from '@n8n/errors'; import { userHasScopes } from '@/permissions.ee/check-access'; import { DIRECTION_LABELS } from './constants'; diff --git a/packages/cli/src/modules/promotions.ee/promotion-providers.service.ts b/packages/cli/src/modules/promotions.ee/promotion-providers.service.ts index 64725492789..dbc2e8aa2e5 100644 --- a/packages/cli/src/modules/promotions.ee/promotion-providers.service.ts +++ b/packages/cli/src/modules/promotions.ee/promotion-providers.service.ts @@ -11,9 +11,7 @@ import { Service } from '@n8n/di'; import { Cipher } from 'n8n-core'; import { jsonParse } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, NotFoundError } from '@n8n/errors'; import type { PromotionProvider } from './database/entities/promotion-provider.entity'; import { PromotionConnectionRepository } from './database/repositories/promotion-connection.repository'; diff --git a/packages/cli/src/modules/promotions.ee/promotions-git.service.ts b/packages/cli/src/modules/promotions.ee/promotions-git.service.ts index ddbe299e599..a48bb740c46 100644 --- a/packages/cli/src/modules/promotions.ee/promotions-git.service.ts +++ b/packages/cli/src/modules/promotions.ee/promotions-git.service.ts @@ -14,8 +14,7 @@ import { type SimpleGitOptions, } from 'simple-git'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error'; +import { BadRequestError, ServiceUnavailableError } from '@n8n/errors'; import { GIT_COMMAND_STALL_TIMEOUT_MS, diff --git a/packages/cli/src/modules/promotions.ee/promotions.service.ts b/packages/cli/src/modules/promotions.ee/promotions.service.ts index 3ff1fa23a0d..4eda6fb7294 100644 --- a/packages/cli/src/modules/promotions.ee/promotions.service.ts +++ b/packages/cli/src/modules/promotions.ee/promotions.service.ts @@ -15,8 +15,7 @@ import { cp, mkdir, mkdtemp, rename, rm, stat } from 'node:fs/promises'; import path from 'node:path'; import { UnexpectedError } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { N8nPackagesService } from '@/modules/n8n-packages/n8n-packages.service'; import { MANIFEST_FILE } from '@/modules/n8n-packages/spec/constants'; import { diff --git a/packages/cli/src/modules/promotions.ee/working-copy-updater.ts b/packages/cli/src/modules/promotions.ee/working-copy-updater.ts index 4b650a2c4e2..b34bf7baf06 100644 --- a/packages/cli/src/modules/promotions.ee/working-copy-updater.ts +++ b/packages/cli/src/modules/promotions.ee/working-copy-updater.ts @@ -7,8 +7,7 @@ import * as fs from 'node:fs/promises'; import path from 'node:path'; import { z } from 'zod'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; +import { BadRequestError, InternalServerError } from '@n8n/errors'; import { MANIFEST_FILE } from '@/modules/n8n-packages/spec/constants'; import type { ManifestEntry, PackageManifest } from '@/modules/n8n-packages/spec/manifest.schema'; diff --git a/packages/cli/src/modules/provisioning.ee/__tests__/provisioning.service.ee.test.ts b/packages/cli/src/modules/provisioning.ee/__tests__/provisioning.service.ee.test.ts index efaf5dea11f..3ffd9c24f99 100644 --- a/packages/cli/src/modules/provisioning.ee/__tests__/provisioning.service.ee.test.ts +++ b/packages/cli/src/modules/provisioning.ee/__tests__/provisioning.service.ee.test.ts @@ -16,8 +16,7 @@ import type { EntityManager } from '@n8n/typeorm'; import { type InstanceSettings } from 'n8n-core'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { type EventService } from '@/events/event.service'; import { ProvisioningService } from '@/modules/provisioning.ee/provisioning.service.ee'; import { type RoleMappingRuleService } from '@/modules/provisioning.ee/role-mapping-rule.service.ee'; diff --git a/packages/cli/src/modules/provisioning.ee/__tests__/role-mapping-rule.service.ee.test.ts b/packages/cli/src/modules/provisioning.ee/__tests__/role-mapping-rule.service.ee.test.ts index 677a9dfa4cd..4fbd7a9af5a 100644 --- a/packages/cli/src/modules/provisioning.ee/__tests__/role-mapping-rule.service.ee.test.ts +++ b/packages/cli/src/modules/provisioning.ee/__tests__/role-mapping-rule.service.ee.test.ts @@ -2,9 +2,7 @@ import { QueryFailedError } from '@n8n/typeorm'; import { mock } from 'vitest-mock-extended'; import { RoleMappingRuleService } from '@/modules/provisioning.ee/role-mapping-rule.service.ee'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, NotFoundError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { UserLike } from '@/events/maps/relay.event-map'; import type { diff --git a/packages/cli/src/modules/provisioning.ee/__tests__/role-mapping-rule.validation.test.ts b/packages/cli/src/modules/provisioning.ee/__tests__/role-mapping-rule.validation.test.ts index fdc61ec5b13..6741d2cae04 100644 --- a/packages/cli/src/modules/provisioning.ee/__tests__/role-mapping-rule.validation.test.ts +++ b/packages/cli/src/modules/provisioning.ee/__tests__/role-mapping-rule.validation.test.ts @@ -1,6 +1,6 @@ import type { Role } from '@n8n/db'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { assertAndNormalizeProjectIdsForRuleType, diff --git a/packages/cli/src/modules/provisioning.ee/errors/sso-access-denied.error.ts b/packages/cli/src/modules/provisioning.ee/errors/sso-access-denied.error.ts index 04b4d724c74..74f308a7dc9 100644 --- a/packages/cli/src/modules/provisioning.ee/errors/sso-access-denied.error.ts +++ b/packages/cli/src/modules/provisioning.ee/errors/sso-access-denied.error.ts @@ -1,4 +1,4 @@ -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; /** * An SSO login denied because role mapping resolved to "Block access". diff --git a/packages/cli/src/modules/provisioning.ee/provisioning.controller.ee.ts b/packages/cli/src/modules/provisioning.ee/provisioning.controller.ee.ts index e065feb3736..9b0a1b3812b 100644 --- a/packages/cli/src/modules/provisioning.ee/provisioning.controller.ee.ts +++ b/packages/cli/src/modules/provisioning.ee/provisioning.controller.ee.ts @@ -4,7 +4,7 @@ import { AuthenticatedRequest } from '@n8n/db'; import { Get, GlobalScope, Patch, RestController } from '@n8n/decorators'; import { Response } from 'express'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { ProvisioningService } from './provisioning.service.ee'; diff --git a/packages/cli/src/modules/provisioning.ee/provisioning.service.ee.ts b/packages/cli/src/modules/provisioning.ee/provisioning.service.ee.ts index 5d5ef510f55..281113eea01 100644 --- a/packages/cli/src/modules/provisioning.ee/provisioning.service.ee.ts +++ b/packages/cli/src/modules/provisioning.ee/provisioning.service.ee.ts @@ -24,7 +24,7 @@ import { InstanceSettings } from 'n8n-core'; import { jsonParse } from 'n8n-workflow'; import { ZodError } from 'zod'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { Publisher } from '@/scaling/pubsub/publisher.service'; import { ProjectService } from '@/services/project.service.ee'; diff --git a/packages/cli/src/modules/provisioning.ee/role-mapping-rule.service.ee.ts b/packages/cli/src/modules/provisioning.ee/role-mapping-rule.service.ee.ts index fe2de8bcc55..d13439ad1e1 100644 --- a/packages/cli/src/modules/provisioning.ee/role-mapping-rule.service.ee.ts +++ b/packages/cli/src/modules/provisioning.ee/role-mapping-rule.service.ee.ts @@ -15,9 +15,7 @@ import { Service } from '@n8n/di'; import { type EntityManager, type FindOptionsOrder, In } from '@n8n/typeorm'; import type { z } from 'zod'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import type { UserLike } from '@/events/maps/relay.event-map'; diff --git a/packages/cli/src/modules/provisioning.ee/role-mapping-rule.validation.ts b/packages/cli/src/modules/provisioning.ee/role-mapping-rule.validation.ts index 63083a2e947..96524f6390f 100644 --- a/packages/cli/src/modules/provisioning.ee/role-mapping-rule.validation.ts +++ b/packages/cli/src/modules/provisioning.ee/role-mapping-rule.validation.ts @@ -1,6 +1,6 @@ import type { Role } from '@n8n/db'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; export function assertRoleCompatibleWithMappingType( role: Role, diff --git a/packages/cli/src/modules/quick-connect/__tests__/quick-connect.service.test.ts b/packages/cli/src/modules/quick-connect/__tests__/quick-connect.service.test.ts index 37736016f2c..7f3b2e7aa96 100644 --- a/packages/cli/src/modules/quick-connect/__tests__/quick-connect.service.test.ts +++ b/packages/cli/src/modules/quick-connect/__tests__/quick-connect.service.test.ts @@ -3,7 +3,7 @@ import type { User } from '@n8n/db'; import { Container } from '@n8n/di'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { IQuickConnectHandler } from '../handlers/handler.interface'; import { QuickConnectConfig } from '../quick-connect.config'; diff --git a/packages/cli/src/modules/quick-connect/quick-connect.service.ts b/packages/cli/src/modules/quick-connect/quick-connect.service.ts index 71ae0aeee5c..a141032facf 100644 --- a/packages/cli/src/modules/quick-connect/quick-connect.service.ts +++ b/packages/cli/src/modules/quick-connect/quick-connect.service.ts @@ -2,7 +2,7 @@ import { Logger } from '@n8n/backend-common'; import type { User } from '@n8n/db'; import { Container, Service } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { IQuickConnectHandler, diff --git a/packages/cli/src/modules/redaction/__tests__/redaction-enforcement.service.test.ts b/packages/cli/src/modules/redaction/__tests__/redaction-enforcement.service.test.ts index c0f4d2bd842..e91b9e779e3 100644 --- a/packages/cli/src/modules/redaction/__tests__/redaction-enforcement.service.test.ts +++ b/packages/cli/src/modules/redaction/__tests__/redaction-enforcement.service.test.ts @@ -2,7 +2,7 @@ import type { RedactionFloor } from '@n8n/api-types'; import type { WorkflowSettings } from 'n8n-workflow'; import { mock } from 'vitest-mock-extended'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { UnprocessableRequestError } from '@n8n/errors'; import type { InstanceRedactionEnforcementService } from '../instance-redaction-enforcement.service'; import { RedactionEnforcementService } from '../redaction-enforcement.service'; diff --git a/packages/cli/src/modules/redaction/executions/__tests__/execution-redaction.service.test.ts b/packages/cli/src/modules/redaction/executions/__tests__/execution-redaction.service.test.ts index c4147313f2e..f8fcfed949e 100644 --- a/packages/cli/src/modules/redaction/executions/__tests__/execution-redaction.service.test.ts +++ b/packages/cli/src/modules/redaction/executions/__tests__/execution-redaction.service.test.ts @@ -5,8 +5,7 @@ import type { INode, IRunExecutionData, ITaskData, WorkflowExecuteMode } from 'n import { shouldRedactConsoleOutput } from 'n8n-workflow'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { ScopeForbiddenError } from '@/errors/response-errors/scope-forbidden.error'; +import { ForbiddenError, ScopeForbiddenError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { ExecutionRedactionOptions, diff --git a/packages/cli/src/modules/redaction/executions/execution-redaction.service.ts b/packages/cli/src/modules/redaction/executions/execution-redaction.service.ts index 04bda02657a..26d64b7ea9e 100644 --- a/packages/cli/src/modules/redaction/executions/execution-redaction.service.ts +++ b/packages/cli/src/modules/redaction/executions/execution-redaction.service.ts @@ -9,8 +9,7 @@ import { } from 'n8n-workflow'; import { isCredSharingEnabled } from '@/constants/credential-sharing'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { ScopeForbiddenError } from '@/errors/response-errors/scope-forbidden.error'; +import { ForbiddenError, ScopeForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import type { ExecutionRedaction, diff --git a/packages/cli/src/modules/redaction/redaction-enforcement.service.ts b/packages/cli/src/modules/redaction/redaction-enforcement.service.ts index 2faf008f7cc..0950d9c8979 100644 --- a/packages/cli/src/modules/redaction/redaction-enforcement.service.ts +++ b/packages/cli/src/modules/redaction/redaction-enforcement.service.ts @@ -1,7 +1,7 @@ import { Service } from '@n8n/di'; import type { WorkflowSettings } from 'n8n-workflow'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { UnprocessableRequestError } from '@n8n/errors'; import { InstanceRedactionEnforcementService } from './instance-redaction-enforcement.service'; import { policyMeetsFloor, REDACTION_FLOOR_VIOLATION_MESSAGE } from './redaction-policy'; diff --git a/packages/cli/src/modules/source-control.ee/__tests__/source-control-scoped.service.test.ts b/packages/cli/src/modules/source-control.ee/__tests__/source-control-scoped.service.test.ts index 567dc42f7b8..fcb4dd8512e 100644 --- a/packages/cli/src/modules/source-control.ee/__tests__/source-control-scoped.service.test.ts +++ b/packages/cli/src/modules/source-control.ee/__tests__/source-control-scoped.service.test.ts @@ -2,7 +2,7 @@ import type { AuthenticatedRequest, Project } from '@n8n/db'; import { hasGlobalScope } from '@n8n/permissions'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { SourceControlContextFactory } from '../source-control-context.factory'; import { SourceControlScopedService } from '../source-control-scoped.service'; diff --git a/packages/cli/src/modules/source-control.ee/__tests__/source-control-status.service.test.ts b/packages/cli/src/modules/source-control.ee/__tests__/source-control-status.service.test.ts index 102cceb7409..51a9aec8006 100644 --- a/packages/cli/src/modules/source-control.ee/__tests__/source-control-status.service.test.ts +++ b/packages/cli/src/modules/source-control.ee/__tests__/source-control-status.service.test.ts @@ -16,7 +16,7 @@ import { Container } from '@n8n/di'; import { InstanceSettings } from 'n8n-core'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { SourceControlContextFactory } from '../source-control-context.factory'; diff --git a/packages/cli/src/modules/source-control.ee/__tests__/source-control.controller.ee.test.ts b/packages/cli/src/modules/source-control.ee/__tests__/source-control.controller.ee.test.ts index fb643244c66..f861f3435b0 100644 --- a/packages/cli/src/modules/source-control.ee/__tests__/source-control.controller.ee.test.ts +++ b/packages/cli/src/modules/source-control.ee/__tests__/source-control.controller.ee.test.ts @@ -7,7 +7,7 @@ import type { Response } from 'express'; import type { Mock } from 'vitest'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { SourceControlContextFactory } from '../source-control-context.factory'; diff --git a/packages/cli/src/modules/source-control.ee/__tests__/source-control.service.test.ts b/packages/cli/src/modules/source-control.ee/__tests__/source-control.service.test.ts index 3506ab1aedf..ac576cd97c1 100644 --- a/packages/cli/src/modules/source-control.ee/__tests__/source-control.service.test.ts +++ b/packages/cli/src/modules/source-control.ee/__tests__/source-control.service.test.ts @@ -9,7 +9,7 @@ import type { CommitResult, PullResult, PushResult } from 'simple-git'; import { SourceControlPreferencesService } from '@/modules/source-control.ee/source-control-preferences.service.ee'; import { SourceControlService } from '@/modules/source-control.ee/source-control.service.ee'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { SourceControlExportService } from '../source-control-export.service.ee'; import type { SourceControlGitService } from '../source-control-git.service.ee'; diff --git a/packages/cli/src/modules/source-control.ee/source-control-scoped.service.ts b/packages/cli/src/modules/source-control.ee/source-control-scoped.service.ts index 330c2279605..ec045d18310 100644 --- a/packages/cli/src/modules/source-control.ee/source-control-scoped.service.ts +++ b/packages/cli/src/modules/source-control.ee/source-control-scoped.service.ts @@ -11,7 +11,7 @@ import { Service } from '@n8n/di'; import { hasGlobalScope } from '@n8n/permissions'; import type { FindOptionsWhere } from '@n8n/typeorm'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { DataTable } from '@/modules/data-table/data-table.entity'; import { SourceControlContextFactory } from './source-control-context.factory'; diff --git a/packages/cli/src/modules/source-control.ee/source-control-status.service.ee.ts b/packages/cli/src/modules/source-control.ee/source-control-status.service.ee.ts index 33730c09bb7..25c3d6e2128 100644 --- a/packages/cli/src/modules/source-control.ee/source-control-status.service.ee.ts +++ b/packages/cli/src/modules/source-control.ee/source-control-status.service.ee.ts @@ -7,7 +7,7 @@ import { In } from '@n8n/typeorm'; import pick from 'lodash/pick'; import { UserError } from 'n8n-workflow'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { ExportableTagEntity } from '@/modules/source-control.ee/types/exportable-tags'; diff --git a/packages/cli/src/modules/source-control.ee/source-control.controller.ee.ts b/packages/cli/src/modules/source-control.ee/source-control.controller.ee.ts index 3b8e5b9839e..6b706986514 100644 --- a/packages/cli/src/modules/source-control.ee/source-control.controller.ee.ts +++ b/packages/cli/src/modules/source-control.ee/source-control.controller.ee.ts @@ -22,8 +22,7 @@ import { SourceControlRequest } from './types/requests'; import { SourceControlGetStatus } from './types/source-control-get-status'; import type { SourceControlPreferences } from './types/source-control-preferences'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { IWorkflowToImport } from '@/interfaces'; diff --git a/packages/cli/src/modules/source-control.ee/source-control.service.ee.ts b/packages/cli/src/modules/source-control.ee/source-control.service.ee.ts index 41a60c396a8..05f88531c5e 100644 --- a/packages/cli/src/modules/source-control.ee/source-control.service.ee.ts +++ b/packages/cli/src/modules/source-control.ee/source-control.service.ee.ts @@ -9,8 +9,7 @@ import pLimit from 'p-limit'; import * as path from 'path'; import type { PushResult } from 'simple-git'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { IWorkflowToImport } from '@/interfaces'; diff --git a/packages/cli/src/modules/sso-oidc/__tests__/oidc.controller.ee.test.ts b/packages/cli/src/modules/sso-oidc/__tests__/oidc.controller.ee.test.ts index f6b0708ab90..77d5e58f469 100644 --- a/packages/cli/src/modules/sso-oidc/__tests__/oidc.controller.ee.test.ts +++ b/packages/cli/src/modules/sso-oidc/__tests__/oidc.controller.ee.test.ts @@ -7,7 +7,7 @@ import { mock } from 'vitest-mock-extended'; import type { AuthService } from '@/auth/auth.service'; import { OIDC_NONCE_COOKIE_NAME, OIDC_STATE_COOKIE_NAME } from '@/constants'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import { SsoAccessDeniedError } from '@/modules/provisioning.ee/errors/sso-access-denied.error'; import type { AuthlessRequest } from '@/requests'; diff --git a/packages/cli/src/modules/sso-oidc/__tests__/oidc.service.ee.test.ts b/packages/cli/src/modules/sso-oidc/__tests__/oidc.service.ee.test.ts index 045deebae4b..0b38af59e38 100644 --- a/packages/cli/src/modules/sso-oidc/__tests__/oidc.service.ee.test.ts +++ b/packages/cli/src/modules/sso-oidc/__tests__/oidc.service.ee.test.ts @@ -22,8 +22,7 @@ vi.mock('openid-client', async (importOriginal) => { }; }); -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { type ProvisioningService } from '@/modules/provisioning.ee/provisioning.service.ee'; import { Publisher } from '@/scaling/pubsub/publisher.service'; import type { JwtService } from '@/services/jwt.service'; diff --git a/packages/cli/src/modules/sso-oidc/oidc.controller.ee.ts b/packages/cli/src/modules/sso-oidc/oidc.controller.ee.ts index c071b6dc995..72eb004241c 100644 --- a/packages/cli/src/modules/sso-oidc/oidc.controller.ee.ts +++ b/packages/cli/src/modules/sso-oidc/oidc.controller.ee.ts @@ -8,8 +8,7 @@ import { Request, Response } from 'express'; import { AuthService } from '@/auth/auth.service'; import { OIDC_NONCE_COOKIE_NAME, OIDC_STATE_COOKIE_NAME } from '@/constants'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { SSO_ACCESS_DENIED_REDIRECT_PATH, diff --git a/packages/cli/src/modules/sso-oidc/oidc.service.ee.ts b/packages/cli/src/modules/sso-oidc/oidc.service.ee.ts index e615e02bc6f..ad7647bd2b3 100644 --- a/packages/cli/src/modules/sso-oidc/oidc.service.ee.ts +++ b/packages/cli/src/modules/sso-oidc/oidc.service.ee.ts @@ -19,8 +19,7 @@ import { jsonParse, UserError } from 'n8n-workflow'; import type * as openidClientTypes from 'openid-client'; import { inspect } from 'util'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { buildOidcClaimsContext } from '@/modules/provisioning.ee/claims-context.builder'; import { ProvisioningService } from '@/modules/provisioning.ee/provisioning.service.ee'; import { JwtService } from '@/services/jwt.service'; diff --git a/packages/cli/src/modules/sso-saml/__tests__/saml.service.ee.test.ts b/packages/cli/src/modules/sso-saml/__tests__/saml.service.ee.test.ts index 0e30deb43a5..c2ad059a137 100644 --- a/packages/cli/src/modules/sso-saml/__tests__/saml.service.ee.test.ts +++ b/packages/cli/src/modules/sso-saml/__tests__/saml.service.ee.test.ts @@ -12,8 +12,7 @@ import type { Cipher, InstanceSettings } from 'n8n-core'; import { CREDENTIAL_BLANKING_VALUE } from 'n8n-workflow'; import type { IdentityProviderInstance, ServiceProviderInstance } from 'samlify'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import type { ProvisioningService } from '@/modules/provisioning.ee/provisioning.service.ee'; import { Publisher } from '@/scaling/pubsub/publisher.service'; import type { CacheService } from '@/services/cache/cache.service'; diff --git a/packages/cli/src/modules/sso-saml/saml-helpers.ts b/packages/cli/src/modules/sso-saml/saml-helpers.ts index 5c1953463bf..d572b793bb5 100644 --- a/packages/cli/src/modules/sso-saml/saml-helpers.ts +++ b/packages/cli/src/modules/sso-saml/saml-helpers.ts @@ -6,7 +6,7 @@ import { Container } from '@n8n/di'; import { randomString } from 'n8n-workflow'; import type { FlowResult } from 'samlify/types/src/flow'; -import { AuthError } from '@/errors/response-errors/auth.error'; +import { AuthError } from '@n8n/errors'; import { PasswordUtility } from '@/services/password.utility'; import { assertAuthenticationMethodCanBeEnabled, diff --git a/packages/cli/src/modules/sso-saml/saml.controller.ee.ts b/packages/cli/src/modules/sso-saml/saml.controller.ee.ts index 3c0f3cf3a40..affb3aebe0e 100644 --- a/packages/cli/src/modules/sso-saml/saml.controller.ee.ts +++ b/packages/cli/src/modules/sso-saml/saml.controller.ee.ts @@ -9,8 +9,7 @@ import type { PostBindingContext } from 'samlify/types/src/entity'; import url from 'url'; import { AuthService } from '@/auth/auth.service'; -import { AuthError } from '@/errors/response-errors/auth.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { AuthError, ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { SSO_ACCESS_DENIED_REDIRECT_PATH } from '@/modules/provisioning.ee/constants'; import { SsoAccessDeniedError } from '@/modules/provisioning.ee/errors/sso-access-denied.error'; diff --git a/packages/cli/src/modules/sso-saml/saml.service.ee.ts b/packages/cli/src/modules/sso-saml/saml.service.ee.ts index fc799217bb8..79b01885647 100644 --- a/packages/cli/src/modules/sso-saml/saml.service.ee.ts +++ b/packages/cli/src/modules/sso-saml/saml.service.ee.ts @@ -17,8 +17,7 @@ import type { PostBindingContext, } from 'samlify/types/src/entity'; -import { AuthError } from '@/errors/response-errors/auth.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { AuthError, BadRequestError } from '@n8n/errors'; import { buildSamlClaimsContext } from '@/modules/provisioning.ee/claims-context.builder'; import { ProvisioningService } from '@/modules/provisioning.ee/provisioning.service.ee'; import { CacheService } from '@/services/cache/cache.service'; diff --git a/packages/cli/src/modules/token-exchange/controllers/__tests__/token-exchange.controller.test.ts b/packages/cli/src/modules/token-exchange/controllers/__tests__/token-exchange.controller.test.ts index 340b5ed3771..11ddb93c151 100644 --- a/packages/cli/src/modules/token-exchange/controllers/__tests__/token-exchange.controller.test.ts +++ b/packages/cli/src/modules/token-exchange/controllers/__tests__/token-exchange.controller.test.ts @@ -5,8 +5,7 @@ import { ErrorReporter } from 'n8n-core'; import { UnexpectedError } from 'n8n-workflow'; import { mock } from 'vitest-mock-extended'; -import { AuthError } from '@/errors/response-errors/auth.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { AuthError, BadRequestError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import type { AuthlessRequest } from '@/requests'; diff --git a/packages/cli/src/modules/token-exchange/controllers/token-exchange.controller.ts b/packages/cli/src/modules/token-exchange/controllers/token-exchange.controller.ts index c5d48510d50..21f3adec94c 100644 --- a/packages/cli/src/modules/token-exchange/controllers/token-exchange.controller.ts +++ b/packages/cli/src/modules/token-exchange/controllers/token-exchange.controller.ts @@ -5,8 +5,7 @@ import type { Response } from 'express'; import { ErrorReporter } from 'n8n-core'; import { z, ZodError } from 'zod'; -import { AuthError } from '@/errors/response-errors/auth.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { AuthError, BadRequestError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { AuthlessRequest } from '@/requests'; diff --git a/packages/cli/src/modules/token-exchange/services/__tests__/token-exchange.service.test.ts b/packages/cli/src/modules/token-exchange/services/__tests__/token-exchange.service.test.ts index 2490d47967a..3431ca7fed1 100644 --- a/packages/cli/src/modules/token-exchange/services/__tests__/token-exchange.service.test.ts +++ b/packages/cli/src/modules/token-exchange/services/__tests__/token-exchange.service.test.ts @@ -3,8 +3,7 @@ import { GLOBAL_MEMBER_ROLE, type User } from '@n8n/db'; import jwt from 'jsonwebtoken'; import { mock } from 'vitest-mock-extended'; -import { AuthError } from '@/errors/response-errors/auth.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { AuthError, BadRequestError } from '@n8n/errors'; import type { JwtService } from '@/services/jwt.service'; import type { TokenExchangeConfig } from '../../token-exchange.config'; diff --git a/packages/cli/src/modules/token-exchange/token-exchange.errors.ts b/packages/cli/src/modules/token-exchange/token-exchange.errors.ts index 2b98d84b55f..ac5ec25b617 100644 --- a/packages/cli/src/modules/token-exchange/token-exchange.errors.ts +++ b/packages/cli/src/modules/token-exchange/token-exchange.errors.ts @@ -1,5 +1,4 @@ -import { AuthError } from '@/errors/response-errors/auth.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { AuthError, BadRequestError } from '@n8n/errors'; import type { TokenExchangeFailureReason } from './token-exchange.types'; diff --git a/packages/cli/src/modules/type-availability-policies/__tests__/type-availability-policy.service.test.ts b/packages/cli/src/modules/type-availability-policies/__tests__/type-availability-policy.service.test.ts index 76ef9c7fc32..945fdea4a50 100644 --- a/packages/cli/src/modules/type-availability-policies/__tests__/type-availability-policy.service.test.ts +++ b/packages/cli/src/modules/type-availability-policies/__tests__/type-availability-policy.service.test.ts @@ -3,8 +3,7 @@ import type { OperationContext, TransactionRunner } from '@n8n/db'; import type { NodeLoader } from 'n8n-workflow'; import { mock } from 'vitest-mock-extended'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ConflictError, NotFoundError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { LoadNodesAndCredentials } from '@/load-nodes-and-credentials'; import type { NodeTypes } from '@/node-types'; diff --git a/packages/cli/src/modules/type-availability-policies/credential-type-policy-instance.controller.ts b/packages/cli/src/modules/type-availability-policies/credential-type-policy-instance.controller.ts index 8dbcb5226ec..d29466a9f25 100644 --- a/packages/cli/src/modules/type-availability-policies/credential-type-policy-instance.controller.ts +++ b/packages/cli/src/modules/type-availability-policies/credential-type-policy-instance.controller.ts @@ -20,7 +20,7 @@ import { } from '@n8n/decorators'; import type { Response } from 'express'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { CREDENTIAL_TYPES_KIND } from './constants'; import { TypeAvailabilityPolicyService } from './type-availability-policy.service'; diff --git a/packages/cli/src/modules/type-availability-policies/type-availability-policy-instance.controller.ts b/packages/cli/src/modules/type-availability-policies/type-availability-policy-instance.controller.ts index a3a93540a29..ebba89e2517 100644 --- a/packages/cli/src/modules/type-availability-policies/type-availability-policy-instance.controller.ts +++ b/packages/cli/src/modules/type-availability-policies/type-availability-policy-instance.controller.ts @@ -20,7 +20,7 @@ import { } from '@n8n/decorators'; import type { Response } from 'express'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { NODE_TYPES_KIND } from './constants'; import { TypeAvailabilityPolicyService } from './type-availability-policy.service'; diff --git a/packages/cli/src/modules/type-availability-policies/type-availability-policy.service.ts b/packages/cli/src/modules/type-availability-policies/type-availability-policy.service.ts index 0b308291740..8e24ab18966 100644 --- a/packages/cli/src/modules/type-availability-policies/type-availability-policy.service.ts +++ b/packages/cli/src/modules/type-availability-policies/type-availability-policy.service.ts @@ -6,8 +6,7 @@ import { Service } from '@n8n/di'; import { LRUCache } from 'lru-cache'; import { OperationalError, UserError } from 'n8n-workflow'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ConflictError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials'; import { NodeTypes } from '@/node-types'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-authorization.access.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-authorization.access.service.test.ts index 1c3b8463009..5bb27b3d4cc 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-authorization.access.service.test.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-authorization.access.service.test.ts @@ -14,7 +14,7 @@ import { mock } from 'vitest-mock-extended'; import { WorkflowReviewAuthorizationService } from '../workflow-review-authorization.service'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { ProjectService } from '@/services/project.service.ee'; import type { RoleService } from '@/services/role.service'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-inbox.detail.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-inbox.detail.service.test.ts index 659ede95d59..184d4dd8f2c 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-inbox.detail.service.test.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-inbox.detail.service.test.ts @@ -10,7 +10,7 @@ import type { } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; import type { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-decision.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-decision.service.test.ts index ddb6c405ca2..8b60406b0e5 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-decision.service.test.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-decision.service.test.ts @@ -30,10 +30,7 @@ import { WorkflowReviewRequestMutationGuard } from '../workflow-review-request-m import { WorkflowReviewStateNotifier } from '../workflow-review-state-notifier.service'; import type { CollaborationService } from '@/collaboration/collaboration.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { ProjectService } from '@/services/project.service.ee'; import type { RoleService } from '@/services/role.service'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-status.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-status.service.test.ts index 0c2576514e6..18782fe709e 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-status.service.test.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-status.service.test.ts @@ -9,8 +9,7 @@ import type { } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import type { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-submission.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-submission.service.test.ts index fbea5443b89..92cd04a931c 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-submission.service.test.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-submission.service.test.ts @@ -25,10 +25,7 @@ import type { import { mock } from 'vitest-mock-extended'; import type { CollaborationService } from '@/collaboration/collaboration.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { RoleService } from '@/services/role.service'; import type { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-submission.version-update.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-submission.version-update.service.test.ts index 9058f01f137..9d0c2f7f8d2 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-submission.version-update.service.test.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-submission.version-update.service.test.ts @@ -24,10 +24,7 @@ import type { import { mock } from 'vitest-mock-extended'; import type { CollaborationService } from '@/collaboration/collaboration.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { RoleService } from '@/services/role.service'; import type { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-activity.service.ts b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-activity.service.ts index 9d9ad7c360f..4238bff9964 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-activity.service.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-activity.service.ts @@ -17,9 +17,7 @@ import { } from '@n8n/db'; import { Service } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ConflictError, ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { WorkflowReviewAuthorizationService } from './workflow-review-authorization.service'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-authorization.service.ts b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-authorization.service.ts index 9fcaf9f3dee..5d24a348ace 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-authorization.service.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-authorization.service.ts @@ -19,7 +19,7 @@ import { hasGlobalScope, } from '@n8n/permissions'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { ProjectService } from '@/services/project.service.ee'; import { RoleService } from '@/services/role.service'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-feature-gate.service.ts b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-feature-gate.service.ts index d56210750fa..e7f0675e514 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-feature-gate.service.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-feature-gate.service.ts @@ -1,7 +1,7 @@ import { LicenseState } from '@n8n/backend-common'; import { Service } from '@n8n/di'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; type WorkflowReviewAvailability = { available: true } | { available: false; errorMessage: string }; diff --git a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-inbox.service.ts b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-inbox.service.ts index b0e549ca1bf..fbdb4f85686 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-inbox.service.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-inbox.service.ts @@ -19,7 +19,7 @@ import { } from '@n8n/db'; import { Service } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; import { WorkflowReviewAuthorizationService } from './workflow-review-authorization.service'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-decision.service.ts b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-decision.service.ts index 2c5e8b0e3a0..84e05a0d0d3 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-decision.service.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-decision.service.ts @@ -19,9 +19,7 @@ import { Service } from '@n8n/di'; import { ensureError } from '@n8n/utils/errors/ensure-error'; import { CollaborationService } from '@/collaboration/collaboration.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ForbiddenError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { WorkflowService } from '@/workflows/workflow.service'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-mutation-guard.service.ts b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-mutation-guard.service.ts index 45cbddf43f2..c1c0ad81561 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-mutation-guard.service.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-mutation-guard.service.ts @@ -6,9 +6,7 @@ import { } from '@n8n/db'; import { Service } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, NotFoundError } from '@n8n/errors'; type BlockedAction = 'submit' | 'review' | 'update'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-status.service.ts b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-status.service.ts index 60488e9732e..3ea17009311 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-status.service.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-status.service.ts @@ -12,7 +12,7 @@ import { } from '@n8n/db'; import { Service } from '@n8n/di'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { WorkflowReviewAuthorizationService } from './workflow-review-authorization.service'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-submission.service.ts b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-submission.service.ts index 395e46823a9..017d3411936 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-submission.service.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-submission.service.ts @@ -22,9 +22,7 @@ import { } from '@n8n/db'; import { Service } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { RoleService } from '@/services/role.service'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; diff --git a/packages/cli/src/node-execution/__tests__/execute-node.service.test.ts b/packages/cli/src/node-execution/__tests__/execute-node.service.test.ts index 672a4f02875..b065fff91c5 100644 --- a/packages/cli/src/node-execution/__tests__/execute-node.service.test.ts +++ b/packages/cli/src/node-execution/__tests__/execute-node.service.test.ts @@ -8,8 +8,7 @@ import { mock } from 'vitest-mock-extended'; import { ActiveExecutions } from '@/active-executions'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { ExecutionPersistence } from '@/executions/execution-persistence'; import { NodeTypes } from '@/node-types'; import { WorkflowRunner } from '@/workflow-runner'; diff --git a/packages/cli/src/node-execution/execute-node.service.ts b/packages/cli/src/node-execution/execute-node.service.ts index 514b52da2de..b0200f534ca 100644 --- a/packages/cli/src/node-execution/execute-node.service.ts +++ b/packages/cli/src/node-execution/execute-node.service.ts @@ -18,8 +18,7 @@ import { v4 as uuid } from 'uuid'; import { ActiveExecutions } from '@/active-executions'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { ExecutionPersistence } from '@/executions/execution-persistence'; import { NodeTypes } from '@/node-types'; import { WorkflowRunner } from '@/workflow-runner'; diff --git a/packages/cli/src/oauth/__tests__/oauth.service.test.ts b/packages/cli/src/oauth/__tests__/oauth.service.test.ts index ebc56ba6ce1..78b38a6df36 100644 --- a/packages/cli/src/oauth/__tests__/oauth.service.test.ts +++ b/packages/cli/src/oauth/__tests__/oauth.service.test.ts @@ -19,9 +19,7 @@ import { AuthService } from '@/auth/auth.service'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; import { DynamicCredentialsProxy } from '@/credentials/dynamic-credentials-proxy'; import { CredentialsHelper } from '@/credentials-helper'; -import { AuthError } from '@/errors/response-errors/auth.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { AuthError, BadRequestError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { ExternalHooks } from '@/external-hooks'; import { OAuthBrowserBindingService } from '@/oauth/oauth-browser-binding.service'; diff --git a/packages/cli/src/oauth/oauth.service.ts b/packages/cli/src/oauth/oauth.service.ts index 12e36d5e66b..86388fcbe8b 100644 --- a/packages/cli/src/oauth/oauth.service.ts +++ b/packages/cli/src/oauth/oauth.service.ts @@ -23,9 +23,7 @@ import { import { AuthService } from '@/auth/auth.service'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; import { CredentialsHelper } from '@/credentials-helper'; -import { AuthError } from '@/errors/response-errors/auth.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { AuthError, BadRequestError, NotFoundError } from '@n8n/errors'; import type { OAuthRequest } from '@/requests'; import { extractAccountIdentifierFromData } from '@/oauth/account-identifier'; import { validateOAuthUrl } from '@/oauth/validate-oauth-url'; diff --git a/packages/cli/src/oauth/validate-oauth-url.ts b/packages/cli/src/oauth/validate-oauth-url.ts index 9571eeed9b2..177bd596a06 100644 --- a/packages/cli/src/oauth/validate-oauth-url.ts +++ b/packages/cli/src/oauth/validate-oauth-url.ts @@ -1,4 +1,4 @@ -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; const ALLOWED_OAUTH_URL_PROTOCOLS = ['http:', 'https:']; diff --git a/packages/cli/src/permissions.ee/__tests__/check-access.test.ts b/packages/cli/src/permissions.ee/__tests__/check-access.test.ts index 0f3244610c8..fced27d6675 100644 --- a/packages/cli/src/permissions.ee/__tests__/check-access.test.ts +++ b/packages/cli/src/permissions.ee/__tests__/check-access.test.ts @@ -12,7 +12,7 @@ import type { Mock } from 'vitest'; import { mock } from 'vitest-mock-extended'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { RoleService } from '@/services/role.service'; import { userHasScopes } from '../check-access'; diff --git a/packages/cli/src/permissions.ee/check-access.ts b/packages/cli/src/permissions.ee/check-access.ts index 858a30c00d0..8fc59d17454 100644 --- a/packages/cli/src/permissions.ee/check-access.ts +++ b/packages/cli/src/permissions.ee/check-access.ts @@ -15,7 +15,7 @@ import { hasGlobalScope, type Scope } from '@n8n/permissions'; import { UnexpectedError } from 'n8n-workflow'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { RoleService } from '@/services/role.service'; const INSTANCE_CREDENTIAL_MANAGEMENT_SCOPES = new Set([ diff --git a/packages/cli/src/public-api/__tests__/public-api-controller.registry.test.ts b/packages/cli/src/public-api/__tests__/public-api-controller.registry.test.ts index 0fd8ddede75..d84b4cb77ec 100644 --- a/packages/cli/src/public-api/__tests__/public-api-controller.registry.test.ts +++ b/packages/cli/src/public-api/__tests__/public-api-controller.registry.test.ts @@ -21,7 +21,7 @@ import request from 'supertest'; import { mock } from 'vitest-mock-extended'; import { z } from 'zod'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import { userHasScopes } from '@/permissions.ee/check-access'; import { diff --git a/packages/cli/src/public-api/public-api-controller.registry.ts b/packages/cli/src/public-api/public-api-controller.registry.ts index b61727d05be..3742381108b 100644 --- a/packages/cli/src/public-api/public-api-controller.registry.ts +++ b/packages/cli/src/public-api/public-api-controller.registry.ts @@ -11,7 +11,7 @@ import { z } from 'zod'; import type { ZodTypeAny } from 'zod'; import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { License } from '@/license'; import { userHasScopes } from '@/permissions.ee/check-access'; diff --git a/packages/cli/src/public-api/public-api-media-type.ts b/packages/cli/src/public-api/public-api-media-type.ts index d64c737b15b..4ed5fa5f4dd 100644 --- a/packages/cli/src/public-api/public-api-media-type.ts +++ b/packages/cli/src/public-api/public-api-media-type.ts @@ -1,4 +1,4 @@ -import { UnsupportedMediaTypeError } from '@/errors/response-errors/unsupported-media-type.error'; +import { UnsupportedMediaTypeError } from '@n8n/errors'; const JSON_MEDIA_TYPE = 'application/json'; diff --git a/packages/cli/src/public-api/v1/__tests__/public-api-error-response.test.ts b/packages/cli/src/public-api/v1/__tests__/public-api-error-response.test.ts index 5bd30ffb371..48f7fbe9591 100644 --- a/packages/cli/src/public-api/v1/__tests__/public-api-error-response.test.ts +++ b/packages/cli/src/public-api/v1/__tests__/public-api-error-response.test.ts @@ -2,9 +2,7 @@ import type { Response } from 'express'; import { BadRequest, Unauthorized } from 'express-openapi-validator/dist/framework/types'; import { UnexpectedError, UserError, OperationalError } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, NotFoundError } from '@n8n/errors'; import { WorkflowPublishBlockedError } from '@/errors/response-errors/workflow-publish-blocked.error'; import { sendPublicApiErrorResponse } from '../public-api-error-response'; diff --git a/packages/cli/src/public-api/v1/controllers/__tests__/credential-type-policies.public.controller.test.ts b/packages/cli/src/public-api/v1/controllers/__tests__/credential-type-policies.public.controller.test.ts index fb66c73b98a..7a921657b03 100644 --- a/packages/cli/src/public-api/v1/controllers/__tests__/credential-type-policies.public.controller.test.ts +++ b/packages/cli/src/public-api/v1/controllers/__tests__/credential-type-policies.public.controller.test.ts @@ -11,7 +11,7 @@ import { Container } from '@n8n/di'; import type { Response } from 'express'; import { mock } from 'vitest-mock-extended'; -import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error'; +import { ServiceUnavailableError } from '@n8n/errors'; import { CREDENTIAL_TYPES_KIND } from '@/modules/type-availability-policies/constants'; import type { EffectivePolicy } from '@/modules/type-availability-policies/type-availability-policy.service'; import { TypeAvailabilityPolicyService } from '@/modules/type-availability-policies/type-availability-policy.service'; diff --git a/packages/cli/src/public-api/v1/controllers/__tests__/node-type-policies.public.controller.test.ts b/packages/cli/src/public-api/v1/controllers/__tests__/node-type-policies.public.controller.test.ts index 04571cd5cb5..22db9ebc194 100644 --- a/packages/cli/src/public-api/v1/controllers/__tests__/node-type-policies.public.controller.test.ts +++ b/packages/cli/src/public-api/v1/controllers/__tests__/node-type-policies.public.controller.test.ts @@ -11,7 +11,7 @@ import { Container } from '@n8n/di'; import type { Response } from 'express'; import { mock } from 'vitest-mock-extended'; -import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error'; +import { ServiceUnavailableError } from '@n8n/errors'; import { NODE_TYPES_KIND } from '@/modules/type-availability-policies/constants'; import type { EffectivePolicy } from '@/modules/type-availability-policies/type-availability-policy.service'; import { TypeAvailabilityPolicyService } from '@/modules/type-availability-policies/type-availability-policy.service'; diff --git a/packages/cli/src/public-api/v1/controllers/__tests__/workflows.public.controller.test.ts b/packages/cli/src/public-api/v1/controllers/__tests__/workflows.public.controller.test.ts index 2a6ef23104e..e01dec38d02 100644 --- a/packages/cli/src/public-api/v1/controllers/__tests__/workflows.public.controller.test.ts +++ b/packages/cli/src/public-api/v1/controllers/__tests__/workflows.public.controller.test.ts @@ -3,7 +3,7 @@ import type { AuthenticatedRequest } from '@n8n/db'; import type { Response } from 'express'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { PolicyViolationError } from '@/policy/policy-violation.error'; import type { WorkflowService } from '@/workflows/workflow.service'; diff --git a/packages/cli/src/public-api/v1/controllers/credential-type-policies.public.controller.ts b/packages/cli/src/public-api/v1/controllers/credential-type-policies.public.controller.ts index ba3b9a0e365..f0dbe2c3215 100644 --- a/packages/cli/src/public-api/v1/controllers/credential-type-policies.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/credential-type-policies.public.controller.ts @@ -40,8 +40,7 @@ import { import { Container } from '@n8n/di'; import type { Response } from 'express'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error'; +import { NotFoundError, ServiceUnavailableError } from '@n8n/errors'; import { CREDENTIAL_TYPES_KIND } from '@/modules/type-availability-policies/constants'; import type { TypeAvailabilityPolicy } from '@/modules/type-availability-policies/database/entities/type-availability-policy.entity'; import { diff --git a/packages/cli/src/public-api/v1/controllers/credentials.public.controller.ts b/packages/cli/src/public-api/v1/controllers/credentials.public.controller.ts index 5c4831f5b3d..370e9e9aca6 100644 --- a/packages/cli/src/public-api/v1/controllers/credentials.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/credentials.public.controller.ts @@ -43,9 +43,7 @@ import { CredentialsService } from '@/credentials/credentials.service'; import { EnterpriseCredentialsService } from '@/credentials/credentials.service.ee'; import { CredentialsHelper } from '@/credentials-helper'; import { CredentialNotFoundError } from '@/errors/credential-not-found.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ForbiddenError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { assertValidUpdateProperties, diff --git a/packages/cli/src/public-api/v1/controllers/data-tables.public.controller.ts b/packages/cli/src/public-api/v1/controllers/data-tables.public.controller.ts index c20ea36424a..ae8cb5e5a18 100644 --- a/packages/cli/src/public-api/v1/controllers/data-tables.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/data-tables.public.controller.ts @@ -27,10 +27,7 @@ import { } from '@n8n/decorators'; import type { Response } from 'express'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from '@n8n/errors'; import { DataTableAggregateService } from '@/modules/data-table/data-table-aggregate.service'; import type { DataTableColumn } from '@/modules/data-table/data-table-column.entity'; import type { DataTable } from '@/modules/data-table/data-table.entity'; diff --git a/packages/cli/src/public-api/v1/controllers/discover.public.controller.ts b/packages/cli/src/public-api/v1/controllers/discover.public.controller.ts index fe2c9a02d85..ac438ffed66 100644 --- a/packages/cli/src/public-api/v1/controllers/discover.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/discover.public.controller.ts @@ -11,7 +11,7 @@ import { } from '@n8n/decorators'; import type { Response } from 'express'; -import { UnauthenticatedError } from '@/errors/response-errors/unauthenticated.error'; +import { UnauthenticatedError } from '@n8n/errors'; import { buildDiscoverResponse } from '@/public-api/v1/handlers/discover/discover.service'; @PublicApiController('/discover') diff --git a/packages/cli/src/public-api/v1/controllers/evaluations.public.controller.ts b/packages/cli/src/public-api/v1/controllers/evaluations.public.controller.ts index 84fe5f8fa8e..a4fc4268fdf 100644 --- a/packages/cli/src/public-api/v1/controllers/evaluations.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/evaluations.public.controller.ts @@ -30,9 +30,7 @@ import type { Response } from 'express'; import { ErrorReporter } from 'n8n-core'; import { EVALUATION_TRIGGER_NODE_TYPE } from 'n8n-workflow'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ConflictError, ForbiddenError, NotFoundError } from '@n8n/errors'; import { EvaluationTestRunService } from '@/evaluation.ee/evaluation-test-run.service'; import { TestRunnerService } from '@/evaluation.ee/test-runner/test-runner.service.ee'; import { diff --git a/packages/cli/src/public-api/v1/controllers/executions.public.controller.ts b/packages/cli/src/public-api/v1/controllers/executions.public.controller.ts index a0d770ad638..dbb50f1cf3a 100644 --- a/packages/cli/src/public-api/v1/controllers/executions.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/executions.public.controller.ts @@ -40,9 +40,7 @@ import { replaceCircularReferences, WorkflowOperationError } from 'n8n-workflow' import { AbortedExecutionRetryError } from '@/errors/aborted-execution-retry.error'; import { MissingExecutionStopError } from '@/errors/missing-execution-stop.error'; import { QueuedExecutionRetryError } from '@/errors/queued-execution-retry.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { isRedactableExecution } from '@/executions/execution-redaction'; import { ExecutionRedactionServiceProxy } from '@/executions/execution-redaction-proxy.service'; diff --git a/packages/cli/src/public-api/v1/controllers/folders.public.controller.ts b/packages/cli/src/public-api/v1/controllers/folders.public.controller.ts index ad9812c4272..365b057c36b 100644 --- a/packages/cli/src/public-api/v1/controllers/folders.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/folders.public.controller.ts @@ -39,8 +39,7 @@ import type { Response } from 'express'; import { UserError } from 'n8n-workflow'; import { FolderNotFoundError } from '@/errors/folder-not-found.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { assertProjectScope } from '@/public-api/v1/shared/services/utils.service'; import { FolderService } from '@/services/folder.service'; import { ProjectService } from '@/services/project.service.ee'; diff --git a/packages/cli/src/public-api/v1/controllers/insights.public.controller.ts b/packages/cli/src/public-api/v1/controllers/insights.public.controller.ts index 55909fbca13..d0122915d4c 100644 --- a/packages/cli/src/public-api/v1/controllers/insights.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/insights.public.controller.ts @@ -14,8 +14,7 @@ import type { Response } from 'express'; import { DateTime } from 'luxon'; import { UserError } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { InsightsService } from '@/modules/insights/insights.service'; @PublicApiController('/insights') diff --git a/packages/cli/src/public-api/v1/controllers/node-type-policies.public.controller.ts b/packages/cli/src/public-api/v1/controllers/node-type-policies.public.controller.ts index f979128e6f3..ae07a0d2194 100644 --- a/packages/cli/src/public-api/v1/controllers/node-type-policies.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/node-type-policies.public.controller.ts @@ -40,8 +40,7 @@ import { import { Container } from '@n8n/di'; import type { Response } from 'express'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error'; +import { NotFoundError, ServiceUnavailableError } from '@n8n/errors'; import { NODE_TYPES_KIND } from '@/modules/type-availability-policies/constants'; import type { TypeAvailabilityPolicy } from '@/modules/type-availability-policies/database/entities/type-availability-policy.entity'; import { diff --git a/packages/cli/src/public-api/v1/controllers/projects.public.controller.ts b/packages/cli/src/public-api/v1/controllers/projects.public.controller.ts index f33fb0a736c..5247deec3ea 100644 --- a/packages/cli/src/public-api/v1/controllers/projects.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/projects.public.controller.ts @@ -37,8 +37,7 @@ import { } from '@n8n/decorators'; import type { Response } from 'express'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import { ProvisioningService } from '@/modules/provisioning.ee/provisioning.service.ee'; import { encodeNextCursor, diff --git a/packages/cli/src/public-api/v1/controllers/promotions.public.controller.ts b/packages/cli/src/public-api/v1/controllers/promotions.public.controller.ts index 2ead342bac5..e3aa9fc5e16 100644 --- a/packages/cli/src/public-api/v1/controllers/promotions.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/promotions.public.controller.ts @@ -57,10 +57,12 @@ import { import { Container } from '@n8n/di'; import type { Response } from 'express'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error'; +import { + BadRequestError, + ForbiddenError, + NotFoundError, + ServiceUnavailableError, +} from '@n8n/errors'; import { encodeNextCursor, resolveOffsetPagination, diff --git a/packages/cli/src/public-api/v1/controllers/role-mapping-rules.public.controller.ts b/packages/cli/src/public-api/v1/controllers/role-mapping-rules.public.controller.ts index b233acf4e62..2f61a611143 100644 --- a/packages/cli/src/public-api/v1/controllers/role-mapping-rules.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/role-mapping-rules.public.controller.ts @@ -27,7 +27,7 @@ import { } from '@n8n/decorators'; import type { Response } from 'express'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { RoleMappingRuleResponse } from '@/modules/provisioning.ee/role-mapping-rule.service.ee'; import { RoleMappingRuleService } from '@/modules/provisioning.ee/role-mapping-rule.service.ee'; import { diff --git a/packages/cli/src/public-api/v1/controllers/roles.public.controller.ts b/packages/cli/src/public-api/v1/controllers/roles.public.controller.ts index 3963b98c727..8ec24651ea9 100644 --- a/packages/cli/src/public-api/v1/controllers/roles.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/roles.public.controller.ts @@ -30,7 +30,7 @@ import { import { RoleNamespace, type Role as RoleDTO } from '@n8n/permissions'; import type { Response } from 'express'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { assertCanManageRoleType, canReassignUsers } from '@/services/role-authorization'; import { RoleService } from '@/services/role.service'; diff --git a/packages/cli/src/public-api/v1/controllers/security-policy.public.controller.ts b/packages/cli/src/public-api/v1/controllers/security-policy.public.controller.ts index 6fe3c65f221..2b7b71c4558 100644 --- a/packages/cli/src/public-api/v1/controllers/security-policy.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/security-policy.public.controller.ts @@ -17,7 +17,7 @@ import { } from '@n8n/decorators'; import type { Response } from 'express'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError } from '@n8n/errors'; import type { SecurityPolicyReadResult } from '@/services/security-settings.service'; import { SecuritySettingsService } from '@/services/security-settings.service'; diff --git a/packages/cli/src/public-api/v1/controllers/source-control.public.controller.ts b/packages/cli/src/public-api/v1/controllers/source-control.public.controller.ts index e3fd1fe3e57..07c83b0d475 100644 --- a/packages/cli/src/public-api/v1/controllers/source-control.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/source-control.public.controller.ts @@ -25,8 +25,7 @@ import { } from '@n8n/decorators'; import type { Response } from 'express'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { BadRequestError, ConflictError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { getTrackingInformationFromPullResult } from '@/modules/source-control.ee/source-control-helper.ee'; import { SourceControlPreferencesService } from '@/modules/source-control.ee/source-control-preferences.service.ee'; diff --git a/packages/cli/src/public-api/v1/controllers/sso-oidc.public.controller.ts b/packages/cli/src/public-api/v1/controllers/sso-oidc.public.controller.ts index f1ee163ecca..79f933aea6b 100644 --- a/packages/cli/src/public-api/v1/controllers/sso-oidc.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/sso-oidc.public.controller.ts @@ -17,7 +17,7 @@ import { } from '@n8n/decorators'; import type { Response } from 'express'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError } from '@n8n/errors'; import { OIDC_CLIENT_SECRET_REDACTED_VALUE } from '@/modules/sso-oidc/constants'; import { OidcService } from '@/modules/sso-oidc/oidc.service.ee'; diff --git a/packages/cli/src/public-api/v1/controllers/tags.public.controller.ts b/packages/cli/src/public-api/v1/controllers/tags.public.controller.ts index 6d39c47e311..3627a5c1ee1 100644 --- a/packages/cli/src/public-api/v1/controllers/tags.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/tags.public.controller.ts @@ -26,8 +26,7 @@ import { } from '@n8n/decorators'; import type { Response } from 'express'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ConflictError, NotFoundError } from '@n8n/errors'; import { encodeNextCursor, resolveOffsetPagination, diff --git a/packages/cli/src/public-api/v1/controllers/users.public.controller.ts b/packages/cli/src/public-api/v1/controllers/users.public.controller.ts index 3989dc8e929..30603b3ded2 100644 --- a/packages/cli/src/public-api/v1/controllers/users.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/users.public.controller.ts @@ -32,7 +32,7 @@ import { import type { Response } from 'express'; import pick from 'lodash/pick'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { encodeNextCursor, diff --git a/packages/cli/src/public-api/v1/controllers/workflows.public.controller.ts b/packages/cli/src/public-api/v1/controllers/workflows.public.controller.ts index 9befb70a7ac..b8cb627e7ba 100644 --- a/packages/cli/src/public-api/v1/controllers/workflows.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/workflows.public.controller.ts @@ -55,9 +55,7 @@ import type { Response } from 'express'; import { PROJECT_ROOT } from 'n8n-workflow'; import { FolderNotFoundError } from '@/errors/folder-not-found.error'; -import { ResponseError } from '@/errors/response-errors/abstract/response.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ResponseError, BadRequestError, NotFoundError } from '@n8n/errors'; import { SharedWorkflowNotFoundError } from '@/errors/shared-workflow-not-found.error'; import { WorkflowHistoryVersionNotFoundError } from '@/errors/workflow-history-version-not-found.error'; import { EventService } from '@/events/event.service'; diff --git a/packages/cli/src/public-api/v1/handlers/credentials/credentials.utils.ts b/packages/cli/src/public-api/v1/handlers/credentials/credentials.utils.ts index bbdf26087ce..ef24cad284c 100644 --- a/packages/cli/src/public-api/v1/handlers/credentials/credentials.utils.ts +++ b/packages/cli/src/public-api/v1/handlers/credentials/credentials.utils.ts @@ -8,7 +8,7 @@ import { } from 'n8n-workflow'; import type { CredentialsHelper } from '@/credentials-helper'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { IDependency, IJsonSchema } from '../../../types'; diff --git a/packages/cli/src/public-api/v1/handlers/data-tables/__tests__/data-tables.handler.test.ts b/packages/cli/src/public-api/v1/handlers/data-tables/__tests__/data-tables.handler.test.ts index ea84fbeae7f..3294c8b408c 100644 --- a/packages/cli/src/public-api/v1/handlers/data-tables/__tests__/data-tables.handler.test.ts +++ b/packages/cli/src/public-api/v1/handlers/data-tables/__tests__/data-tables.handler.test.ts @@ -4,8 +4,7 @@ import { GLOBAL_MEMBER_SCOPES, type Scope } from '@n8n/permissions'; import type { Response } from 'express'; import type { Mock, Mocked } from 'vitest'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { DataTableAggregateService } from '@/modules/data-table/data-table-aggregate.service'; import { DataTableService } from '@/modules/data-table/data-table.service'; import { DataTableNotFoundError } from '@/modules/data-table/errors/data-table-not-found.error'; diff --git a/packages/cli/src/public-api/v1/handlers/data-tables/data-tables.columns.handler.ts b/packages/cli/src/public-api/v1/handlers/data-tables/data-tables.columns.handler.ts index 20122d4b9ce..99521057b59 100644 --- a/packages/cli/src/public-api/v1/handlers/data-tables/data-tables.columns.handler.ts +++ b/packages/cli/src/public-api/v1/handlers/data-tables/data-tables.columns.handler.ts @@ -1,8 +1,7 @@ import { AddDataTableColumnDto, updateDataTableColumnSchema } from '@n8n/api-types'; import { Container } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { BadRequestError, ConflictError } from '@n8n/errors'; import { DataTableService } from '@/modules/data-table/data-table.service'; import { DataTableColumnNameConflictError } from '@/modules/data-table/errors/data-table-column-name-conflict.error'; import { DataTableSystemColumnNameConflictError } from '@/modules/data-table/errors/data-table-system-column-name-conflict.error'; diff --git a/packages/cli/src/public-api/v1/handlers/data-tables/data-tables.rows.handler.ts b/packages/cli/src/public-api/v1/handlers/data-tables/data-tables.rows.handler.ts index 43b9a883a1a..a6144d13bcf 100644 --- a/packages/cli/src/public-api/v1/handlers/data-tables/data-tables.rows.handler.ts +++ b/packages/cli/src/public-api/v1/handlers/data-tables/data-tables.rows.handler.ts @@ -7,8 +7,7 @@ import { } from '@n8n/api-types'; import { Container } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { assertRowReadAccessIfReturningRows } from '@/modules/data-table/data-table-permissions'; import { DataTableService } from '@/modules/data-table/data-table.service'; import { DataTableNotFoundError } from '@/modules/data-table/errors/data-table-not-found.error'; diff --git a/packages/cli/src/public-api/v1/handlers/ldap/ldap.handler.ts b/packages/cli/src/public-api/v1/handlers/ldap/ldap.handler.ts index 2312f8e4ef4..71e598bd62a 100644 --- a/packages/cli/src/public-api/v1/handlers/ldap/ldap.handler.ts +++ b/packages/cli/src/public-api/v1/handlers/ldap/ldap.handler.ts @@ -1,8 +1,7 @@ import { LdapSyncDto, UpdateLdapConfigurationDto } from '@n8n/api-types'; import { Container } from '@n8n/di'; -import { ResponseError } from '@/errors/response-errors/abstract/response.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { ResponseError, BadRequestError } from '@n8n/errors'; import { getLdapSynchronizationsWithCount } from '@/modules/ldap.ee/helpers.ee'; import { LdapConnectionError, LdapRejectionError } from '@/modules/ldap.ee/ldap.errors'; import { LdapService } from '@/modules/ldap.ee/ldap.service.ee'; diff --git a/packages/cli/src/public-api/v1/handlers/log-streaming/log-streaming.handler.ts b/packages/cli/src/public-api/v1/handlers/log-streaming/log-streaming.handler.ts index 39d1a84f30d..54e42e16d7f 100644 --- a/packages/cli/src/public-api/v1/handlers/log-streaming/log-streaming.handler.ts +++ b/packages/cli/src/public-api/v1/handlers/log-streaming/log-streaming.handler.ts @@ -5,9 +5,7 @@ import { Container } from '@n8n/di'; import type { MessageEventBusDestinationOptions } from 'n8n-workflow'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, NotFoundError } from '@n8n/errors'; import { eventNamesAll } from '@/eventbus/event-message-classes'; import { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus'; import { createMessageEventBusDestination } from '@/modules/log-streaming.ee/create-message-event-bus-destination'; diff --git a/packages/cli/src/public-api/v1/handlers/n8n-packages/__tests__/n8n-packages.handler.test.ts b/packages/cli/src/public-api/v1/handlers/n8n-packages/__tests__/n8n-packages.handler.test.ts index 0a3cd09675b..e7e3d5272ac 100644 --- a/packages/cli/src/public-api/v1/handlers/n8n-packages/__tests__/n8n-packages.handler.test.ts +++ b/packages/cli/src/public-api/v1/handlers/n8n-packages/__tests__/n8n-packages.handler.test.ts @@ -6,10 +6,7 @@ import { UserError } from 'n8n-workflow'; import { PassThrough } from 'node:stream'; import type { Mocked } from 'vitest'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import type { RelayEventMap } from '@/events/maps/relay.event-map'; import { diff --git a/packages/cli/src/public-api/v1/handlers/n8n-packages/n8n-packages.handler.ts b/packages/cli/src/public-api/v1/handlers/n8n-packages/n8n-packages.handler.ts index 08112ce007c..5ba906ab3d4 100644 --- a/packages/cli/src/public-api/v1/handlers/n8n-packages/n8n-packages.handler.ts +++ b/packages/cli/src/public-api/v1/handlers/n8n-packages/n8n-packages.handler.ts @@ -9,8 +9,7 @@ import type { ApiKeyScope } from '@n8n/permissions'; import type { Response } from 'express'; import { UserError } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { PackageEntityAccessDeniedError, diff --git a/packages/cli/src/public-api/v1/handlers/otel/otel.handler.ts b/packages/cli/src/public-api/v1/handlers/otel/otel.handler.ts index 8a31f2d4c2d..a4850ccdd2c 100644 --- a/packages/cli/src/public-api/v1/handlers/otel/otel.handler.ts +++ b/packages/cli/src/public-api/v1/handlers/otel/otel.handler.ts @@ -2,8 +2,7 @@ import { TestOtelTraceDto, UpdateOtelSettingsDto } from '@n8n/api-types'; import { ModuleRegistry } from '@n8n/backend-common'; import { Container } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { BadRequestError, ConflictError } from '@n8n/errors'; import { OtelLifecycleHandler } from '@/modules/otel/otel-lifecycle-handler'; import { OtelSettingsService } from '@/modules/otel/otel-settings.service'; import { OtelService } from '@/modules/otel/otel.service'; diff --git a/packages/cli/src/public-api/v1/handlers/sso-saml/sso-saml.handler.ts b/packages/cli/src/public-api/v1/handlers/sso-saml/sso-saml.handler.ts index 1fbf4e575e7..db3e1525ce0 100644 --- a/packages/cli/src/public-api/v1/handlers/sso-saml/sso-saml.handler.ts +++ b/packages/cli/src/public-api/v1/handlers/sso-saml/sso-saml.handler.ts @@ -2,8 +2,7 @@ import { UpdateSamlConfigurationDto } from '@n8n/api-types'; import { InstanceSettingsLoaderConfig } from '@n8n/config'; import { Container } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { BadRequestError, ConflictError } from '@n8n/errors'; import { SamlService } from '@/modules/sso-saml/saml.service.ee'; import { toSamlConfigurationResponse, toSamlPreferencesUpdate } from './sso-saml.mapper'; diff --git a/packages/cli/src/public-api/v1/handlers/workflows/workflows.handler.ts b/packages/cli/src/public-api/v1/handlers/workflows/workflows.handler.ts index d80674dc815..288fc36e1cf 100644 --- a/packages/cli/src/public-api/v1/handlers/workflows/workflows.handler.ts +++ b/packages/cli/src/public-api/v1/handlers/workflows/workflows.handler.ts @@ -1,6 +1,6 @@ import { Container } from '@n8n/di'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; diff --git a/packages/cli/src/public-api/v1/shared/middlewares/global.middleware.ts b/packages/cli/src/public-api/v1/shared/middlewares/global.middleware.ts index da9b01f1edc..be3f9d2db14 100644 --- a/packages/cli/src/public-api/v1/shared/middlewares/global.middleware.ts +++ b/packages/cli/src/public-api/v1/shared/middlewares/global.middleware.ts @@ -8,7 +8,7 @@ import type express from 'express'; import type { NextFunction, Request, Response } from 'express'; import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { License } from '@/license'; import { userHasScopes } from '@/permissions.ee/check-access'; import { USER_QUOTA_FORBIDDEN_MESSAGE } from '@/public-api/constants'; diff --git a/packages/cli/src/public-api/v1/shared/services/__tests__/pagination.service.test.ts b/packages/cli/src/public-api/v1/shared/services/__tests__/pagination.service.test.ts index 209ce60e79d..f0ad41f71b1 100644 --- a/packages/cli/src/public-api/v1/shared/services/__tests__/pagination.service.test.ts +++ b/packages/cli/src/public-api/v1/shared/services/__tests__/pagination.service.test.ts @@ -1,4 +1,4 @@ -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { resolveOffsetPagination } from '../pagination.service'; diff --git a/packages/cli/src/public-api/v1/shared/services/pagination.service.ts b/packages/cli/src/public-api/v1/shared/services/pagination.service.ts index 80aefb4ebdc..2816a82b4d9 100644 --- a/packages/cli/src/public-api/v1/shared/services/pagination.service.ts +++ b/packages/cli/src/public-api/v1/shared/services/pagination.service.ts @@ -1,6 +1,6 @@ import { jsonParse } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { CursorPagination, diff --git a/packages/cli/src/public-api/v1/shared/services/utils.service.ts b/packages/cli/src/public-api/v1/shared/services/utils.service.ts index d6e91b2d58c..473c613fbdf 100644 --- a/packages/cli/src/public-api/v1/shared/services/utils.service.ts +++ b/packages/cli/src/public-api/v1/shared/services/utils.service.ts @@ -2,8 +2,7 @@ import { ProjectRepository, type User } from '@n8n/db'; import { Container } from '@n8n/di'; import type { Scope } from '@n8n/permissions'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ForbiddenError, NotFoundError } from '@n8n/errors'; import { ProjectService } from '@/services/project.service.ee'; export async function assertProjectScope( diff --git a/packages/cli/src/push/__tests__/index.test.ts b/packages/cli/src/push/__tests__/index.test.ts index bb0ae7155c3..155079551c2 100644 --- a/packages/cli/src/push/__tests__/index.test.ts +++ b/packages/cli/src/push/__tests__/index.test.ts @@ -8,7 +8,7 @@ import type { MockInstance } from 'vitest'; import { captor, mock } from 'vitest-mock-extended'; import { type WebSocket, Server as WSServer } from 'ws'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { Push } from '@/push'; import { SSEPush } from '@/push/sse.push'; import type { WebSocketPushRequest, SSEPushRequest, PushResponse } from '@/push/types'; diff --git a/packages/cli/src/push/index.ts b/packages/cli/src/push/index.ts index 29beab45980..21a11d97ff9 100644 --- a/packages/cli/src/push/index.ts +++ b/packages/cli/src/push/index.ts @@ -13,8 +13,7 @@ import { parse as parseUrl } from 'url'; import { Server as WSServer } from 'ws'; import { AuthService } from '@/auth/auth.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; +import { BadRequestError, InternalServerError } from '@n8n/errors'; import { MAX_PUBSUB_PAYLOAD_BYTES } from '@/scaling/constants'; import { Publisher } from '@/scaling/pubsub/publisher.service'; diff --git a/packages/cli/src/response-helper.ts b/packages/cli/src/response-helper.ts index 598c131719a..a8505f80684 100644 --- a/packages/cli/src/response-helper.ts +++ b/packages/cli/src/response-helper.ts @@ -1,7 +1,7 @@ import { inDevelopment, Logger } from '@n8n/backend-common'; import { isUniqueConstraintError, type User } from '@n8n/db'; import { Container } from '@n8n/di'; -import type { ReportingOptions } from '@n8n/errors'; +import { type ReportingOptions } from '@n8n/errors'; import type { Request, Response } from 'express'; import { ErrorReporter } from 'n8n-core'; import { ensureError } from '@n8n/utils/errors/ensure-error'; diff --git a/packages/cli/src/services/__tests__/active-workflows.service.test.ts b/packages/cli/src/services/__tests__/active-workflows.service.test.ts index 4c6b23ed7c3..d4e884f2dbd 100644 --- a/packages/cli/src/services/__tests__/active-workflows.service.test.ts +++ b/packages/cli/src/services/__tests__/active-workflows.service.test.ts @@ -3,7 +3,7 @@ import type { User, WorkflowRepository } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; import type { ActivationErrorsService } from '@/activation-errors.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { ProjectScopeService } from '@/permissions.ee/project-scope.service'; import { ActiveWorkflowsService } from '@/services/active-workflows.service'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; diff --git a/packages/cli/src/services/__tests__/ai-gateway.service.test.ts b/packages/cli/src/services/__tests__/ai-gateway.service.test.ts index 1fea8b1c159..2ac42799e0b 100644 --- a/packages/cli/src/services/__tests__/ai-gateway.service.test.ts +++ b/packages/cli/src/services/__tests__/ai-gateway.service.test.ts @@ -9,7 +9,7 @@ import { mock } from 'vitest-mock-extended'; import { N8N_VERSION, AI_ASSISTANT_SDK_VERSION } from '@/constants'; import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { License } from '@/license'; import { AiGatewayService } from '@/services/ai-gateway.service'; import type { OwnershipService } from '@/services/ownership.service'; diff --git a/packages/cli/src/services/__tests__/ai-preference-write.test.ts b/packages/cli/src/services/__tests__/ai-preference-write.test.ts index 24339fe873c..ea3b21f975b 100644 --- a/packages/cli/src/services/__tests__/ai-preference-write.test.ts +++ b/packages/cli/src/services/__tests__/ai-preference-write.test.ts @@ -5,11 +5,13 @@ import { TELEMETRY_EVENT } from '@n8n/telemetry'; import { mock } from 'vitest-mock-extended'; import { AiPreferenceScopeFullError } from '@/errors/response-errors/ai-preference-scope-full.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { + BadRequestError, + ConflictError, + ForbiddenError, + InternalServerError, + NotFoundError, +} from '@n8n/errors'; import { toAiPreferenceWriteRejection, writeAssistantPreference, diff --git a/packages/cli/src/services/__tests__/ai-preference.service.test.ts b/packages/cli/src/services/__tests__/ai-preference.service.test.ts index d1d2ee26880..c125ad0da3f 100644 --- a/packages/cli/src/services/__tests__/ai-preference.service.test.ts +++ b/packages/cli/src/services/__tests__/ai-preference.service.test.ts @@ -13,8 +13,7 @@ import { GLOBAL_MEMBER_ROLE, GLOBAL_OWNER_ROLE } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; import { AiPreferenceScopeFullError } from '@/errors/response-errors/ai-preference-scope-full.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { AI_PREFERENCES_CLEARED_BLOCK, AI_PREFERENCES_REPLACES_EARLIER, diff --git a/packages/cli/src/services/__tests__/dynamic-node-parameters.service.test.ts b/packages/cli/src/services/__tests__/dynamic-node-parameters.service.test.ts index eecf05faba6..1c76e97ec26 100644 --- a/packages/cli/src/services/__tests__/dynamic-node-parameters.service.test.ts +++ b/packages/cli/src/services/__tests__/dynamic-node-parameters.service.test.ts @@ -30,8 +30,7 @@ import { DynamicNodeParametersService } from '../dynamic-node-parameters.service import { WorkflowLoaderService } from '../workflow-loader.service'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { NodeTypes } from '@/node-types'; import * as checkAccess from '@/permissions.ee/check-access'; diff --git a/packages/cli/src/services/__tests__/email-change.service.test.ts b/packages/cli/src/services/__tests__/email-change.service.test.ts index bee805bf907..52619a7f378 100644 --- a/packages/cli/src/services/__tests__/email-change.service.test.ts +++ b/packages/cli/src/services/__tests__/email-change.service.test.ts @@ -4,8 +4,7 @@ import type { AuthIdentity, User } from '@n8n/db'; import type { Mock } from 'vitest'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { InvalidMfaCodeError } from '@/errors/response-errors/invalid-mfa-code.error'; import type { MfaService } from '@/mfa/mfa.service'; import type { PasswordUtility } from '@/services/password.utility'; diff --git a/packages/cli/src/services/__tests__/ownership.service.test.ts b/packages/cli/src/services/__tests__/ownership.service.test.ts index 6b760592863..c6c0c3e79c8 100644 --- a/packages/cli/src/services/__tests__/ownership.service.test.ts +++ b/packages/cli/src/services/__tests__/ownership.service.test.ts @@ -18,7 +18,7 @@ import { PROJECT_OWNER_ROLE_SLUG } from '@n8n/permissions'; import { v4 as uuid } from 'uuid'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import { OwnershipService } from '@/services/ownership.service'; import { PasswordUtility } from '@/services/password.utility'; diff --git a/packages/cli/src/services/__tests__/project.service.ee.test.ts b/packages/cli/src/services/__tests__/project.service.ee.test.ts index 0f3c6281ce1..f4c13eee3f5 100644 --- a/packages/cli/src/services/__tests__/project.service.ee.test.ts +++ b/packages/cli/src/services/__tests__/project.service.ee.test.ts @@ -23,8 +23,7 @@ import { ProjectService } from '../project.service.ee'; import type { RoleService } from '../role.service'; import type { ICredentialConnectionStatusProvider } from '@/credentials/credential-connection-status-provider.interface'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { AgentChatAttachmentService } from '@/modules/agents/agent-chat-attachment.service'; import type { AgentExecutionService } from '@/modules/agents/agent-execution.service'; diff --git a/packages/cli/src/services/__tests__/public-api-key.service.test.ts b/packages/cli/src/services/__tests__/public-api-key.service.test.ts index f434c41a677..3163df279fc 100644 --- a/packages/cli/src/services/__tests__/public-api-key.service.test.ts +++ b/packages/cli/src/services/__tests__/public-api-key.service.test.ts @@ -3,8 +3,7 @@ import type { ApiKey, ApiKeyRepository, User } from '@n8n/db'; import { hasGlobalScope } from '@n8n/permissions'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import type { UserManagementMailer } from '@/user-management/email'; import type { JwtService } from '../jwt.service'; diff --git a/packages/cli/src/services/__tests__/role-authorization.test.ts b/packages/cli/src/services/__tests__/role-authorization.test.ts index 51becdf94e2..94532bc0c27 100644 --- a/packages/cli/src/services/__tests__/role-authorization.test.ts +++ b/packages/cli/src/services/__tests__/role-authorization.test.ts @@ -2,7 +2,7 @@ import type { User } from '@n8n/db'; import type { Scope } from '@n8n/permissions'; import { mock } from 'vitest-mock-extended'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { assertCanManageRoleType } from '@/services/role-authorization'; describe('assertCanManageRoleType', () => { diff --git a/packages/cli/src/services/__tests__/role.service.assignments.test.ts b/packages/cli/src/services/__tests__/role.service.assignments.test.ts index 0292a3c6bfd..70abb3d7517 100644 --- a/packages/cli/src/services/__tests__/role.service.assignments.test.ts +++ b/packages/cli/src/services/__tests__/role.service.assignments.test.ts @@ -4,7 +4,7 @@ import { mockInstance } from '@n8n/backend-test-utils'; import { RoleRepository, ScopeRepository } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { RoleCacheService } from '@/services/role-cache.service'; import { RoleDeletionCheckProxy } from '@/services/role-deletion-check-proxy.service'; diff --git a/packages/cli/src/services/__tests__/role.service.customRoles.test.ts b/packages/cli/src/services/__tests__/role.service.customRoles.test.ts index 4d071b2e275..f1e34850eeb 100644 --- a/packages/cli/src/services/__tests__/role.service.customRoles.test.ts +++ b/packages/cli/src/services/__tests__/role.service.customRoles.test.ts @@ -7,7 +7,7 @@ import { RoleRepository, ScopeRepository } from '@n8n/db'; import { MANDATORY_INSTANCE_SCOPES } from '@n8n/permissions'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { RoleCacheService } from '@/services/role-cache.service'; import { RoleDeletionCheckProxy } from '@/services/role-deletion-check-proxy.service'; diff --git a/packages/cli/src/services/__tests__/user.service.test.ts b/packages/cli/src/services/__tests__/user.service.test.ts index fd700db60e3..21e53914d36 100644 --- a/packages/cli/src/services/__tests__/user.service.test.ts +++ b/packages/cli/src/services/__tests__/user.service.test.ts @@ -19,9 +19,7 @@ import { v4 as uuid } from 'uuid'; import { mock } from 'vitest-mock-extended'; import { RESPONSE_ERROR_MESSAGES } from '@/constants'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ForbiddenError, NotFoundError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { ExternalHooks } from '@/external-hooks'; import type { License } from '@/license'; diff --git a/packages/cli/src/services/active-workflows.service.ts b/packages/cli/src/services/active-workflows.service.ts index 232449f05a5..b81682ad51b 100644 --- a/packages/cli/src/services/active-workflows.service.ts +++ b/packages/cli/src/services/active-workflows.service.ts @@ -5,7 +5,7 @@ import { Service } from '@n8n/di'; import { hasGlobalScope } from '@n8n/permissions'; import { ActivationErrorsService } from '@/activation-errors.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { WorkflowSharingService } from '@/workflows/workflow-sharing.service'; diff --git a/packages/cli/src/services/ai-gateway.service.ts b/packages/cli/src/services/ai-gateway.service.ts index 0debdd3e27f..b9f9c9a254a 100644 --- a/packages/cli/src/services/ai-gateway.service.ts +++ b/packages/cli/src/services/ai-gateway.service.ts @@ -16,7 +16,7 @@ import { OperationalError, UserError } from 'n8n-workflow'; import { N8N_VERSION, AI_ASSISTANT_SDK_VERSION } from '@/constants'; import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { License } from '@/license'; import { checkAiGatewayEligibility } from '@/services/ai-gateway-eligibility'; import { OwnershipService } from '@/services/ownership.service'; diff --git a/packages/cli/src/services/ai-preference-write.ts b/packages/cli/src/services/ai-preference-write.ts index 3aaaef20569..95c642c2a41 100644 --- a/packages/cli/src/services/ai-preference-write.ts +++ b/packages/cli/src/services/ai-preference-write.ts @@ -3,10 +3,8 @@ import type { Logger } from '@n8n/backend-common'; import type { User } from '@n8n/db'; import { TELEMETRY_EVENT } from '@n8n/telemetry'; -import { ResponseError } from '@/errors/response-errors/abstract/response.error'; +import { ResponseError, ConflictError, ForbiddenError } from '@n8n/errors'; import { AiPreferenceScopeFullError } from '@/errors/response-errors/ai-preference-scope-full.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; import type { AiPreferenceService } from '@/services/ai-preference.service'; import type { Telemetry } from '@/telemetry'; diff --git a/packages/cli/src/services/ai-preference.service.ts b/packages/cli/src/services/ai-preference.service.ts index 67ecb5224ef..8de5c8d49ca 100644 --- a/packages/cli/src/services/ai-preference.service.ts +++ b/packages/cli/src/services/ai-preference.service.ts @@ -22,10 +22,7 @@ import { hasGlobalScope } from '@n8n/permissions'; import { randomUUID } from 'node:crypto'; import { AiPreferenceScopeFullError } from '@/errors/response-errors/ai-preference-scope-full.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from '@n8n/errors'; /** `type` lets the prompt name a personal project without its owner's name. */ export type AiPreferenceProjectRef = { id: string; name: string; type?: Project['type'] }; diff --git a/packages/cli/src/services/dynamic-node-parameters.service.ts b/packages/cli/src/services/dynamic-node-parameters.service.ts index 52affc0b931..4d3b651dadf 100644 --- a/packages/cli/src/services/dynamic-node-parameters.service.ts +++ b/packages/cli/src/services/dynamic-node-parameters.service.ts @@ -29,8 +29,7 @@ import { } from 'n8n-workflow'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { NodeTypes } from '@/node-types'; import { userHasScopes } from '@/permissions.ee/check-access'; import { withExpressionIsolate } from '@/utils'; diff --git a/packages/cli/src/services/email-change.service.ts b/packages/cli/src/services/email-change.service.ts index 264f6b934d2..4ebe702d1e2 100644 --- a/packages/cli/src/services/email-change.service.ts +++ b/packages/cli/src/services/email-change.service.ts @@ -3,8 +3,7 @@ import { GlobalConfig } from '@n8n/config'; import type { AuthIdentity, User } from '@n8n/db'; import { Service } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { InvalidMfaCodeError } from '@/errors/response-errors/invalid-mfa-code.error'; import { MfaService } from '@/mfa/mfa.service'; import { PasswordUtility } from '@/services/password.utility'; diff --git a/packages/cli/src/services/ownership.service.ts b/packages/cli/src/services/ownership.service.ts index b0ba9f07fda..ab8245236e6 100644 --- a/packages/cli/src/services/ownership.service.ts +++ b/packages/cli/src/services/ownership.service.ts @@ -18,7 +18,7 @@ import { IsNull } from '@n8n/typeorm/find-options/operator/IsNull'; import { Not } from '@n8n/typeorm/find-options/operator/Not'; import config from '@/config'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { CacheService } from '@/services/cache/cache.service'; diff --git a/packages/cli/src/services/project.service.ee.ts b/packages/cli/src/services/project.service.ee.ts index 6ac9b2cc6e7..8e8d9effa5c 100644 --- a/packages/cli/src/services/project.service.ee.ts +++ b/packages/cli/src/services/project.service.ee.ts @@ -32,10 +32,7 @@ import type { FindOptionsWhere, EntityManager } from '@n8n/typeorm'; import { In } from '@n8n/typeorm'; import { UserError } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { UserManagementMailer } from '@/user-management/email'; diff --git a/packages/cli/src/services/public-api-key.service.ts b/packages/cli/src/services/public-api-key.service.ts index 131b027c05c..8fc36453038 100644 --- a/packages/cli/src/services/public-api-key.service.ts +++ b/packages/cli/src/services/public-api-key.service.ts @@ -21,8 +21,7 @@ import { } from '@n8n/typeorm'; import { randomUUID } from 'crypto'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { UserManagementMailer } from '@/user-management/email'; import { JwtService } from './jwt.service'; diff --git a/packages/cli/src/services/role-authorization.ts b/packages/cli/src/services/role-authorization.ts index 9212d263612..6bf6e370e4b 100644 --- a/packages/cli/src/services/role-authorization.ts +++ b/packages/cli/src/services/role-authorization.ts @@ -2,7 +2,7 @@ import type { User } from '@n8n/db'; import { getAuthPrincipalScopes, type Role as RoleDTO, type RoleNamespace } from '@n8n/permissions'; import { RESPONSE_ERROR_MESSAGES } from '@/constants'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; /** * Whether a caller can manage a role requires `role:manage`; `role:manageProject` grants it for diff --git a/packages/cli/src/services/role.service.ts b/packages/cli/src/services/role.service.ts index 3e5f34fe106..d3676bc1bc2 100644 --- a/packages/cli/src/services/role.service.ts +++ b/packages/cli/src/services/role.service.ts @@ -41,8 +41,7 @@ import { } from '@n8n/permissions'; import { UnexpectedError, UserError } from 'n8n-workflow'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { isUniqueConstraintError } from '@/response-helper'; diff --git a/packages/cli/src/services/sandbox-settings.service.ts b/packages/cli/src/services/sandbox-settings.service.ts index 228635a43b3..ae3bce9cccc 100644 --- a/packages/cli/src/services/sandbox-settings.service.ts +++ b/packages/cli/src/services/sandbox-settings.service.ts @@ -13,7 +13,7 @@ import { type InstanceCredentialUse, type ResolvedInstanceCredential, } from '@/credentials/instance-credential-broker'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { UnprocessableRequestError } from '@n8n/errors'; const N8N_SANDBOX_HEADER_NAME = 'x-api-key'; diff --git a/packages/cli/src/services/session-cookie-auth.strategy.ts b/packages/cli/src/services/session-cookie-auth.strategy.ts index 66d4db01dc3..4aaf65540cd 100644 --- a/packages/cli/src/services/session-cookie-auth.strategy.ts +++ b/packages/cli/src/services/session-cookie-auth.strategy.ts @@ -5,7 +5,7 @@ import { JsonWebTokenError, TokenExpiredError } from 'jsonwebtoken'; import { AuthService } from '@/auth/auth.service'; import { AUTH_COOKIE_NAME } from '@/constants'; -import { AuthError } from '@/errors/response-errors/auth.error'; +import { AuthError } from '@n8n/errors'; import type { AuthStrategy, AuthStrategyOptions } from './auth-strategy.types'; diff --git a/packages/cli/src/services/user.service.ts b/packages/cli/src/services/user.service.ts index 7714031f95f..b9bd42a2cdf 100644 --- a/packages/cli/src/services/user.service.ts +++ b/packages/cli/src/services/user.service.ts @@ -36,10 +36,7 @@ import { PublicApiKeyService } from './public-api-key.service'; import { RoleService } from './role.service'; import { RESPONSE_ERROR_MESSAGES } from '@/constants'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ForbiddenError, InternalServerError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { ExternalHooks } from '@/external-hooks'; import type { Invitation } from '@/interfaces'; diff --git a/packages/cli/src/sso.ee/sso-helpers.ts b/packages/cli/src/sso.ee/sso-helpers.ts index 2b146a462ee..4b3fc5c575f 100644 --- a/packages/cli/src/sso.ee/sso-helpers.ts +++ b/packages/cli/src/sso.ee/sso-helpers.ts @@ -4,7 +4,7 @@ import { isAuthProviderType, SettingsRepository, type AuthProviderType } from '@ import { Container } from '@n8n/di'; import config from '@/config'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; /** * Only one authentication method can be active at a time. This function sets diff --git a/packages/cli/src/task-runners/task-broker/auth/__tests__/task-broker-auth.controller.test.ts b/packages/cli/src/task-runners/task-broker/auth/__tests__/task-broker-auth.controller.test.ts index dec7304afb9..628d823e3e2 100644 --- a/packages/cli/src/task-runners/task-broker/auth/__tests__/task-broker-auth.controller.test.ts +++ b/packages/cli/src/task-runners/task-broker/auth/__tests__/task-broker-auth.controller.test.ts @@ -3,8 +3,7 @@ import { GlobalConfig } from '@n8n/config'; import { CacheService } from '@/services/cache/cache.service'; -import { BadRequestError } from '../../../../errors/response-errors/bad-request.error'; -import { ForbiddenError } from '../../../../errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import type { AuthlessRequest } from '../../../../requests'; import { TaskBrokerAuthController } from '../task-broker-auth.controller'; import { TaskBrokerAuthService } from '../task-broker-auth.service'; diff --git a/packages/cli/src/task-runners/task-broker/auth/task-broker-auth.controller.ts b/packages/cli/src/task-runners/task-broker/auth/task-broker-auth.controller.ts index c60cf2440c4..b0dda748970 100644 --- a/packages/cli/src/task-runners/task-broker/auth/task-broker-auth.controller.ts +++ b/packages/cli/src/task-runners/task-broker/auth/task-broker-auth.controller.ts @@ -4,8 +4,7 @@ import type { AuthlessRequest } from '@/requests'; import { bearerTokenSchema, taskBrokerAuthRequestBodySchema } from './task-broker-auth.schema'; import { TaskBrokerAuthService } from './task-broker-auth.service'; -import { BadRequestError } from '../../../errors/response-errors/bad-request.error'; -import { ForbiddenError } from '../../../errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; /** * Controller responsible for authenticating Task Runner connections diff --git a/packages/cli/src/user-management/email/user-management-mailer.ts b/packages/cli/src/user-management/email/user-management-mailer.ts index 1d3e26c5bf8..1ee101174e6 100644 --- a/packages/cli/src/user-management/email/user-management-mailer.ts +++ b/packages/cli/src/user-management/email/user-management-mailer.ts @@ -19,7 +19,7 @@ import type { } from './interfaces'; import { NodeMailer } from './node-mailer'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; +import { InternalServerError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import type { RelayEventMap } from '@/events/maps/relay.event-map'; import { UrlService } from '@n8n/backend-services'; diff --git a/packages/cli/src/webhooks/__tests__/test-webhooks.test.ts b/packages/cli/src/webhooks/__tests__/test-webhooks.test.ts index 9d63e6a45a4..cdaee35e591 100644 --- a/packages/cli/src/webhooks/__tests__/test-webhooks.test.ts +++ b/packages/cli/src/webhooks/__tests__/test-webhooks.test.ts @@ -23,7 +23,7 @@ import { TEST_WEBHOOK_TIMEOUT, TEST_WEBHOOK_TIMEOUT_BUFFER, } from '@/constants'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { WebhookNotFoundError } from '@/errors/response-errors/webhook-not-found.error'; import type { TestWebhookRegistrationsService, diff --git a/packages/cli/src/webhooks/__tests__/waiting-webhooks.test.ts b/packages/cli/src/webhooks/__tests__/waiting-webhooks.test.ts index 89494520302..540446f177a 100644 --- a/packages/cli/src/webhooks/__tests__/waiting-webhooks.test.ts +++ b/packages/cli/src/webhooks/__tests__/waiting-webhooks.test.ts @@ -7,8 +7,7 @@ import type { INodeParameters, IWorkflowBase, Workflow } from 'n8n-workflow'; import { SEND_AND_WAIT_OPERATION } from 'n8n-workflow'; import { mock } from 'vitest-mock-extended'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ConflictError, NotFoundError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { ExecutionPersistence } from '@/executions/execution-persistence'; import { WaitingWebhooks } from '@/webhooks/waiting-webhooks'; diff --git a/packages/cli/src/webhooks/__tests__/webhook-form-data.test.ts b/packages/cli/src/webhooks/__tests__/webhook-form-data.test.ts index ee5870e53d9..6ec555eff53 100644 --- a/packages/cli/src/webhooks/__tests__/webhook-form-data.test.ts +++ b/packages/cli/src/webhooks/__tests__/webhook-form-data.test.ts @@ -6,8 +6,7 @@ import { createServer } from 'node:http'; import request from 'supertest'; import type TestAgent from 'supertest/lib/agent'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ContentTooLargeError } from '@/errors/response-errors/content-too-large.error'; +import { BadRequestError, ContentTooLargeError } from '@n8n/errors'; import { rawBodyReader } from '@/middlewares'; import { createMultiFormDataParser } from '../webhook-form-data'; diff --git a/packages/cli/src/webhooks/__tests__/webhook-helpers.test.ts b/packages/cli/src/webhooks/__tests__/webhook-helpers.test.ts index ac4b7ea4993..f634620dae8 100644 --- a/packages/cli/src/webhooks/__tests__/webhook-helpers.test.ts +++ b/packages/cli/src/webhooks/__tests__/webhook-helpers.test.ts @@ -58,7 +58,7 @@ import { mock, type MockProxy } from 'vitest-mock-extended'; import { ActiveExecutions } from '@/active-executions'; import { AuthService } from '@/auth/auth.service'; -import type { ResponseError } from '@/errors/response-errors/abstract/response.error'; +import { type ResponseError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { WebhookResponseRelay } from '@/scaling/webhook-response-relay'; import { EngineDataPlaneProxyService } from '@/services/engine-data-plane-proxy.service'; diff --git a/packages/cli/src/webhooks/__tests__/webhook-request-handler.test.ts b/packages/cli/src/webhooks/__tests__/webhook-request-handler.test.ts index 02f5e604e37..63e4aef1258 100644 --- a/packages/cli/src/webhooks/__tests__/webhook-request-handler.test.ts +++ b/packages/cli/src/webhooks/__tests__/webhook-request-handler.test.ts @@ -6,7 +6,7 @@ import { OperationalError, randomString } from 'n8n-workflow'; import type { IHttpRequestMethods } from 'n8n-workflow'; import { mock } from 'vitest-mock-extended'; -import { ResponseError } from '@/errors/response-errors/abstract/response.error'; +import { ResponseError } from '@n8n/errors'; import { createWebhookHandlerFor } from '@/webhooks/webhook-request-handler'; import type { IWebhookManager, diff --git a/packages/cli/src/webhooks/live-webhooks.ts b/packages/cli/src/webhooks/live-webhooks.ts index ba87bd94024..0ab5303e2e3 100644 --- a/packages/cli/src/webhooks/live-webhooks.ts +++ b/packages/cli/src/webhooks/live-webhooks.ts @@ -13,7 +13,7 @@ import { } from 'n8n-workflow'; import type { INode, IWebhookData, IHttpRequestMethods, IWorkflowBase } from 'n8n-workflow'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { WebhookNotFoundError } from '@/errors/response-errors/webhook-not-found.error'; import { NodeTypes } from '@/node-types'; import * as WebhookHelpers from '@/webhooks/webhook-helpers'; diff --git a/packages/cli/src/webhooks/test-webhooks.ts b/packages/cli/src/webhooks/test-webhooks.ts index bea1d482b14..a09535d6536 100644 --- a/packages/cli/src/webhooks/test-webhooks.ts +++ b/packages/cli/src/webhooks/test-webhooks.ts @@ -23,7 +23,7 @@ import { TEST_WEBHOOK_TIMEOUT, TEST_WEBHOOK_TIMEOUT_BUFFER, } from '@/constants'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { WebhookNotFoundError } from '@/errors/response-errors/webhook-not-found.error'; import { SingleWebhookTriggerError } from '@/errors/single-webhook-trigger.error'; import { WorkflowMissingIdError } from '@/errors/workflow-missing-id.error'; diff --git a/packages/cli/src/webhooks/waiting-forms.ts b/packages/cli/src/webhooks/waiting-forms.ts index 61e5317550a..0ffb433b765 100644 --- a/packages/cli/src/webhooks/waiting-forms.ts +++ b/packages/cli/src/webhooks/waiting-forms.ts @@ -4,8 +4,7 @@ import type express from 'express'; import type { IRunData } from 'n8n-workflow'; import { FORM_NODE_TYPE, WAITING_FORMS_EXECUTION_STATUS, Workflow } from 'n8n-workflow'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ConflictError, NotFoundError } from '@n8n/errors'; import { applyCors } from '@/utils/cors.util'; import { WaitingWebhooks } from '@/webhooks/waiting-webhooks'; import { applyFormSandboxCSP } from '@/webhooks/webhook-response-headers'; diff --git a/packages/cli/src/webhooks/waiting-webhooks.ts b/packages/cli/src/webhooks/waiting-webhooks.ts index a470bf53a76..1253b8bdae5 100644 --- a/packages/cli/src/webhooks/waiting-webhooks.ts +++ b/packages/cli/src/webhooks/waiting-webhooks.ts @@ -26,8 +26,7 @@ import type { import { EventService } from '@/events/event.service'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { ConflictError, NotFoundError } from '@n8n/errors'; import { ExecutionPersistence } from '@/executions/execution-persistence'; import { getWorkflowActiveStatusFromWorkflowData } from '@/executions/execution.utils'; import { NodeTypes } from '@/node-types'; diff --git a/packages/cli/src/webhooks/webhook-form-data.ts b/packages/cli/src/webhooks/webhook-form-data.ts index 2435d5e0586..700c457749a 100644 --- a/packages/cli/src/webhooks/webhook-form-data.ts +++ b/packages/cli/src/webhooks/webhook-form-data.ts @@ -2,8 +2,7 @@ import formidable from 'formidable'; import { rm } from 'node:fs/promises'; import type { IncomingMessage } from 'node:http'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ContentTooLargeError } from '@/errors/response-errors/content-too-large.error'; +import { BadRequestError, ContentTooLargeError } from '@n8n/errors'; import { discardBlankFileInputs } from '@/webhooks/webhook-blank-file-inputs'; // formidable reports the status code it considers appropriate on `httpCode` diff --git a/packages/cli/src/webhooks/webhook-helpers.ts b/packages/cli/src/webhooks/webhook-helpers.ts index c14b93e3cfd..7f603c70e33 100644 --- a/packages/cli/src/webhooks/webhook-helpers.ts +++ b/packages/cli/src/webhooks/webhook-helpers.ts @@ -62,11 +62,13 @@ import { finished } from 'stream/promises'; import { ActiveExecutions } from '@/active-executions'; import { AuthService } from '@/auth/auth.service'; import { MCP_TRIGGER_NODE_TYPE } from '@/constants'; -import { ResponseError } from '@/errors/response-errors/abstract/response.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { UnsupportedMediaTypeError } from '@/errors/response-errors/unsupported-media-type.error'; +import { + ResponseError, + BadRequestError, + InternalServerError, + NotFoundError, + UnsupportedMediaTypeError, +} from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { createExecutionIdV2 } from '@/executions/execution-id'; import { parseBody } from '@/middlewares'; diff --git a/packages/cli/src/workflow-helpers.ts b/packages/cli/src/workflow-helpers.ts index 5b29a6b0f9b..fda3c58b5ae 100644 --- a/packages/cli/src/workflow-helpers.ts +++ b/packages/cli/src/workflow-helpers.ts @@ -28,7 +28,7 @@ import { } from 'n8n-workflow'; import { v4 as uuid } from 'uuid'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { VariablesService } from '@/environments.ee/variables/variables.service.ee'; import { ExecutionPersistence } from '@/executions/execution-persistence'; diff --git a/packages/cli/src/workflows/__tests__/workflow-creation.service.test.ts b/packages/cli/src/workflows/__tests__/workflow-creation.service.test.ts index 96375f94d1e..7ace3e9f09b 100644 --- a/packages/cli/src/workflows/__tests__/workflow-creation.service.test.ts +++ b/packages/cli/src/workflows/__tests__/workflow-creation.service.test.ts @@ -14,9 +14,7 @@ import type { MockProxy } from 'vitest-mock-extended'; import { mock } from 'vitest-mock-extended'; import type { CredentialsFinderService } from '@/credentials/credentials-finder.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ForbiddenError, NotFoundError } from '@n8n/errors'; import type { ExternalHooks, WorkflowLifecycleHookActor } from '@/external-hooks'; import type { McpSettingsService } from '@/modules/mcp/mcp.settings.service'; import type { InstanceRedactionEnforcementService } from '@/modules/redaction/instance-redaction-enforcement.service'; diff --git a/packages/cli/src/workflows/__tests__/workflow-execution.service.test.ts b/packages/cli/src/workflows/__tests__/workflow-execution.service.test.ts index 4cb9bd70c7f..fc2d82fd45b 100644 --- a/packages/cli/src/workflows/__tests__/workflow-execution.service.test.ts +++ b/packages/cli/src/workflows/__tests__/workflow-execution.service.test.ts @@ -31,7 +31,7 @@ import type { WorkflowRequest } from '../workflow.request'; import { DuplicateExecutionError } from '@/errors/duplicate-execution.error'; import { ExecutionAlreadyResumingError } from '@/errors/execution-already-resuming.error'; import { PreExecuteBlockedError } from '@/errors/pre-execute-blocked.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import type { EventService } from '@/events/event.service'; import type { ExecutionCrashService } from '@/executions/execution-crash.service'; import type { IWorkflowErrorData } from '@/interfaces'; diff --git a/packages/cli/src/workflows/__tests__/workflow.service.test.ts b/packages/cli/src/workflows/__tests__/workflow.service.test.ts index 8f3a30d1613..2bed72574b8 100644 --- a/packages/cli/src/workflows/__tests__/workflow.service.test.ts +++ b/packages/cli/src/workflows/__tests__/workflow.service.test.ts @@ -21,10 +21,12 @@ import { mock } from 'vitest-mock-extended'; import type { MockProxy } from 'vitest-mock-extended'; import type { ActiveWorkflowManager } from '@/active-workflow-manager'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { + BadRequestError, + ConflictError, + NotFoundError, + UnprocessableRequestError, +} from '@n8n/errors'; import { WorkflowActivationBadRequestError } from '@/errors/response-errors/workflow-activation-bad-request.error'; import { WorkflowDeactivationBadRequestError } from '@/errors/response-errors/workflow-deactivation-bad-request.error'; import { WorkflowPublishBlockedError } from '@/errors/response-errors/workflow-publish-blocked.error'; diff --git a/packages/cli/src/workflows/__tests__/workflows.controller.test.ts b/packages/cli/src/workflows/__tests__/workflows.controller.test.ts index 30465234082..0d0602b0870 100644 --- a/packages/cli/src/workflows/__tests__/workflows.controller.test.ts +++ b/packages/cli/src/workflows/__tests__/workflows.controller.test.ts @@ -9,8 +9,7 @@ import { mock } from 'vitest-mock-extended'; import { WorkflowsController } from '../workflows.controller'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import type { ExecutionService } from '@/executions/execution.service'; import type { ProjectService } from '@/services/project.service.ee'; diff --git a/packages/cli/src/workflows/workflow-creation.service.ts b/packages/cli/src/workflows/workflow-creation.service.ts index 8490bb12afd..76dc8bc057f 100644 --- a/packages/cli/src/workflows/workflow-creation.service.ts +++ b/packages/cli/src/workflows/workflow-creation.service.ts @@ -15,10 +15,7 @@ import { PROJECT_ROOT } from 'n8n-workflow'; import { v4 as uuid } from 'uuid'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { InternalServerError } from '@/errors/response-errors/internal-server.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ForbiddenError, InternalServerError, NotFoundError } from '@n8n/errors'; import { WorkflowValidationError } from '@/errors/response-errors/workflow-validation.error'; import { EventService } from '@/events/event.service'; import type { WorkflowActionSource } from '@/events/maps/relay.event-map'; diff --git a/packages/cli/src/workflows/workflow-execution.service.ts b/packages/cli/src/workflows/workflow-execution.service.ts index 094b1426e17..bcada7101cb 100644 --- a/packages/cli/src/workflows/workflow-execution.service.ts +++ b/packages/cli/src/workflows/workflow-execution.service.ts @@ -41,7 +41,7 @@ import { import { ExecutionAlreadyResumingError } from '@/errors/execution-already-resuming.error'; import { PreExecuteBlockedError } from '@/errors/pre-execute-blocked.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { ExecutionCrashService } from '@/executions/execution-crash.service'; import { ExecutionPersistence } from '@/executions/execution-persistence'; diff --git a/packages/cli/src/workflows/workflow-history/__tests__/workflow-history.controller.test.ts b/packages/cli/src/workflows/workflow-history/__tests__/workflow-history.controller.test.ts index e584a2b7082..d1d82608d09 100644 --- a/packages/cli/src/workflows/workflow-history/__tests__/workflow-history.controller.test.ts +++ b/packages/cli/src/workflows/workflow-history/__tests__/workflow-history.controller.test.ts @@ -4,7 +4,7 @@ import type { AuthenticatedRequest, User } from '@n8n/db'; import { Container } from '@n8n/di'; import { mock } from 'vitest-mock-extended'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { SharedWorkflowNotFoundError } from '@/errors/shared-workflow-not-found.error'; import { WorkflowHistoryVersionNotFoundError } from '@/errors/workflow-history-version-not-found.error'; import type { WorkflowHistoryRequest } from '@/requests'; diff --git a/packages/cli/src/workflows/workflow-history/workflow-history.controller.ts b/packages/cli/src/workflows/workflow-history/workflow-history.controller.ts index a6e7bda7dae..e7e866f5920 100644 --- a/packages/cli/src/workflows/workflow-history/workflow-history.controller.ts +++ b/packages/cli/src/workflows/workflow-history/workflow-history.controller.ts @@ -6,7 +6,7 @@ import { import { AuthenticatedRequest } from '@n8n/db'; import { RestController, Get, Post, Query, Body, Patch, Param, Licensed } from '@n8n/decorators'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { NotFoundError } from '@n8n/errors'; import { SharedWorkflowNotFoundError } from '@/errors/shared-workflow-not-found.error'; import { WorkflowHistoryVersionNotFoundError } from '@/errors/workflow-history-version-not-found.error'; import { WorkflowHistoryRequest } from '@/requests'; diff --git a/packages/cli/src/workflows/workflow.service.ee.ts b/packages/cli/src/workflows/workflow.service.ee.ts index 7e85acc4d4a..e0a6e721967 100644 --- a/packages/cli/src/workflows/workflow.service.ee.ts +++ b/packages/cli/src/workflows/workflow.service.ee.ts @@ -34,8 +34,7 @@ import { CredentialsFinderService } from '@/credentials/credentials-finder.servi import { CredentialsService } from '@/credentials/credentials.service'; import { EnterpriseCredentialsService } from '@/credentials/credentials.service.ee'; import { FolderNotFoundError } from '@/errors/folder-not-found.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { TransferWorkflowError } from '@/errors/response-errors/transfer-workflow.error'; import { PolicyEnforcementService } from '@/policy/policy-enforcement.service'; import { OwnershipService } from '@/services/ownership.service'; diff --git a/packages/cli/src/workflows/workflow.service.ts b/packages/cli/src/workflows/workflow.service.ts index d00e5b66895..5f5ba1a94f0 100644 --- a/packages/cli/src/workflows/workflow.service.ts +++ b/packages/cli/src/workflows/workflow.service.ts @@ -42,10 +42,7 @@ import { WorkflowValidationService } from './workflow-validation.service'; import { ActiveWorkflowManager } from '@/active-workflow-manager'; import { FolderNotFoundError } from '@/errors/folder-not-found.error'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from '@n8n/errors'; import { WorkflowActivationBadRequestError } from '@/errors/response-errors/workflow-activation-bad-request.error'; import { WorkflowDeactivationBadRequestError } from '@/errors/response-errors/workflow-deactivation-bad-request.error'; import { WorkflowPublishForbiddenError } from '@/errors/response-errors/workflow-publish-forbidden.error'; diff --git a/packages/cli/src/workflows/workflows.controller.ts b/packages/cli/src/workflows/workflows.controller.ts index 4ecf49e9a63..6228a756335 100644 --- a/packages/cli/src/workflows/workflows.controller.ts +++ b/packages/cli/src/workflows/workflows.controller.ts @@ -41,9 +41,7 @@ import express from 'express'; import { calculateWorkflowChecksum } from 'n8n-workflow'; import { AuthService } from '@/auth/auth.service'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, ForbiddenError, NotFoundError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { ExecutionService } from '@/executions/execution.service'; import { IWorkflowResponse } from '@/interfaces'; diff --git a/packages/cli/test/integration/credentials/instance-credentials.api.test.ts b/packages/cli/test/integration/credentials/instance-credentials.api.test.ts index 42daa33a265..dd60890a331 100644 --- a/packages/cli/test/integration/credentials/instance-credentials.api.test.ts +++ b/packages/cli/test/integration/credentials/instance-credentials.api.test.ts @@ -11,7 +11,7 @@ import { Container } from '@n8n/di'; import { CredentialsService } from '@/credentials/credentials.service'; import { InstanceCredentialBroker } from '@/credentials/instance-credential-broker'; import type { InstanceCredentialUse } from '@/credentials/instance-credential-use.registry'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { UnprocessableRequestError } from '@n8n/errors'; import { ExternalHooks } from '@/external-hooks'; import { diff --git a/packages/cli/test/integration/environments/source-control-access-control.test.ts b/packages/cli/test/integration/environments/source-control-access-control.test.ts index 6860b04eb4d..b87d8f8bfc3 100644 --- a/packages/cli/test/integration/environments/source-control-access-control.test.ts +++ b/packages/cli/test/integration/environments/source-control-access-control.test.ts @@ -5,7 +5,7 @@ import { GLOBAL_MEMBER_ROLE, GLOBAL_OWNER_ROLE, type User } from '@n8n/db'; import { Container } from '@n8n/di'; import { hasGlobalScope } from '@n8n/permissions'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { ForbiddenError } from '@n8n/errors'; import { SourceControlPreferencesService } from '@/modules/source-control.ee/source-control-preferences.service.ee'; import { SourceControlService } from '@/modules/source-control.ee/source-control.service.ee'; import { Telemetry } from '@/telemetry'; diff --git a/packages/cli/test/integration/environments/source-control.service.test.ts b/packages/cli/test/integration/environments/source-control.service.test.ts index e5706e10e5f..a2bc1050189 100644 --- a/packages/cli/test/integration/environments/source-control.service.test.ts +++ b/packages/cli/test/integration/environments/source-control.service.test.ts @@ -20,8 +20,7 @@ import { basename, isAbsolute } from 'node:path'; import type { Mock } from 'vitest'; import { mock } from 'vitest-mock-extended'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { DataTable } from '@/modules/data-table/data-table.entity'; import { diff --git a/packages/cli/test/integration/mfa/mfa.api.test.ts b/packages/cli/test/integration/mfa/mfa.api.test.ts index 1fdcf0b5197..22715ad95af 100644 --- a/packages/cli/test/integration/mfa/mfa.api.test.ts +++ b/packages/cli/test/integration/mfa/mfa.api.test.ts @@ -7,7 +7,7 @@ import { randomString } from 'n8n-workflow'; import { AuthService } from '@/auth/auth.service'; import config from '@/config'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { ExternalHooks } from '@/external-hooks'; import { MFA_ENFORCE_SETTING } from '@/mfa/constants'; import { MFA_CACHE_KEY } from '@/mfa/mfa.service'; diff --git a/packages/cli/test/integration/middlewares/body-parser.test.ts b/packages/cli/test/integration/middlewares/body-parser.test.ts index de3bcdb80e5..bf2d9467dcb 100644 --- a/packages/cli/test/integration/middlewares/body-parser.test.ts +++ b/packages/cli/test/integration/middlewares/body-parser.test.ts @@ -4,8 +4,7 @@ import { createServer } from 'http'; import request from 'supertest'; import { gzipSync, deflateSync } from 'zlib'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { BadRequestError, UnprocessableRequestError } from '@n8n/errors'; import { rawBodyReader, bodyParser, parseBody } from '@/middlewares/body-parser'; describe('bodyParser', () => { diff --git a/packages/cli/test/integration/oidc/oidc.service.ee.test.ts b/packages/cli/test/integration/oidc/oidc.service.ee.test.ts index 68618045f0e..447ef1ba300 100644 --- a/packages/cli/test/integration/oidc/oidc.service.ee.test.ts +++ b/packages/cli/test/integration/oidc/oidc.service.ee.test.ts @@ -23,8 +23,7 @@ beforeAll(async () => { real_odic_client = await vi.importActual('openid-client'); }); -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { License } from '@/license'; import { ProvisioningService } from '@/modules/provisioning.ee/provisioning.service.ee'; import { OIDC_CLIENT_SECRET_REDACTED_VALUE } from '@/modules/sso-oidc/constants'; diff --git a/packages/cli/test/integration/public-api/executions.test.ts b/packages/cli/test/integration/public-api/executions.test.ts index 2197061a847..a3995de8fec 100644 --- a/packages/cli/test/integration/public-api/executions.test.ts +++ b/packages/cli/test/integration/public-api/executions.test.ts @@ -18,7 +18,7 @@ import { ActiveExecutions } from '@/active-executions'; import type { ActiveWorkflowManager } from '@/active-workflow-manager'; import { AbortedExecutionRetryError } from '@/errors/aborted-execution-retry.error'; import { QueuedExecutionRetryError } from '@/errors/queued-execution-retry.error'; -import { ConflictError } from '@/errors/response-errors/conflict.error'; +import { ConflictError } from '@n8n/errors'; import { ExecutionService } from '@/executions/execution.service'; import { CommunityPackagesService } from '@/modules/community-packages/community-packages.service'; import { Telemetry } from '@/telemetry'; diff --git a/packages/cli/test/integration/public-api/promotions.test.ts b/packages/cli/test/integration/public-api/promotions.test.ts index 1c0442e5027..9b3df9250be 100644 --- a/packages/cli/test/integration/public-api/promotions.test.ts +++ b/packages/cli/test/integration/public-api/promotions.test.ts @@ -6,7 +6,7 @@ import { GLOBAL_MEMBER_ROLE, ProjectRepository, UserRepository } from '@n8n/db'; import { Container } from '@n8n/di'; import { createDeferredPromise } from '@n8n/utils/promise/deferred-promise'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { BadRequestError } from '@n8n/errors'; import { PromotionConfigRepository } from '@/modules/promotions.ee/database/repositories/promotion-config.repository'; import { PromotionConnectionProjectRepository } from '@/modules/promotions.ee/database/repositories/promotion-connection-project.repository'; import { PromotionConnectionRepository } from '@/modules/promotions.ee/database/repositories/promotion-connection.repository'; diff --git a/packages/cli/test/integration/public-api/source-control.test.ts b/packages/cli/test/integration/public-api/source-control.test.ts index 61cf9828f21..a535b97d954 100644 --- a/packages/cli/test/integration/public-api/source-control.test.ts +++ b/packages/cli/test/integration/public-api/source-control.test.ts @@ -3,8 +3,7 @@ import { mockInstance } from '@n8n/backend-test-utils'; import type { User } from '@n8n/db'; import { Container } from '@n8n/di'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { EventService } from '@/events/event.service'; import { SourceControlPreferencesService } from '@/modules/source-control.ee/source-control-preferences.service.ee'; import { SourceControlService } from '@/modules/source-control.ee/source-control.service.ee'; diff --git a/packages/cli/test/integration/saml/saml.api.test.ts b/packages/cli/test/integration/saml/saml.api.test.ts index 3e3f4155c32..137abc82e8c 100644 --- a/packages/cli/test/integration/saml/saml.api.test.ts +++ b/packages/cli/test/integration/saml/saml.api.test.ts @@ -26,8 +26,7 @@ import type express from 'express'; import { CREDENTIAL_BLANKING_VALUE } from 'n8n-workflow'; import { TEMPLATES_DIR } from '@/constants'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; +import { BadRequestError, ForbiddenError } from '@n8n/errors'; import { ProvisioningService } from '@/modules/provisioning.ee/provisioning.service.ee'; import { EC_TEST_CERTIFICATE, diff --git a/packages/cli/test/integration/services/role.service.test.ts b/packages/cli/test/integration/services/role.service.test.ts index 876e9139e57..bdccfae7796 100644 --- a/packages/cli/test/integration/services/role.service.test.ts +++ b/packages/cli/test/integration/services/role.service.test.ts @@ -10,8 +10,7 @@ import { MANDATORY_INSTANCE_SCOPES, } from '@n8n/permissions'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { BadRequestError, NotFoundError } from '@n8n/errors'; import { License } from '@/license'; import { ProvisioningRoleDeletionChecker } from '@/modules/provisioning.ee/role-deletion-checker.ee'; import { ProjectService } from '@/services/project.service.ee'; diff --git a/packages/workflow/package.json b/packages/workflow/package.json index 85be52da007..58f657b0f80 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -76,10 +76,8 @@ "@n8n/utils": "workspace:*", "@n8n/expression-runtime": "workspace:*", "@n8n/tournament": "workspace:*", - "@sentry/node": "catalog:sentry", "ast-types": "0.16.1", "axios": "catalog:", - "callsites": "catalog:", "esprima-next": "5.8.4", "form-data": "catalog:", "jmespath": "0.16.0", diff --git a/packages/workflow/src/errors/abstract/execution-base.error.ts b/packages/workflow/src/errors/abstract/execution-base.error.ts index c13cd08fb86..605f55b67bf 100644 --- a/packages/workflow/src/errors/abstract/execution-base.error.ts +++ b/packages/workflow/src/errors/abstract/execution-base.error.ts @@ -1,4 +1,4 @@ -import { BaseError, type BaseErrorOptions } from '../base/base.error'; +import { BaseError, type BaseErrorOptions } from '@n8n/errors'; import type { Functionality, IDataObject, JsonObject } from '../../interfaces'; interface ExecutionBaseErrorOptions extends BaseErrorOptions { diff --git a/packages/workflow/src/errors/db-connection-timeout-error.ts b/packages/workflow/src/errors/db-connection-timeout-error.ts index 19b33e47c47..acfa4d9f991 100644 --- a/packages/workflow/src/errors/db-connection-timeout-error.ts +++ b/packages/workflow/src/errors/db-connection-timeout-error.ts @@ -1,4 +1,4 @@ -import { OperationalError } from './base/operational.error'; +import { OperationalError } from '@n8n/errors'; export type DbConnectionTimeoutErrorOpts = { configuredTimeoutInMs: number; diff --git a/packages/workflow/src/errors/index.ts b/packages/workflow/src/errors/index.ts index b9c6615bb75..8856d44f6ef 100644 --- a/packages/workflow/src/errors/index.ts +++ b/packages/workflow/src/errors/index.ts @@ -1,9 +1,15 @@ -export { BaseError, type BaseErrorOptions } from './base/base.error'; -export { OperationalError, type OperationalErrorOptions } from './base/operational.error'; -export { UnexpectedError, type UnexpectedErrorOptions } from './base/unexpected.error'; -export { UserError, type UserErrorOptions } from './base/user.error'; +export { + ApplicationError, + BaseError, + type BaseErrorOptions, + OperationalError, + type OperationalErrorOptions, + UnexpectedError, + type UnexpectedErrorOptions, + UserError, + type UserErrorOptions, +} from '@n8n/errors'; export { NodeVersionNotFoundError } from './node-version-not-found.error'; -export { ApplicationError } from '@n8n/errors'; export { ExpressionError } from './expression.error'; export { ExecutionCancelledError, diff --git a/packages/workflow/src/errors/node-operation.error.ts b/packages/workflow/src/errors/node-operation.error.ts index 0fc41f96699..2b0d2d474b2 100644 --- a/packages/workflow/src/errors/node-operation.error.ts +++ b/packages/workflow/src/errors/node-operation.error.ts @@ -1,5 +1,5 @@ import { NodeError } from './abstract/node.error'; -import { OperationalError } from './base/operational.error'; +import { OperationalError } from '@n8n/errors'; import type { NodeOperationErrorOptions } from './node-api.error'; import type { INode, JsonObject } from '../interfaces'; diff --git a/packages/workflow/src/errors/node-version-not-found.error.ts b/packages/workflow/src/errors/node-version-not-found.error.ts index 95a4001d07b..2aab75dac8e 100644 --- a/packages/workflow/src/errors/node-version-not-found.error.ts +++ b/packages/workflow/src/errors/node-version-not-found.error.ts @@ -1,4 +1,4 @@ -import { UserError } from './base/user.error'; +import { UserError } from '@n8n/errors'; /** * Thrown when a node type is resolved at a `typeVersion` that does not exist in diff --git a/packages/workflow/src/errors/trigger-close.error.ts b/packages/workflow/src/errors/trigger-close.error.ts index cc3a2831d2b..672caabb156 100644 --- a/packages/workflow/src/errors/trigger-close.error.ts +++ b/packages/workflow/src/errors/trigger-close.error.ts @@ -1,5 +1,5 @@ import type { INode } from '../interfaces'; -import { OperationalError, type OperationalErrorOptions } from './base/operational.error'; +import { OperationalError, type OperationalErrorOptions } from '@n8n/errors'; interface TriggerCloseErrorOptions extends ErrorOptions { level: OperationalErrorOptions['level']; diff --git a/packages/workflow/src/node-parameters/filter-parameter.ts b/packages/workflow/src/node-parameters/filter-parameter.ts index fcaded8105c..a7e3465491b 100644 --- a/packages/workflow/src/node-parameters/filter-parameter.ts +++ b/packages/workflow/src/node-parameters/filter-parameter.ts @@ -1,7 +1,7 @@ import type { Result } from '@n8n/utils/result'; import type { DateTime } from 'luxon'; -import { UserError } from '../errors/base/user.error'; +import { UserError } from '@n8n/errors'; import type { FilterConditionValue, FilterOperatorType, diff --git a/packages/workflow/src/utils.ts b/packages/workflow/src/utils.ts index 8c35ea2ee34..3457a8ead26 100644 --- a/packages/workflow/src/utils.ts +++ b/packages/workflow/src/utils.ts @@ -6,7 +6,7 @@ import merge from 'lodash/merge'; import path from 'path'; import { ALPHABET } from './constants'; -import { UserError } from './errors/base/user.error'; +import { UserError } from '@n8n/errors'; import type { BinaryFileType, IDisplayOptions, INodeProperties, JsonObject } from './interfaces'; import * as LoggerProxy from './logger-proxy'; diff --git a/packages/workflow/test/errors/base/operational.error.test.ts b/packages/workflow/test/errors/base/operational.error.test.ts index 5aa60b72643..47b78423b65 100644 --- a/packages/workflow/test/errors/base/operational.error.test.ts +++ b/packages/workflow/test/errors/base/operational.error.test.ts @@ -1,7 +1,11 @@ -import { BaseError } from '../../../src/errors/base/base.error'; -import { OperationalError } from '../../../src/errors/base/operational.error'; +import { BaseError, OperationalError as SharedOperationalError } from '@n8n/errors'; +import { OperationalError } from '../../../src/errors'; describe('OperationalError', () => { + it('should re-export the shared class', () => { + expect(OperationalError).toBe(SharedOperationalError); + }); + it('should be an instance of OperationalError', () => { const error = new OperationalError('test'); expect(error).toBeInstanceOf(OperationalError); diff --git a/packages/workflow/test/errors/base/unexpected.error.test.ts b/packages/workflow/test/errors/base/unexpected.error.test.ts index 3582fc8d4b7..ba5d57f3fe9 100644 --- a/packages/workflow/test/errors/base/unexpected.error.test.ts +++ b/packages/workflow/test/errors/base/unexpected.error.test.ts @@ -1,7 +1,11 @@ -import { BaseError } from '../../../src/errors/base/base.error'; -import { UnexpectedError } from '../../../src/errors/base/unexpected.error'; +import { BaseError, UnexpectedError as SharedUnexpectedError } from '@n8n/errors'; +import { UnexpectedError } from '../../../src/errors'; describe('UnexpectedError', () => { + it('should re-export the shared class', () => { + expect(UnexpectedError).toBe(SharedUnexpectedError); + }); + it('should be an instance of UnexpectedError', () => { const error = new UnexpectedError('test'); expect(error).toBeInstanceOf(UnexpectedError); diff --git a/packages/workflow/test/errors/base/user.error.test.ts b/packages/workflow/test/errors/base/user.error.test.ts index dfc56f256a0..9127cc1ce60 100644 --- a/packages/workflow/test/errors/base/user.error.test.ts +++ b/packages/workflow/test/errors/base/user.error.test.ts @@ -1,7 +1,11 @@ -import { BaseError } from '../../../src/errors/base/base.error'; -import { UserError } from '../../../src/errors/base/user.error'; +import { BaseError, UserError as SharedUserError } from '@n8n/errors'; +import { UserError } from '../../../src/errors'; describe('UserError', () => { + it('should re-export the shared class', () => { + expect(UserError).toBe(SharedUserError); + }); + it('should be an instance of UserError', () => { const error = new UserError('test'); expect(error).toBeInstanceOf(UserError); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 17bd4504808..184fa8145d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7880,18 +7880,12 @@ importers: '@n8n/utils': specifier: workspace:* version: link:../@n8n/utils - '@sentry/node': - specifier: catalog:sentry - version: 10.70.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1))(supports-color@8.1.1) ast-types: specifier: 0.16.1 version: 0.16.1 axios: specifier: 1.18.0 version: 1.18.0(patch_hash=149e256a2a7b632497650b32816716ced972ab02a5ab00fbd8a5158a51722c49)(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1) - callsites: - specifier: 'catalog:' - version: 3.1.0 esprima-next: specifier: 5.8.4 version: 5.8.4 From 46645c6d736692d0745ab2eed52268815c8dcdf7 Mon Sep 17 00:00:00 2001 From: "n8n-cat-bot[bot]" <283985454+n8n-cat-bot[bot]@users.noreply.github.com> Date: Tue, 29 Sep 2026 07:26:46 +0000 Subject: [PATCH 06/55] test: Keep WebSocket test connections offline (#39832) Co-authored-by: n8n-cat-bot[bot] --- .../src/app/push-connection/__tests__/mockWebSocketClient.ts | 5 +++-- .../app/push-connection/__tests__/useWebSocketClient.test.ts | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/frontend/editor-ui/src/app/push-connection/__tests__/mockWebSocketClient.ts b/packages/frontend/editor-ui/src/app/push-connection/__tests__/mockWebSocketClient.ts index e926afd044e..42a1ec49cff 100644 --- a/packages/frontend/editor-ui/src/app/push-connection/__tests__/mockWebSocketClient.ts +++ b/packages/frontend/editor-ui/src/app/push-connection/__tests__/mockWebSocketClient.ts @@ -1,11 +1,12 @@ import { WebSocketState, type WebSocketStateType } from '@/app/push-connection/useWebSocketClient'; /** Mocked WebSocket class to help testing */ -export class MockWebSocket extends WebSocket { +export class MockWebSocket extends EventTarget { readyState: WebSocketStateType = WebSocketState.CONNECTING; + binaryType: BinaryType = 'blob'; constructor(url: string) { - super(url); + super(); MockWebSocket._instance = this; MockWebSocket.init(url); diff --git a/packages/frontend/editor-ui/src/app/push-connection/__tests__/useWebSocketClient.test.ts b/packages/frontend/editor-ui/src/app/push-connection/__tests__/useWebSocketClient.test.ts index 4438a72337b..2087c6aded7 100644 --- a/packages/frontend/editor-ui/src/app/push-connection/__tests__/useWebSocketClient.test.ts +++ b/packages/frontend/editor-ui/src/app/push-connection/__tests__/useWebSocketClient.test.ts @@ -12,6 +12,8 @@ describe('useWebSocketClient', () => { afterEach(() => { vi.clearAllTimers(); vi.clearAllMocks(); + vi.useRealTimers(); + vi.unstubAllGlobals(); }); test('should create WebSocket connection with provided URL', () => { From 0afda588042c712da6225663669f847cdfe788f7 Mon Sep 17 00:00:00 2001 From: Elias Meire Date: Tue, 29 Sep 2026 07:34:41 +0000 Subject: [PATCH 07/55] fix: Hide node types that the instance does not load (#39571) --- .../community-node-types.service.test.ts | 70 ++++++++++++++++++- .../community-node-types.service.ts | 30 ++++++-- .../@n8n/stores/src/settings.store.test.ts | 68 ------------------ .../@n8n/stores/src/settings.store.ts | 23 +----- .../composables/useCanvasOperations.test.ts | 14 ++++ .../app/composables/useCanvasOperations.ts | 11 +++ .../useSelectionValidation.test.ts | 25 ++++++- .../app/composables/useSelectionValidation.ts | 14 +++- .../composables/useWorkflowExtraction.test.ts | 22 +++--- .../app/composables/useWorkflowExtraction.ts | 7 +- .../src/app/stores/nodeTypes.store.test.ts | 38 ++++++++++ .../src/app/stores/nodeTypes.store.ts | 11 +++ .../components/SetupWizard/SetupWizard.vue | 8 ++- .../views/EvaluationsRootView.test.ts | 1 + .../composables/useNodeCommands.test.ts | 12 ++++ .../commandBar/composables/useNodeCommands.ts | 3 +- .../composables/useContextMenu.test.ts | 23 ++++-- .../composables/useContextMenuItems.ts | 24 +++---- .../components/Modes/ActionsMode.vue | 4 +- .../components/Modes/NodesMode.test.ts | 22 ++++++ .../components/Modes/NodesMode.vue | 15 ++-- .../components/Panel/NodesListPanel.test.ts | 52 +++++++++++++- .../composables/useActions.test.ts | 50 +++++++++++++ .../nodeCreator/composables/useActions.ts | 15 +++- .../nodeCreator/composables/useViewStacks.ts | 6 +- .../shared/nodeCreator/views/NodeCreation.vue | 3 + .../canvas/components/Canvas.test.ts | 8 ++- .../workflows/canvas/components/Canvas.vue | 12 ++-- .../selection/CanvasSelectionToolbar.test.ts | 6 +- .../selection/CanvasSelectionToolbar.vue | 6 +- .../WorkflowSettings/WorkflowSettings.test.ts | 8 ++- .../WorkflowSettings/WorkflowSettings.vue | 7 +- 32 files changed, 464 insertions(+), 154 deletions(-) diff --git a/packages/cli/src/modules/community-packages/__tests__/community-node-types.service.test.ts b/packages/cli/src/modules/community-packages/__tests__/community-node-types.service.test.ts index 0d13b676737..3b36d59840c 100644 --- a/packages/cli/src/modules/community-packages/__tests__/community-node-types.service.test.ts +++ b/packages/cli/src/modules/community-packages/__tests__/community-node-types.service.test.ts @@ -23,6 +23,7 @@ describe('CommunityNodeTypesService', () => { let configMock: any; let communityPackagesServiceMock: any; let loggerMock: any; + let globalConfigMock: any; beforeEach(() => { vi.clearAllMocks(); @@ -37,11 +38,17 @@ describe('CommunityNodeTypesService', () => { nodesApiVersion: N8N_NODES_API_VERSION, }; communityPackagesServiceMock = {}; + globalConfigMock = { nodes: { exclude: [], include: [] } }; if (mockDateNow.mockRestore) mockDateNow.mockRestore(); if (mockMathRandom.mockRestore) mockMathRandom.mockRestore(); - service = new CommunityNodeTypesService(loggerMock, configMock, communityPackagesServiceMock); + service = new CommunityNodeTypesService( + loggerMock, + configMock, + communityPackagesServiceMock, + globalConfigMock, + ); }); afterEach(() => { @@ -409,6 +416,48 @@ describe('CommunityNodeTypesService', () => { communityPackagesServiceMock.getAllInstalledPackages = vi.fn().mockResolvedValue([]); }); + it('should not create an AI tool version for a node type the loader would skip', async () => { + globalConfigMock.nodes = { exclude: ['n8n-nodes-test.test'], include: [] }; + (getCommunityNodeTypes as Mock).mockResolvedValueOnce([ + { + name: 'n8n-nodes-test.test', + packageName: 'n8n-nodes-test', + nodeDescription: { + name: 'test-node-preview', + displayName: 'Test Node', + inputs: ['main'], + outputs: ['main'], + usableAsTool: true, + }, + }, + ]); + + const result = await service.getCommunityNodeTypes(); + + expect(result).toEqual([]); + }); + + it('should keep the AI tool version when NODES_INCLUDE lists its base node type', async () => { + globalConfigMock.nodes = { exclude: [], include: ['n8n-nodes-test.test'] }; + (getCommunityNodeTypes as Mock).mockResolvedValueOnce([ + { + name: 'n8n-nodes-test.test', + packageName: 'n8n-nodes-test', + nodeDescription: { + name: 'test-node-preview', + displayName: 'Test Node', + inputs: ['main'], + outputs: ['main'], + usableAsTool: true, + }, + }, + ]); + + const result = await service.getCommunityNodeTypes(); + + expect(result.map((n) => n.name)).toEqual(['n8n-nodes-test.test', 'n8n-nodes-test.testTool']); + }); + it('should create AI tool versions for nodes with usableAsTool flag', async () => { const mockNodeTypes = [ { @@ -903,6 +952,17 @@ describe('CommunityNodeTypesService', () => { expect(result[1].isInstalled).toBe(false); }); + it.each([ + [{ exclude: ['package-2.node2'], include: [] }], + [{ exclude: [], include: ['package-1.node1'] }], + ])('should leave out node types the loader would skip with %j', async (nodes) => { + globalConfigMock.nodes = nodes; + + const result = await service.getCommunityNodeTypes(); + + expect(result.map((n) => n.name)).toEqual(['package-1.node1']); + }); + it('should fetch updates when interval has passed', async () => { (service as any).lastUpdateTimestamp = 0; const fetchSpy = vi.spyOn(service as any, 'fetchNodeTypes').mockResolvedValue(undefined); @@ -949,6 +1009,14 @@ describe('CommunityNodeTypesService', () => { expect(result).toBeNull(); }); + it('should return null for a node type the loader would skip', async () => { + globalConfigMock.nodes = { exclude: ['package-1.node1'], include: [] }; + + const result = await service.getCommunityNodeType('package-1.node1'); + + expect(result).toBeNull(); + }); + it('should determine installation status from package name', async () => { const mockNodeTypes = [ { name: 'package-1.node1', packageName: 'package-1', npmVersion: '1.0.0' }, diff --git a/packages/cli/src/modules/community-packages/community-node-types.service.ts b/packages/cli/src/modules/community-packages/community-node-types.service.ts index dbbc8f34d56..fa3851fd220 100644 --- a/packages/cli/src/modules/community-packages/community-node-types.service.ts +++ b/packages/cli/src/modules/community-packages/community-node-types.service.ts @@ -1,5 +1,6 @@ import type { CommunityNodeType } from '@n8n/api-types'; import { inProduction, Logger } from '@n8n/backend-common'; +import { GlobalConfig } from '@n8n/config'; import { Service } from '@n8n/di'; import cloneDeep from 'lodash/cloneDeep'; import { ensureError } from '@n8n/utils/errors/ensure-error'; @@ -32,8 +33,25 @@ export class CommunityNodeTypesService { private readonly logger: Logger, private config: CommunityPackagesConfig, private communityPackagesService: CommunityPackagesService, + private readonly globalConfig: GlobalConfig, ) {} + /** + * Same rule the node loader applies, so nothing offers a type that would not load. + * The loader builds AI tool copies from loaded base nodes, so a copy follows its base node. + */ + private isLoadable(nodeTypeName: string) { + const baseName = nodeTypeName.slice(0, -'Tool'.length); + const isToolCopy = + nodeTypeName.endsWith('Tool') && + this.communityNodeTypes.get(baseName)?.nodeDescription.usableAsTool === true; + const loaderName = isToolCopy ? baseName : nodeTypeName; + + const { exclude, include } = this.globalConfig.nodes; + if (exclude.includes(loaderName)) return false; + return include.length === 0 || include.includes(loaderName); + } + private async detectUpdates( environment: 'staging' | 'production', ): Promise<{ typesToUpdate?: number[]; scheduleRetry?: boolean }> { @@ -227,16 +245,18 @@ export class CommunityNodeTypesService { const isInstalled = await this.createIsInstalled(); - return Array.from(this.communityNodeTypes.values()).map((nodeType) => ({ - ...nodeType, - isInstalled: isInstalled(nodeType), - })); + return Array.from(this.communityNodeTypes.values()) + .filter((nodeType) => this.isLoadable(nodeType.name)) + .map((nodeType) => ({ + ...nodeType, + isInstalled: isInstalled(nodeType), + })); } async getCommunityNodeType(type: string): Promise { const nodeType = this.communityNodeTypes.get(type); const isInstalled = await this.createIsInstalled(); - if (!nodeType) return null; + if (!nodeType || !this.isLoadable(nodeType.name)) return null; return { ...nodeType, isInstalled: isInstalled(nodeType) }; } diff --git a/packages/frontend/@n8n/stores/src/settings.store.test.ts b/packages/frontend/@n8n/stores/src/settings.store.test.ts index d9a77e045db..770becffadc 100644 --- a/packages/frontend/@n8n/stores/src/settings.store.test.ts +++ b/packages/frontend/@n8n/stores/src/settings.store.test.ts @@ -146,74 +146,6 @@ describe('settings.store', () => { }); }); - describe('isExecuteWorkflowNodeExcluded', () => { - it('should return true when executeWorkflow is in excludeNodes', async () => { - getSettings.mockResolvedValueOnce({ - ...mockSettings, - excludeNodes: ['n8n-nodes-base.executeWorkflow'], - }); - - const settingsStore = useSettingsStore(); - await settingsStore.getSettings(); - - expect(settingsStore.isExecuteWorkflowNodeExcluded).toBe(true); - }); - - it('should return false when executeWorkflow is not excluded', async () => { - getSettings.mockResolvedValueOnce({ - ...mockSettings, - excludeNodes: ['n8n-nodes-base.executeCommand'], - }); - - const settingsStore = useSettingsStore(); - await settingsStore.getSettings(); - - expect(settingsStore.isExecuteWorkflowNodeExcluded).toBe(false); - }); - - it('should return false when only executeWorkflowTrigger is excluded', async () => { - getSettings.mockResolvedValueOnce({ - ...mockSettings, - excludeNodes: ['n8n-nodes-base.executeWorkflowTrigger'], - }); - - const settingsStore = useSettingsStore(); - await settingsStore.getSettings(); - - expect(settingsStore.isExecuteWorkflowNodeExcluded).toBe(false); - }); - }); - - describe('isSubworkflowConversionDisabled', () => { - it.each([ - [['n8n-nodes-base.executeWorkflow']], - [['n8n-nodes-base.executeWorkflowTrigger']], - [['n8n-nodes-base.executeWorkflow', 'n8n-nodes-base.executeWorkflowTrigger']], - ])('should return true when %j is excluded', async (excludeNodes) => { - getSettings.mockResolvedValueOnce({ - ...mockSettings, - excludeNodes, - }); - - const settingsStore = useSettingsStore(); - await settingsStore.getSettings(); - - expect(settingsStore.isSubworkflowConversionDisabled).toBe(true); - }); - - it('should return false when both sub-workflow nodes are available', async () => { - getSettings.mockResolvedValueOnce({ - ...mockSettings, - excludeNodes: ['n8n-nodes-base.executeCommand'], - }); - - const settingsStore = useSettingsStore(); - await settingsStore.getSettings(); - - expect(settingsStore.isSubworkflowConversionDisabled).toBe(false); - }); - }); - describe('isCrdtCollaborationEnabled', () => { it('should return true when collaboration.crdt is local', async () => { getSettings.mockResolvedValueOnce({ diff --git a/packages/frontend/@n8n/stores/src/settings.store.ts b/packages/frontend/@n8n/stores/src/settings.store.ts index 44840ba8d4c..f2d2ae3b249 100644 --- a/packages/frontend/@n8n/stores/src/settings.store.ts +++ b/packages/frontend/@n8n/stores/src/settings.store.ts @@ -13,12 +13,7 @@ import * as moduleSettingsApi from '@n8n/rest-api-client/api/module-settings'; import * as settingsApi from '@n8n/rest-api-client/api/settings'; import { testHealthEndpoint } from '@n8n/rest-api-client/api/templates'; import Bowser from 'bowser'; -import { - EXECUTE_WORKFLOW_NODE_TYPE, - EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE, - type IDataObject, - type WorkflowSettings, -} from 'n8n-workflow'; +import type { IDataObject, WorkflowSettings } from 'n8n-workflow'; import { defineStore } from 'pinia'; import { computed, ref } from 'vue'; @@ -269,20 +264,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => { () => settings.value.workflowCallerPolicyDefaultOption, ); - const isNodeTypeExcluded = (nodeType: string) => { - const excludeNodes = settings.value.excludeNodes; - return Array.isArray(excludeNodes) && excludeNodes.includes(nodeType); - }; - - const isExecuteWorkflowNodeExcluded = computed(() => - isNodeTypeExcluded(EXECUTE_WORKFLOW_NODE_TYPE), - ); - - const isSubworkflowConversionDisabled = computed( - () => - isExecuteWorkflowNodeExcluded.value || isNodeTypeExcluded(EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE), - ); - const permanentlyDismissedBanners = computed(() => settings.value.banners?.dismissed ?? []); const isCommunityPlan = computed(() => planName.value.toLowerCase() === 'community'); @@ -515,8 +496,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => { isMultiMain, isWorkerViewAvailable, workflowCallerPolicyDefaultOption, - isExecuteWorkflowNodeExcluded, - isSubworkflowConversionDisabled, permanentlyDismissedBanners, saveDataErrorExecution, saveDataSuccessExecution, diff --git a/packages/frontend/editor-ui/src/app/composables/useCanvasOperations.test.ts b/packages/frontend/editor-ui/src/app/composables/useCanvasOperations.test.ts index 59b1711fab0..99577b52506 100644 --- a/packages/frontend/editor-ui/src/app/composables/useCanvasOperations.test.ts +++ b/packages/frontend/editor-ui/src/app/composables/useCanvasOperations.test.ts @@ -1558,6 +1558,20 @@ describe('useCanvasOperations', () => { }); }); + describe('addNodesAndConnections', () => { + it('should add nothing and clear the connection context when a node type is not loaded', async () => { + vi.mocked(useNodeTypesStore().isNodeTypeUnavailable).mockReturnValue(true); + const addNodeSpy = vi.spyOn(workflowDocumentStoreInstance, 'addNode'); + + const { addNodesAndConnections } = useCanvasOperations(); + const { addedNodes } = await addNodesAndConnections([{ type: 'type' }], [], {}); + + expect(addedNodes).toEqual([]); + expect(addNodeSpy).not.toHaveBeenCalled(); + expect(useUIStore().resetLastInteractedWith).toHaveBeenCalled(); + }); + }); + describe('addNodes', () => { it('should add nodes at specified positions', async () => { const nodeTypesStore = useNodeTypesStore(); diff --git a/packages/frontend/editor-ui/src/app/composables/useCanvasOperations.ts b/packages/frontend/editor-ui/src/app/composables/useCanvasOperations.ts index 8f93da3c626..d01ce9db305 100644 --- a/packages/frontend/editor-ui/src/app/composables/useCanvasOperations.ts +++ b/packages/frontend/editor-ui/src/app/composables/useCanvasOperations.ts @@ -3890,6 +3890,17 @@ export function useCanvasOperations() { trackBulk?: boolean; }, ) { + // New nodes only: imports go through `addNodes` and keep unknown types as placeholders. + const unavailable = nodes.filter((node) => nodeTypesStore.isNodeTypeUnavailable(node.type)); + if (unavailable.length > 0) { + console.warn( + 'Skipped adding node types this instance does not load:', + unavailable.map((node) => node.type), + ); + uiStore.resetLastInteractedWith(); + return { addedNodes: [] }; + } + // An empty group contains only its internal anchor, which the selected node must replace. const replacementTargetId = options.replaceNodeId; const replacementTarget = replacementTargetId diff --git a/packages/frontend/editor-ui/src/app/composables/useSelectionValidation.test.ts b/packages/frontend/editor-ui/src/app/composables/useSelectionValidation.test.ts index a3cc9e30181..4f7d86e9991 100644 --- a/packages/frontend/editor-ui/src/app/composables/useSelectionValidation.test.ts +++ b/packages/frontend/editor-ui/src/app/composables/useSelectionValidation.test.ts @@ -7,7 +7,11 @@ import { createApp, shallowRef } from 'vue'; import { useSelectionValidation } from './useSelectionValidation'; import { WorkflowDocumentStoreKey } from '@/app/constants/injectionKeys'; -import { STICKY_NODE_TYPE } from '@/app/constants/nodeTypes'; +import { + EXECUTE_WORKFLOW_NODE_TYPE, + EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE, + STICKY_NODE_TYPE, +} from '@/app/constants/nodeTypes'; import { useNodeTypesStore } from '@/app/stores/nodeTypes.store'; import { createWorkflowDocumentId, @@ -822,4 +826,23 @@ describe('useSelectionValidation', () => { expect(result.reason).toBe('multiple-output-branches'); } }); + + describe('isSubworkflowConversionDisabled', () => { + it.each([EXECUTE_WORKFLOW_NODE_TYPE, EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE])( + 'returns true when %s is not loaded', + (missingType) => { + vi.spyOn(useNodeTypesStore(), 'isNodeTypeUnavailable').mockImplementation( + (type) => type === missingType, + ); + + expect(useSelectionValidation().isSubworkflowConversionDisabled()).toBe(true); + }, + ); + + it('returns false when both sub-workflow node types are loaded', () => { + vi.spyOn(useNodeTypesStore(), 'isNodeTypeUnavailable').mockReturnValue(false); + + expect(useSelectionValidation().isSubworkflowConversionDisabled()).toBe(false); + }); + }); }); diff --git a/packages/frontend/editor-ui/src/app/composables/useSelectionValidation.ts b/packages/frontend/editor-ui/src/app/composables/useSelectionValidation.ts index 3543e8396fe..aaa8c4d811d 100644 --- a/packages/frontend/editor-ui/src/app/composables/useSelectionValidation.ts +++ b/packages/frontend/editor-ui/src/app/composables/useSelectionValidation.ts @@ -14,7 +14,11 @@ import { import { useNodeGroupRules } from '@/app/composables/useNodeGroupRules'; import { injectWorkflowDocumentStore } from '@/app/stores/workflowDocument.store'; import { useNodeTypesStore } from '@/app/stores/nodeTypes.store'; -import { STICKY_NODE_TYPE } from '@/app/constants/nodeTypes'; +import { + EXECUTE_WORKFLOW_NODE_TYPE, + EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE, + STICKY_NODE_TYPE, +} from '@/app/constants/nodeTypes'; import { useEmptyCanvasGroupsFlag } from '@/features/workflows/canvas/composables/useEmptyCanvasGroupsFlag'; import type { INodeUi } from '@/Interface'; @@ -31,6 +35,13 @@ export function useSelectionValidation() { const { allowTriggerInGroup, allowMultipleBoundaryNodes } = useNodeGroupRules(); const emptyCanvasGroupsEnabled = useEmptyCanvasGroupsFlag(); + function isSubworkflowConversionDisabled(): boolean { + return ( + nodeTypesStore.isNodeTypeUnavailable(EXECUTE_WORKFLOW_NODE_TYPE) || + nodeTypesStore.isNodeTypeUnavailable(EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE) + ); + } + /** * Expands a selection of node ids to include all sub-nodes (memory, tools, * models, etc.) attached to any selected node via non-main connections. @@ -133,6 +144,7 @@ export function useSelectionValidation() { } return { + isSubworkflowConversionDisabled, isSelectionExtractable, isSelectionGroupable, expandSelectionWithSubNodes, diff --git a/packages/frontend/editor-ui/src/app/composables/useWorkflowExtraction.test.ts b/packages/frontend/editor-ui/src/app/composables/useWorkflowExtraction.test.ts index 6f0c8bc68cd..bac593eec2c 100644 --- a/packages/frontend/editor-ui/src/app/composables/useWorkflowExtraction.test.ts +++ b/packages/frontend/editor-ui/src/app/composables/useWorkflowExtraction.test.ts @@ -2,6 +2,7 @@ import { setActivePinia } from 'pinia'; import { createTestingPinia } from '@pinia/testing'; import type { IConnections, INode, IWorkflowGroup } from 'n8n-workflow'; import { NodeConnectionTypes } from 'n8n-workflow'; +import { EXECUTE_WORKFLOW_NODE_TYPE, EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE } from '@/app/constants'; import type { INodeUi } from '@/Interface'; import type { WorkflowDataCreate } from '@n8n/rest-api-client/api/workflows'; @@ -37,6 +38,7 @@ const { deleteGroup: vi.fn(), }, mockNodeTypesStore: { + isNodeTypeUnavailable: vi.fn(), getNodeType: vi.fn().mockReturnValue({ displayName: 'Set', name: 'n8n-nodes-base.set', @@ -126,7 +128,6 @@ vi.mock('vue-router', () => ({ import { useWorkflowExtraction } from '@/app/composables/useWorkflowExtraction'; import { RemoveNodeGroupCommand, UpdateNodeGroupCommand } from '@/app/models/history'; -import { useSettingsStore } from '@n8n/stores/settings.store'; function makeNode(name: string, position: [number, number] = [0, 0]): INodeUi { return { @@ -187,6 +188,7 @@ describe('useWorkflowExtraction', () => { mockWorkflowDocumentStore.addNodesToGroup.mockReset(); mockWorkflowDocumentStore.deleteGroup.mockReset(); mockNodeTypesStore.getNodeType.mockClear(); + mockNodeTypesStore.isNodeTypeUnavailable.mockReset().mockReturnValue(false); mockHistoryStore.startRecordingUndo.mockClear(); mockHistoryStore.stopRecordingUndo.mockClear(); mockHistoryStore.pushCommandToUndo.mockClear(); @@ -204,16 +206,18 @@ describe('useWorkflowExtraction', () => { expect(mockTelemetry.track).not.toHaveBeenCalled(); }); - it('does not start extraction when executeWorkflow is excluded', () => { - const settingsStore = useSettingsStore(); - vi.spyOn(settingsStore, 'isSubworkflowConversionDisabled', 'get').mockReturnValue(true); + it.each([EXECUTE_WORKFLOW_NODE_TYPE, EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE])( + 'does not start extraction when %s is not loaded', + (missingType) => { + mockNodeTypesStore.isNodeTypeUnavailable.mockImplementation((type) => type === missingType); - const { extractWorkflow } = useWorkflowExtraction(); - extractWorkflow(['id-A']); + const { extractWorkflow } = useWorkflowExtraction(); + extractWorkflow(['id-A']); - expect(mockTelemetry.track).not.toHaveBeenCalled(); - expect(mockUIStore.openModalWithData).not.toHaveBeenCalled(); - }); + expect(mockTelemetry.track).not.toHaveBeenCalled(); + expect(mockUIStore.openModalWithData).not.toHaveBeenCalled(); + }, + ); it('includes attached sub-nodes when starting extraction', () => { const nodeA = makeNode('A', [0, 0]); diff --git a/packages/frontend/editor-ui/src/app/composables/useWorkflowExtraction.ts b/packages/frontend/editor-ui/src/app/composables/useWorkflowExtraction.ts index 47880bb0091..92b47740b58 100644 --- a/packages/frontend/editor-ui/src/app/composables/useWorkflowExtraction.ts +++ b/packages/frontend/editor-ui/src/app/composables/useWorkflowExtraction.ts @@ -27,7 +27,6 @@ import { useSelectionValidation } from './useSelectionValidation'; import type { AddedNode, INodeUi, IWorkflowDb } from '@/Interface'; import type { WorkflowDataCreate } from '@n8n/rest-api-client/api/workflows'; import { useI18n } from '@n8n/i18n'; -import { useSettingsStore } from '@n8n/stores/settings.store'; import { PUSH_NODES_OFFSET } from '@/app/utils/nodeViewUtils'; import { useUIStore } from '@/app/stores/ui.store'; import { useNodeTypesStore } from '@/app/stores/nodeTypes.store'; @@ -48,7 +47,6 @@ export function useWorkflowExtraction() { const workflowsStore = useWorkflowsStore(); const workflowDocumentStore = injectWorkflowDocumentStore(); const nodeTypesStore = useNodeTypesStore(); - const settingsStore = useSettingsStore(); const toast = useToast(); const router = useRouter(); const historyStore = useHistoryStore(); @@ -56,7 +54,8 @@ export function useWorkflowExtraction() { const i18n = useI18n(); const telemetry = useTelemetry(); const groupTelemetry = useCanvasNodeGroupTelemetry(); - const { expandSelectionWithSubNodes, isSelectionExtractable } = useSelectionValidation(); + const { expandSelectionWithSubNodes, isSelectionExtractable, isSubworkflowConversionDisabled } = + useSelectionValidation(); function showError(message: string) { toast.showMessage({ @@ -691,7 +690,7 @@ export function useWorkflowExtraction() { * @param nodeIds the ids to be extracted from the current workflow into a sub-workflow */ function extractWorkflow(nodeIds: string[]) { - if (nodeIds.length === 0 || settingsStore.isSubworkflowConversionDisabled) return; + if (nodeIds.length === 0 || isSubworkflowConversionDisabled()) return; const success = tryExtractNodesIntoSubworkflow(nodeIds); trackStartExtractWorkflow(nodeIds.length, success); diff --git a/packages/frontend/editor-ui/src/app/stores/nodeTypes.store.test.ts b/packages/frontend/editor-ui/src/app/stores/nodeTypes.store.test.ts index f7d2b8e5e71..5319d21bd9a 100644 --- a/packages/frontend/editor-ui/src/app/stores/nodeTypes.store.test.ts +++ b/packages/frontend/editor-ui/src/app/stores/nodeTypes.store.test.ts @@ -5,6 +5,8 @@ import type { INodeTypeDescription } from 'n8n-workflow'; import { useNodeTypesStore } from '@/app/stores/nodeTypes.store'; import * as nodeTypesApi from '@n8n/rest-api-client/api/nodeTypes'; import { LOCAL_STORAGE_DATA_WORKER } from '@/app/constants/localStorage'; +import { useSettingsStore } from '@n8n/stores/settings.store'; +import type { CommunityNodeType } from '@n8n/api-types'; const mocks = vi.hoisted(() => ({ rootStore: { @@ -52,6 +54,42 @@ describe('useNodeTypesStore', () => { store = useNodeTypesStore(); }); + describe('isNodeTypeUnavailable', () => { + beforeEach(() => { + setActivePinia(createTestingPinia({ stubActions: false })); + store = useNodeTypesStore(); + store.setNodeTypes([makeNodeType({ name: 'n8n-nodes-test.loaded', outputs: ['main'] })]); + }); + + it('should return false for a loaded node type', () => { + expect(store.isNodeTypeUnavailable('n8n-nodes-test.loaded')).toBe(false); + }); + + it('should return false for a loaded node type named with the preview token', () => { + expect(store.isNodeTypeUnavailable('n8n-nodes-preview-test.loaded')).toBe(false); + }); + + it('should return true for a node type that is not loaded', () => { + expect(store.isNodeTypeUnavailable('n8n-nodes-test.missing')).toBe(true); + }); + + it('should return false before any node types are loaded', () => { + store.nodeTypes = {}; + + expect(store.isNodeTypeUnavailable('n8n-nodes-test.missing')).toBe(false); + }); + + it('should return false for a vetted community node type that is not installed', async () => { + vi.spyOn(useSettingsStore(), 'isCommunityNodesFeatureEnabled', 'get').mockReturnValue(true); + vi.mocked(nodeTypesApi.fetchCommunityNodeTypes).mockResolvedValueOnce([ + { name: 'n8n-nodes-vetted.node', nodeDescription: { name: 'n8n-nodes-vetted.node' } }, + ] as CommunityNodeType[]); + await store.fetchCommunityNodePreviews(); + + expect(store.isNodeTypeUnavailable('n8n-nodes-vetted.node')).toBe(false); + }); + }); + describe('isModelNode', () => { it('should return true for a node that outputs AiLanguageModel', () => { const nodeType = makeNodeType({ diff --git a/packages/frontend/editor-ui/src/app/stores/nodeTypes.store.ts b/packages/frontend/editor-ui/src/app/stores/nodeTypes.store.ts index 31c2d7f4613..90f0fb9ccc7 100644 --- a/packages/frontend/editor-ui/src/app/stores/nodeTypes.store.ts +++ b/packages/frontend/editor-ui/src/app/stores/nodeTypes.store.ts @@ -504,6 +504,16 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, () => { } }; + // True for types the instance does not load (NODES_EXCLUDE, NODES_INCLUDE). Vetted community + // types stay available: the node creator offers them for installation. Before the types load, + // nothing counts as unavailable, so callers do not hide or skip every node. + function isNodeTypeUnavailable(nodeTypeName: string): boolean { + if (Object.keys(nodeTypes.value).length === 0) return false; + return ( + !getNodeType.value(removePreviewToken(nodeTypeName)) && !communityNodeType.value(nodeTypeName) + ); + } + const getIsNodeInstalled = computed(() => { return (nodeTypeName: string) => { const cleanedNodeTypeName = removePreviewToken(nodeTypeName); @@ -583,5 +593,6 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, () => { removeNodeTypes, getCommunityNodeAttributes, getIsNodeInstalled, + isNodeTypeUnavailable, }; }); diff --git a/packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/SetupWizard/SetupWizard.vue b/packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/SetupWizard/SetupWizard.vue index 09b635094fb..22570f35d0d 100644 --- a/packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/SetupWizard/SetupWizard.vue +++ b/packages/frontend/editor-ui/src/features/ai/evaluation.ee/components/SetupWizard/SetupWizard.vue @@ -11,6 +11,8 @@ import { I18nT } from 'vue-i18n'; import { N8nButton, N8nCallout, N8nText } from '@n8n/design-system'; import { injectWorkflowDocumentStore } from '@/app/stores/workflowDocument.store'; +import { useNodeTypesStore } from '@/app/stores/nodeTypes.store'; +import { EVALUATION_TRIGGER_NODE_TYPE } from 'n8n-workflow'; import { useWorkflowEvaluationState } from '../../composables/useWorkflowEvaluationState'; defineEmits<{ runTest: []; @@ -20,6 +22,7 @@ const router = useRouter(); const locale = useI18n(); const workflowDocumentStore = injectWorkflowDocumentStore(); const evaluationStore = useEvaluationStore(); +const nodeTypesStore = useNodeTypesStore(); const evaluationState = useWorkflowEvaluationState(); const usageStore = useUsageStore(); const pageRedirectionHelper = usePageRedirectionHelper(); @@ -119,7 +122,10 @@ function onSeePlans() { -
+
({ useNodeTypesStore: vi.fn(() => ({ getNodeType, + isNodeTypeUnavailable: () => false, getAllNodeTypes: vi.fn().mockReturnValue({ nodeTypes: {}, init: async () => {}, diff --git a/packages/frontend/editor-ui/src/features/shared/commandBar/composables/useNodeCommands.test.ts b/packages/frontend/editor-ui/src/features/shared/commandBar/composables/useNodeCommands.test.ts index e0bdbb50df9..a0a6d59e666 100644 --- a/packages/frontend/editor-ui/src/features/shared/commandBar/composables/useNodeCommands.test.ts +++ b/packages/frontend/editor-ui/src/features/shared/commandBar/composables/useNodeCommands.test.ts @@ -91,6 +91,7 @@ describe('useNodeCommands', () => { mockCanvasEventBusEmit = vi.mocked(canvasEventBus.emit); mockNodeTypesStore = useNodeTypesStore(); + vi.spyOn(mockNodeTypesStore, 'isNodeTypeUnavailable').mockReturnValue(false); mockSourceControlStore = useSourceControlStore(); mockWorkflowsStore = useWorkflowsStore(); @@ -344,6 +345,17 @@ describe('useNodeCommands', () => { expect(stickyCommand).toBeDefined(); }); + it('should not include add sticky note command when the sticky note type is not loaded', () => { + vi.mocked(mockNodeTypesStore.isNodeTypeUnavailable).mockReturnValue(true); + + const { commands } = useNodeCommands({ + lastQuery: ref(''), + activeNodeId: ref(null), + }); + + expect(commands.value.find((cmd) => cmd.id === 'add-sticky')).toBeUndefined(); + }); + it('should not include add sticky note command when user lacks update permission', () => { mockGetResourcePermissions.mockReturnValue({ workflow: { update: false, execute: true }, diff --git a/packages/frontend/editor-ui/src/features/shared/commandBar/composables/useNodeCommands.ts b/packages/frontend/editor-ui/src/features/shared/commandBar/composables/useNodeCommands.ts index 2481d6a6a71..221718069f1 100644 --- a/packages/frontend/editor-ui/src/features/shared/commandBar/composables/useNodeCommands.ts +++ b/packages/frontend/editor-ui/src/features/shared/commandBar/composables/useNodeCommands.ts @@ -2,6 +2,7 @@ import { type Component, computed, type Ref } from 'vue'; import { useI18n } from '@n8n/i18n'; import { N8nIcon } from '@n8n/design-system'; import { useNodeTypesStore } from '@/app/stores/nodeTypes.store'; +import { STICKY_NODE_TYPE } from '@/app/constants'; import { useCredentialsStore } from '@/features/credentials/credentials.store'; import { useCanvasOperations } from '@/app/composables/useCanvasOperations'; import { useActionsGenerator } from '@/features/shared/nodeCreator/composables/useActionsGeneration'; @@ -197,7 +198,7 @@ export function useNodeCommands(options: { }, }, ...rootOpenNodeCommandItems.value, - ...(hasPermission('update') + ...(hasPermission('update') && !nodeTypesStore.isNodeTypeUnavailable(STICKY_NODE_TYPE) ? [ { id: ITEM_ID.ADD_STICKY, diff --git a/packages/frontend/editor-ui/src/features/shared/contextMenu/composables/useContextMenu.test.ts b/packages/frontend/editor-ui/src/features/shared/contextMenu/composables/useContextMenu.test.ts index 1ed61bcd934..bdd30e9ef3e 100644 --- a/packages/frontend/editor-ui/src/features/shared/contextMenu/composables/useContextMenu.test.ts +++ b/packages/frontend/editor-ui/src/features/shared/contextMenu/composables/useContextMenu.test.ts @@ -23,7 +23,7 @@ import { useSourceControlStore } from '@/features/integrations/sourceControl.ee/ import { useUIStore } from '@/app/stores/ui.store'; import { useWorkflowsStore } from '@/app/stores/workflows.store'; import { useFocusedNodesStore } from '@/features/ai/assistant/focusedNodes.store'; -import { useSettingsStore } from '@n8n/stores/settings.store'; +import { useNodeTypesStore } from '@/app/stores/nodeTypes.store'; import { useTypeAvailabilityPoliciesStore } from '@n8n/frontend-module-type-availability-policies'; import { useWorkflowDocumentStore, @@ -128,6 +128,7 @@ describe('useContextMenu', () => { groupViewState.current = undefined; mockEmptyCanvasGroupsEnabled.value = true; setActivePinia(createPinia()); + vi.spyOn(useNodeTypesStore(), 'isNodeTypeUnavailable').mockReturnValue(false); sourceControlStore = useSourceControlStore(); vi.spyOn(sourceControlStore, 'preferences', 'get').mockReturnValue({ branchReadOnly: false, @@ -195,8 +196,9 @@ describe('useContextMenu', () => { describe('extract_sub_workflow gating', () => { it('hides convert to sub-workflow when executeWorkflow is excluded', () => { - const settingsStore = useSettingsStore(); - vi.spyOn(settingsStore, 'isSubworkflowConversionDisabled', 'get').mockReturnValue(true); + vi.spyOn(useNodeTypesStore(), 'isNodeTypeUnavailable').mockImplementation( + (type) => type === EXECUTE_WORKFLOW_NODE_TYPE, + ); const { open, actions } = useContextMenu(); open(mockEvent, { source: 'canvas', nodeIds: selectedNodes.map((n) => n.id) }); @@ -205,8 +207,9 @@ describe('useContextMenu', () => { }); it('hides convert to sub-workflow on a group target when executeWorkflow is excluded', () => { - const settingsStore = useSettingsStore(); - vi.spyOn(settingsStore, 'isSubworkflowConversionDisabled', 'get').mockReturnValue(true); + vi.spyOn(useNodeTypesStore(), 'isNodeTypeUnavailable').mockImplementation( + (type) => type === EXECUTE_WORKFLOW_NODE_TYPE, + ); const group = workflowDocumentStore.createGroup([nodes[0].id, nodes[1].id], 'My group'); const { open, actions } = useContextMenu(); @@ -536,6 +539,16 @@ describe('useContextMenu', () => { expect(byId.add_sticky?.disabled).toBe(true); }); + it('leaves out add sticky when the sticky note type is not loaded', () => { + vi.mocked(useNodeTypesStore().isNodeTypeUnavailable).mockImplementation( + (type) => type === STICKY_NODE_TYPE, + ); + const { open, actions } = useContextMenu(); + open(mockEvent, { source: 'canvas', nodeIds: [] }); + + expect(actions.value.some((action) => action.id === 'add_sticky')).toBe(false); + }); + it('keeps the mutating actions enabled when the target is not read-only', () => { const { open, actions } = useContextMenu(); open(mockEvent, { source: 'canvas', nodeIds: selectedNodes.map((n) => n.id) }); diff --git a/packages/frontend/editor-ui/src/features/shared/contextMenu/composables/useContextMenuItems.ts b/packages/frontend/editor-ui/src/features/shared/contextMenu/composables/useContextMenuItems.ts index 20c43944416..2e8bebaf6bd 100644 --- a/packages/frontend/editor-ui/src/features/shared/contextMenu/composables/useContextMenuItems.ts +++ b/packages/frontend/editor-ui/src/features/shared/contextMenu/composables/useContextMenuItems.ts @@ -13,7 +13,6 @@ import { usePostHog } from '@/app/stores/posthog.store'; import { useI18n } from '@n8n/i18n'; import { CANVAS_NODE_CONTEXT_FLAG } from '@n8n/api-types'; import { getResourcePermissions } from '@n8n/permissions'; -import { useSettingsStore } from '@n8n/stores/settings.store'; import type { INode, INodeTypeDescription } from 'n8n-workflow'; import { getEmptyGroupAnchor, NodeHelpers, WEBHOOK_NODE_TYPE } from 'n8n-workflow'; import { computed, type ComputedRef } from 'vue'; @@ -89,13 +88,12 @@ export function useContextMenuItems( ): ComputedRef { const uiStore = useUIStore(); const nodeTypesStore = useNodeTypesStore(); - const settingsStore = useSettingsStore(); const workflowDocumentStore = injectWorkflowDocumentStore(); const sourceControlStore = useSourceControlStore(); const collaborationStore = useCollaborationStore(); const focusedNodesStore = useFocusedNodesStore(); const posthog = usePostHog(); - const { resolveGroupableNodeIds } = useSelectionValidation(); + const { resolveGroupableNodeIds, isSubworkflowConversionDisabled } = useSelectionValidation(); const groupView = injectContextMenuGroupView(); const i18n = useI18n(); @@ -289,9 +287,7 @@ export function useContextMenuItems( const onlyStickies = nodes.every((node) => node.type === STICKY_NODE_TYPE); const canExtract = - !settingsStore.isSubworkflowConversionDisabled && - nodes.some(isExecutable) && - !nodes.every(isAiSubNode); + !isSubworkflowConversionDisabled() && nodes.some(isExecutable) && !nodes.every(isAiSubNode); const i18nOptions = isGroupTarget ? { @@ -469,12 +465,16 @@ export function useContextMenuItems( label: i18n.baseText('contextMenu.addNode'), disabled: isReadOnly.value, }, - { - id: 'add_sticky', - shortcut: { shiftKey: true, keys: ['s'] }, - label: i18n.baseText('contextMenu.addSticky'), - disabled: isReadOnly.value, - }, + ...(nodeTypesStore.isNodeTypeUnavailable(STICKY_NODE_TYPE) + ? [] + : [ + { + id: 'add_sticky' as const, + shortcut: { shiftKey: true, keys: ['s'] }, + label: i18n.baseText('contextMenu.addSticky'), + disabled: isReadOnly.value, + }, + ]), ...layoutActions, ...groupViewActions, // Join the group-view section diff --git a/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/ActionsMode.vue b/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/ActionsMode.vue index 4bbad7d875f..17e5aaa7077 100644 --- a/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/ActionsMode.vue +++ b/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/ActionsMode.vue @@ -17,6 +17,7 @@ import { import { useUsersStore } from '@n8n/stores/users.store'; import { useExternalHooks } from '@/app/composables/useExternalHooks'; +import { useNodeTypesStore } from '@/app/stores/nodeTypes.store'; import { useActions } from '../../composables/useActions'; import { useKeyboardNavigation } from '../../composables/useKeyboardNavigation'; @@ -57,6 +58,7 @@ const { } = useActions(); const nodeCreatorStore = useNodeCreatorStore(); +const nodeTypesStore = useNodeTypesStore(); const { openSampleWorkflowTemplate } = useCalloutHelpers(); // We only inject labels if search is empty @@ -131,7 +133,7 @@ const containsAPIAction = computed(() => { return ((p as ActionCreateElement).properties.actionKey ?? '') === CUSTOM_API_CALL_KEY; }); - return result; + return result && !nodeTypesStore.isNodeTypeUnavailable(HTTP_REQUEST_NODE_TYPE); }); const isTriggerRootView = computed(() => rootView.value === TRIGGER_NODE_CREATOR_VIEW); diff --git a/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/NodesMode.test.ts b/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/NodesMode.test.ts index 831e3f83aa8..e62b9e5734e 100644 --- a/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/NodesMode.test.ts +++ b/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/NodesMode.test.ts @@ -24,6 +24,7 @@ import { createComponentRenderer } from '@/__tests__/render'; import { waitAllPromises } from '@n8n/frontend-test-utils'; import { mockRestrictedNodeTypes } from '@/__tests__/mocks'; import { mockSimplifiedNodeType } from '../../__tests__/utils'; +import { useNodeTypesStore } from '@/app/stores/nodeTypes.store'; import NodesMode from './NodesMode.vue'; const mockIsFeatureEnabled = vi.hoisted(() => vi.fn(() => true)); @@ -108,6 +109,7 @@ describe('NodesMode', () => { mockIsFeatureEnabled.mockReturnValue(true); pinia = createPinia(); setActivePinia(pinia); + vi.spyOn(useNodeTypesStore(), 'isNodeTypeUnavailable').mockReturnValue(false); }); afterEach(() => { @@ -245,6 +247,26 @@ describe('NodesMode', () => { expect(emitted('nodeTypeSelected')).toEqual([[[{ type: 'n8n-nodes-base.webhook' }]]]); }); + it('drops a node type that is not loaded from the empty-search suggestions', async () => { + vi.mocked(useNodeTypesStore().isNodeTypeUnavailable).mockImplementation( + (type) => type === HTTP_REQUEST_NODE_TYPE, + ); + useViewStacks().pushViewStack({ + title: 'What triggers this workflow?', + mode: 'nodes', + rootView: TRIGGER_NODE_CREATOR_VIEW, + search: 'missing node', + items: [], + }); + + render({ pinia }); + await nextTick(); + + expect(screen.getByText('No results for "missing node"')).toBeInTheDocument(); + expect(screen.queryByText('HTTP Request')).not.toBeInTheDocument(); + expect(screen.getByText('Webhook')).toBeInTheDocument(); + }); + it('still adds an available node on Enter', async () => { pushSearchStackWith([setNodeElement()]); diff --git a/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/NodesMode.vue b/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/NodesMode.vue index d2d04f62559..8388bbc0be2 100644 --- a/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/NodesMode.vue +++ b/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Modes/NodesMode.vue @@ -94,7 +94,8 @@ const isMcpCategory = computed(() => activeViewStack.value.subcategory === AI_CA const globalSearchItemsDiff = computed(() => useViewStacks().globalSearchItemsDiff); const workflowDocumentStore = injectWorkflowDocumentStore(); -const communityNodesAndActions = computed(() => useNodeTypesStore().communityNodesAndActions); +const nodeTypesStore = useNodeTypesStore(); +const communityNodesAndActions = computed(() => nodeTypesStore.communityNodesAndActions); const moreFromCommunity = computed(() => { const hits = filterAndSearchNodes( @@ -186,7 +187,7 @@ function onSelected(item: INodeCreateElement) { const payload = nodeCreateElementToNodeTypeSelectedPayload(item); let nodeActions = getFilteredActions(item, actions); const notInstalledCommunityNode = - isCommunityPackageName(item.key) && !useNodeTypesStore().getIsNodeInstalled(item.key); + isCommunityPackageName(item.key) && !nodeTypesStore.getIsNodeInstalled(item.key); const nodeIcon = getNodeIconSource( item.properties, null, @@ -412,8 +413,14 @@ registerKeyHook('MainViewArrowLeft', { diff --git a/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Panel/NodesListPanel.test.ts b/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Panel/NodesListPanel.test.ts index e68a2757bb8..0f9ed0d1fbf 100644 --- a/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Panel/NodesListPanel.test.ts +++ b/packages/frontend/editor-ui/src/features/shared/nodeCreator/components/Panel/NodesListPanel.test.ts @@ -6,11 +6,15 @@ import type { INodeTypeDescription } from 'n8n-workflow'; import { useNodeCreatorStore } from '@/features/shared/nodeCreator/nodeCreator.store'; import { useViewStacks } from '@/features/shared/nodeCreator/composables/useViewStacks'; import { mockRestrictedNodeTypes } from '@/__tests__/mocks'; -import { mockSimplifiedNodeType } from '../../__tests__/utils'; +import { useNodeTypesStore } from '@/app/stores/nodeTypes.store'; +import { mockActionCreateElement, mockSimplifiedNodeType } from '../../__tests__/utils'; import NodesListPanel from './NodesListPanel.vue'; import { REGULAR_NODE_CREATOR_VIEW, DEBOUNCE_TIME, + SCHEDULE_TRIGGER_NODE_TYPE, + CUSTOM_API_CALL_KEY, + HTTP_REQUEST_NODE_TYPE, NODE_CREATOR_OPEN_SOURCES, } from '@/app/constants'; import type { ActionTypeDescription, NodeFilterType, SimplifiedNodeType } from '@/Interface'; @@ -92,6 +96,7 @@ describe('NodesListPanel', () => { const { container } = getWrapperComponent(() => { const { setMergeNodes } = useNodeCreatorStore(); + vi.spyOn(useNodeTypesStore(), 'isNodeTypeUnavailable').mockReturnValue(false); setMergeNodes([...mockedTriggerNodes, ...mockedRegularNodes]); return {}; }); @@ -118,6 +123,19 @@ describe('NodesListPanel', () => { expect(screen.queryAllByTestId('item-iterator-item')).toHaveLength(10); }); + it('should hide a listed trigger whose node type is not loaded', async () => { + getWrapperComponent(() => { + vi.spyOn(useNodeTypesStore(), 'isNodeTypeUnavailable').mockImplementation( + (type) => type === SCHEDULE_TRIGGER_NODE_TYPE, + ); + return {}; + }); + await nextTick(); + + expect(screen.queryByText('On a schedule')).not.toBeInTheDocument(); + expect(screen.getByText('Trigger manually')).toBeInTheDocument(); + }); + it('should render regular nodes', async () => { const mockedNodes = [...Array(8).keys()].map( (n) => @@ -788,6 +806,38 @@ describe('NodesListPanel', () => { }); }); + describe('custom API call hint', () => { + it.each([ + [false, true], + [true, false], + ])( + 'should set hint visibility when HTTP Request unavailable is %s (visible: %s)', + async (httpUnavailable, hintVisible) => { + getWrapperComponent(() => { + vi.spyOn(useNodeTypesStore(), 'isNodeTypeUnavailable').mockImplementation( + (type) => httpUnavailable && type === HTTP_REQUEST_NODE_TYPE, + ); + return {}; + }); + await nextTick(); + + useViewStacks().pushViewStack({ + title: 'Slack', + subcategory: 'Slack', + mode: 'actions', + hasSearch: true, + items: [ + mockActionCreateElement('Slack'), + mockActionCreateElement('Slack', { actionKey: CUSTOM_API_CALL_KEY }), + ], + }); + await nextTick(); + + expect(screen.queryByText(/custom Slack API call/) !== null).toBe(hintVisible); + }, + ); + }); + describe('restricted node types', () => { const triggerNodes = [...Array(9).keys()].map( (n) => diff --git a/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useActions.test.ts b/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useActions.test.ts index b4da69fb805..87bf70e5de2 100644 --- a/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useActions.test.ts +++ b/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useActions.test.ts @@ -81,6 +81,7 @@ describe('useActions', () => { afterEach(() => { vi.clearAllMocks(); + vi.mocked(useNodeTypesStore().isNodeTypeUnavailable).mockReset(); mockDocumentStoreState.allNodes = []; mockDocumentStoreState.getNodeById = () => undefined; useUIStore().lastInteractedWithNodeId = undefined; @@ -154,6 +155,29 @@ describe('useActions', () => { }); }); + test('should fall back to a manual trigger when the Chat Trigger type is not loaded', () => { + const nodeCreatorStore = useNodeCreatorStore(); + mockDocumentStoreState.workflowTriggerNodes = []; + mockDocumentStoreState.allNodes = []; + vi.spyOn(nodeCreatorStore, 'openSource', 'get').mockReturnValue( + NODE_CREATOR_OPEN_SOURCES.ADD_NODE_BUTTON, + ); + vi.spyOn(nodeCreatorStore, 'selectedView', 'get').mockReturnValue(TRIGGER_NODE_CREATOR_VIEW); + vi.mocked(useNodeTypesStore().isNodeTypeUnavailable).mockImplementation( + (type) => type === CHAT_TRIGGER_NODE_TYPE, + ); + + const { getAddedNodesAndConnections } = useActions(); + + expect(getAddedNodesAndConnections([{ type: AGENT_NODE_TYPE }])).toEqual({ + connections: [{ from: { nodeIndex: 0 }, to: { nodeIndex: 1 } }], + nodes: [ + { type: MANUAL_TRIGGER_NODE_TYPE, isAutoAdd: true }, + { type: AGENT_NODE_TYPE, openDetail: true }, + ], + }); + }); + test('should insert a ChatTrigger node when an AI Agent is added on an empty canvas', () => { mockDocumentStoreState.workflowTriggerNodes = []; mockDocumentStoreState.allNodes = []; @@ -267,6 +291,32 @@ describe('useActions', () => { }); }); + test('should skip an auto-added node whose type is not loaded and keep the other connections', () => { + const nodeTypesStore = useNodeTypesStore(); + nodeTypesStore.nodeTypes = { + [OPEN_AI_CHAT_MODEL_NODE_TYPE]: mockLanguageModelNodeType(OPEN_AI_CHAT_MODEL_NODE_TYPE), + }; + vi.mocked(nodeTypesStore.isNodeTypeUnavailable).mockImplementation( + (type) => type === CHAT_TRIGGER_NODE_TYPE, + ); + mockDocumentStoreState.aiNodes = []; + + const { getAddedNodesAndConnections } = useActions(); + + expect(getAddedNodesAndConnections([{ type: OPEN_AI_CHAT_MODEL_NODE_TYPE }])).toEqual({ + connections: [ + { + from: { nodeIndex: 1, type: NodeConnectionTypes.AiLanguageModel }, + to: { nodeIndex: 0 }, + }, + ], + nodes: [ + { type: CHAIN_LLM_LANGCHAIN_NODE_TYPE, isAutoAdd: true }, + { type: OPEN_AI_CHAT_MODEL_NODE_TYPE, openDetail: true }, + ], + }); + }); + test('should not insert a ChatTrigger node when an AI Agent is added with a non-trigger node prseent', () => { mockDocumentStoreState.workflowTriggerNodes = [{ type: GITHUB_TRIGGER_NODE_TYPE } as never]; mockDocumentStoreState.allNodes = [ diff --git a/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useActions.ts b/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useActions.ts index 9e37a143a90..db70d2a7e47 100644 --- a/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useActions.ts +++ b/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useActions.ts @@ -277,6 +277,7 @@ export const useActions = () => { const isCompatibleNode = addedNodes.some((node) => COMPATIBLE_CHAT_NODES.includes(node.type)); if (!isCompatibleNode) return false; + if (nodeTypesStore.isNodeTypeUnavailable(CHAT_TRIGGER_NODE_TYPE)) return false; const allNodes = workflowDocumentStore.value.allNodes; return allNodes.filter((x) => x.type !== MANUAL_TRIGGER_NODE_TYPE).length === 0; @@ -396,7 +397,19 @@ export const useActions = () => { } }); - return { nodes, connections }; + // Skip auto-added nodes the instance does not load, and their connections. + const keptIndexes = nodes.flatMap((node, index) => + node.isAutoAdd && nodeTypesStore.isNodeTypeUnavailable(node.type) ? [] : [index], + ); + return { + nodes: keptIndexes.map((index) => nodes[index]), + connections: connections.flatMap(({ from, to }) => { + const fromIndex = keptIndexes.indexOf(from.nodeIndex); + const toIndex = keptIndexes.indexOf(to.nodeIndex); + if (fromIndex === -1 || toIndex === -1) return []; + return [{ from: { ...from, nodeIndex: fromIndex }, to: { ...to, nodeIndex: toIndex } }]; + }), + }; } function setAddedNodeActionParameters( diff --git a/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useViewStacks.ts b/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useViewStacks.ts index 92a5af1c815..684a8ed18f8 100644 --- a/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useViewStacks.ts +++ b/packages/frontend/editor-ui/src/features/shared/nodeCreator/composables/useViewStacks.ts @@ -116,6 +116,7 @@ const nodePopularityMap = Object.values(nodePopularity).reduce((acc, node) => { export const useViewStacks = defineStore('nodeCreatorViewStacks', () => { const nodeCreatorStore = useNodeCreatorStore(); + const nodeTypesStore = useNodeTypesStore(); const workflowDocumentStore = injectWorkflowDocumentStore(); const { getActiveItemIndex } = useKeyboardNavigation(); const i18n = useI18n(); @@ -545,7 +546,10 @@ export const useViewStacks = defineStore('nodeCreatorViewStacks', () => { const stack = getLastActiveStack(); if (!stack || !activeViewStack.value.uuid) return; - let stackItems = stack?.items ?? []; + // Views list some nodes by hand. Every other list is built from the loaded node types. + let stackItems = (stack?.items ?? []).filter( + (item) => item.type !== 'node' || !nodeTypesStore.isNodeTypeUnavailable(item.key), + ); if (!stack?.items) { const subcategory = stack?.subcategory ?? DEFAULT_SUBCATEGORY; diff --git a/packages/frontend/editor-ui/src/features/shared/nodeCreator/views/NodeCreation.vue b/packages/frontend/editor-ui/src/features/shared/nodeCreator/views/NodeCreation.vue index 460c40a0ebd..a731fbe4f89 100644 --- a/packages/frontend/editor-ui/src/features/shared/nodeCreator/views/NodeCreation.vue +++ b/packages/frontend/editor-ui/src/features/shared/nodeCreator/views/NodeCreation.vue @@ -10,6 +10,7 @@ import { STICKY_NODE_TYPE, } from '@/app/constants'; import { useUIStore } from '@/app/stores/ui.store'; +import { useNodeTypesStore } from '@/app/stores/nodeTypes.store'; import { useEditorContext } from '@/app/composables/useEditorContext'; import { useInstanceAiEditorCapability } from '@/app/composables/useInstanceAiEditorCapability'; import { useFocusPanelStore } from '@/app/stores/focusPanel.store'; @@ -62,6 +63,7 @@ const emit = defineEmits<{ }>(); const uiStore = useUIStore(); +const nodeTypesStore = useNodeTypesStore(); const focusPanelStore = useFocusPanelStore(); const setupPanelStore = useSetupPanelStore(); const i18n = useI18n(); @@ -222,6 +224,7 @@ function openCommandBar(event: MouseEvent) { /> ; beforeEach(() => { const pinia = createPinia(); setActivePinia(pinia); + vi.spyOn(useNodeTypesStore(), 'isNodeTypeUnavailable').mockReturnValue(false); workflowDocumentStore = useWorkflowDocumentStore(createWorkflowDocumentId('wf-test')); renderComponent = createComponentRenderer(Canvas, { @@ -1080,7 +1082,9 @@ describe('Canvas', () => { }); it('hides the convert button when executeWorkflow is excluded', async () => { - vi.spyOn(useSettingsStore(), 'isSubworkflowConversionDisabled', 'get').mockReturnValue(true); + vi.spyOn(useNodeTypesStore(), 'isNodeTypeUnavailable').mockImplementation( + (type) => type === EXECUTE_WORKFLOW_NODE_TYPE, + ); const rendered = await setupExpandedGroupWithLooseNodes(); diff --git a/packages/frontend/editor-ui/src/features/workflows/canvas/components/Canvas.vue b/packages/frontend/editor-ui/src/features/workflows/canvas/components/Canvas.vue index 8ea6c3fdba9..8be59cf8e58 100644 --- a/packages/frontend/editor-ui/src/features/workflows/canvas/components/Canvas.vue +++ b/packages/frontend/editor-ui/src/features/workflows/canvas/components/Canvas.vue @@ -12,6 +12,7 @@ import { CANVAS_GROUP_HEADER_TOGGLE_SUPPRESS_DURATION, CanvasKey, MODAL_CONFIRM, + STICKY_NODE_TYPE, } from '@/app/constants'; import { useMessage } from '@/app/composables/useMessage'; import { findGroupIdsWithTrigger } from '../nodeGroups.utils'; @@ -464,7 +465,7 @@ const { readOnly: () => props.readOnly || props.suppressInteraction, }); -const { isSelectionExtractable } = useSelectionValidation(); +const { isSelectionExtractable, isSubworkflowConversionDisabled } = useSelectionValidation(); const { allowTriggerInGroup } = useNodeGroupRules(); // Groups that start the workflow themselves @@ -483,7 +484,7 @@ const groupIdsWithTrigger = computed(() => { // Groups that can be extracted to sub-workflows const extractableGroupIds = computed(() => { const ids = new Set(); - if (settingsStore.isSubworkflowConversionDisabled) return ids; + if (isSubworkflowConversionDisabled()) return ids; for (const group of workflowDocumentStore.value.allGroups) { if (isEmptyGroup(group.id)) continue; if (isSelectionExtractable(group.nodeIds).valid) { @@ -595,7 +596,10 @@ const keyMap = computed(() => { props.eventBus.emit('deprecated:tab-shortcut'); }, }, - shift_s: () => emit('create:sticky'), + shift_s: { + disabled: () => nodeTypesStore.isNodeTypeUnavailable(STICKY_NODE_TYPE), + run: () => emit('create:sticky'), + }, shift_f: () => emit('toggle:focus-panel'), ctrl_alt_n: { disabled: () => settingsStore.isCanvasOnly, @@ -610,7 +614,7 @@ const keyMap = computed(() => { }, shift_alt_t: async () => await onTidyUp({ source: 'keyboard-shortcut' }), alt_x: { - disabled: () => settingsStore.isSubworkflowConversionDisabled || soleSelectedEmptyGroup.value, + disabled: () => isSubworkflowConversionDisabled() || soleSelectedEmptyGroup.value, run: emitWithSelectedNodes((ids) => emit('extract-workflow', ids)), }, c: () => emit('start-chat'), diff --git a/packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/selection/CanvasSelectionToolbar.test.ts b/packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/selection/CanvasSelectionToolbar.test.ts index 09f8f091fdd..411d2534c21 100644 --- a/packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/selection/CanvasSelectionToolbar.test.ts +++ b/packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/selection/CanvasSelectionToolbar.test.ts @@ -10,10 +10,10 @@ import { useWorkflowDocumentStore, createWorkflowDocumentId, } from '@/app/stores/workflowDocument.store'; -import { useSettingsStore } from '@n8n/stores/settings.store'; const isSelectionGroupableMock = vi.fn(); const isSelectionExtractableMock = vi.fn(); +const isSubworkflowConversionDisabledMock = vi.fn(); const expandSelectionWithSubNodesMock = vi.fn((nodeIds: string[]) => nodeIds); // Composes the two mocks above (like the real implementation does, minus id // resolution) so tests keep customizing those as knobs. @@ -37,6 +37,7 @@ vi.mock('@/app/composables/useSelectionValidation', () => ({ useSelectionValidation: () => ({ isSelectionGroupable: isSelectionGroupableMock, isSelectionExtractable: isSelectionExtractableMock, + isSubworkflowConversionDisabled: isSubworkflowConversionDisabledMock, expandSelectionWithSubNodes: expandSelectionWithSubNodesMock, resolveGroupableNodeIds: resolveGroupableNodeIdsMock, }), @@ -79,6 +80,7 @@ describe('CanvasSelectionToolbar', () => { setActivePinia(createPinia()); workflowDocumentStore = useWorkflowDocumentStore(createWorkflowDocumentId('wf-test')); isSelectionGroupableMock.mockImplementation(getDefaultGroupableResult); + isSubworkflowConversionDisabledMock.mockReturnValue(false); isSelectionExtractableMock.mockImplementation((nodeIds: string[]) => nodeIds.length < 2 ? { valid: false, reason: 'invalid-subgraph' } @@ -189,7 +191,7 @@ describe('CanvasSelectionToolbar', () => { }); it('hides Extract when executeWorkflow is excluded', () => { - vi.spyOn(useSettingsStore(), 'isSubworkflowConversionDisabled', 'get').mockReturnValue(true); + isSubworkflowConversionDisabledMock.mockReturnValue(true); const wrapper = render({ selectedNodes: [makeNode('a'), makeNode('b')] }); diff --git a/packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/selection/CanvasSelectionToolbar.vue b/packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/selection/CanvasSelectionToolbar.vue index 92cf7791fa1..04d38b35631 100644 --- a/packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/selection/CanvasSelectionToolbar.vue +++ b/packages/frontend/editor-ui/src/features/workflows/canvas/components/elements/selection/CanvasSelectionToolbar.vue @@ -10,7 +10,6 @@ import { useVueFlowTransformPaneTeleport } from '../../../composables/useVueFlow import { useCanvasNodeGroupActions } from '../../../composables/useCanvasNodeGroupActions'; import { useSelectionValidation } from '@/app/composables/useSelectionValidation'; import { useIsNodeContextEnabled } from '@/features/ai/instanceAi/composables/useIsNodeContextEnabled'; -import { useSettingsStore } from '@n8n/stores/settings.store'; import type { BoundingBox } from '../../../canvas.types'; const TOOLBAR_OFFSET_PX = 12; @@ -35,9 +34,8 @@ const props = withDefaults( ); const i18n = useI18n(); -const settingsStore = useSettingsStore(); const { teleportTarget } = useVueFlowTransformPaneTeleport(); -const { isSelectionExtractable } = useSelectionValidation(); +const { isSelectionExtractable, isSubworkflowConversionDisabled } = useSelectionValidation(); const { canGroup, groupSelection } = useCanvasNodeGroupActions(() => props.selectedNodes, { readOnly: () => props.readOnly, }); @@ -54,7 +52,7 @@ const selectedNodeIds = computed(() => props.selectedNodes.map((node) => node.id const canExtractWorkflow = computed( () => !props.readOnly && - !settingsStore.isSubworkflowConversionDisabled && + !isSubworkflowConversionDisabled() && isSelectionExtractable(selectedNodeIds.value).valid, ); diff --git a/packages/frontend/editor-ui/src/features/workflows/components/WorkflowSettings/WorkflowSettings.test.ts b/packages/frontend/editor-ui/src/features/workflows/components/WorkflowSettings/WorkflowSettings.test.ts index ba5ddf6201a..5b3364a1e62 100644 --- a/packages/frontend/editor-ui/src/features/workflows/components/WorkflowSettings/WorkflowSettings.test.ts +++ b/packages/frontend/editor-ui/src/features/workflows/components/WorkflowSettings/WorkflowSettings.test.ts @@ -8,7 +8,8 @@ import { SYSTEM_RESOLVER_ID, type FrontendSettings } from '@n8n/api-types'; import { createComponentRenderer } from '@/__tests__/render'; import { createTestWorkflow } from '@/__tests__/mocks'; import { getDropdownItems, mockedStore, type MockedStore } from '@/__tests__/utils'; -import { EnterpriseEditionFeature } from '@/app/constants'; +import { EnterpriseEditionFeature, EXECUTE_WORKFLOW_NODE_TYPE } from '@/app/constants'; +import { useNodeTypesStore } from '@/app/stores/nodeTypes.store'; import { useRBACStore } from '@n8n/stores/rbac.store'; import WorkflowSettingsVue from '@/features/workflows/components/WorkflowSettings/WorkflowSettings.vue'; import { useWorkflowsStore } from '@/app/stores/workflows.store'; @@ -169,6 +170,7 @@ describe('WorkflowSettingsVue', () => { workflowsStore = mockedStore(useWorkflowsStore); workflowsListStore = mockedStore(useWorkflowsListStore); settingsStore = mockedStore(useSettingsStore); + vi.spyOn(useNodeTypesStore(), 'isNodeTypeUnavailable').mockReturnValue(false); sourceControlStore = mockedStore(useSourceControlStore); projectsStore = mockedStore(useProjectsStore); @@ -262,7 +264,9 @@ describe('WorkflowSettingsVue', () => { it('should lock caller policy to none when executeWorkflow is excluded', async () => { settingsStore.settings.enterprise[EnterpriseEditionFeature.Sharing] = true; - vi.spyOn(settingsStore, 'isExecuteWorkflowNodeExcluded', 'get').mockReturnValue(true); + vi.mocked(useNodeTypesStore().isNodeTypeUnavailable).mockImplementation( + (type) => type === EXECUTE_WORKFLOW_NODE_TYPE, + ); workflowDocumentStore.setSettings({ callerPolicy: 'workflowsFromAList', callerIds: 'abc', diff --git a/packages/frontend/editor-ui/src/features/workflows/components/WorkflowSettings/WorkflowSettings.vue b/packages/frontend/editor-ui/src/features/workflows/components/WorkflowSettings/WorkflowSettings.vue index 0def1266d2f..eb68c795869 100644 --- a/packages/frontend/editor-ui/src/features/workflows/components/WorkflowSettings/WorkflowSettings.vue +++ b/packages/frontend/editor-ui/src/features/workflows/components/WorkflowSettings/WorkflowSettings.vue @@ -11,7 +11,9 @@ import { WORKFLOW_SETTINGS_MODAL_KEY, NODE_CREATOR_OPEN_SOURCES, TIME_SAVED_NODE_TYPE, + EXECUTE_WORKFLOW_NODE_TYPE, } from '@/app/constants'; +import { useNodeTypesStore } from '@/app/stores/nodeTypes.store'; import { EXECUTION_LOGIC_V2_EXPERIMENT } from '@/app/constants/experiments'; import { @@ -93,6 +95,7 @@ const canUpdateCredentialResolver = hasPermission(['rbac'], { const rootStore = useRootStore(); const settingsStore = useSettingsStore(); +const nodeTypesStore = useNodeTypesStore(); const sourceControlStore = useSourceControlStore(); const collaborationStore = useCollaborationStore(); const workflowsStore = useWorkflowsStore(); @@ -913,7 +916,7 @@ onMounted(async () => { workflowSettingsData.callerPolicy = defaultValues.value .workflowCallerPolicy as WorkflowSettings.CallerPolicy; } - if (settingsStore.isExecuteWorkflowNodeExcluded) { + if (nodeTypesStore.isNodeTypeUnavailable(EXECUTE_WORKFLOW_NODE_TYPE)) { workflowSettingsData.callerPolicy = 'none'; } if (workflowSettingsData.executionTimeout === undefined) { @@ -1162,7 +1165,7 @@ onBeforeUnmount(() => { :disabled=" readOnlyEnv || !workflowPermissions.update || - settingsStore.isExecuteWorkflowNodeExcluded + nodeTypesStore.isNodeTypeUnavailable(EXECUTE_WORKFLOW_NODE_TYPE) " :placeholder="i18n.baseText('workflowSettings.selectOption')" filterable From c92f86ef71d68e7647bf33b5d75d0a71548d472d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Repe=C4=87?= Date: Tue, 29 Sep 2026 07:38:48 +0000 Subject: [PATCH 08/55] ci: Copy the full typescript-config package into the benchmark image (no-changelog) (#39833) --- packages/@n8n/benchmark/Dockerfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/@n8n/benchmark/Dockerfile b/packages/@n8n/benchmark/Dockerfile index 0f438370023..1439f67b813 100644 --- a/packages/@n8n/benchmark/Dockerfile +++ b/packages/@n8n/benchmark/Dockerfile @@ -46,9 +46,7 @@ ENV DOCKER_BUILD=true RUN pnpm install --frozen-lockfile # TS config files -COPY --chown=node:node ./packages/@n8n/typescript-config/tsconfig.common.json /app/packages/@n8n/typescript-config/tsconfig.common.json -COPY --chown=node:node ./packages/@n8n/typescript-config/tsconfig.build.json /app/packages/@n8n/typescript-config/tsconfig.build.json -COPY --chown=node:node ./packages/@n8n/typescript-config/tsconfig.backend.json /app/packages/@n8n/typescript-config/tsconfig.backend.json +COPY --chown=node:node ./packages/@n8n/typescript-config /app/packages/@n8n/typescript-config COPY --chown=node:node ./packages/@n8n/benchmark/tsconfig.json /app/packages/@n8n/benchmark/tsconfig.json COPY --chown=node:node ./packages/@n8n/benchmark/tsconfig.build.json /app/packages/@n8n/benchmark/tsconfig.build.json From a66221d24a3625e5ef0a1c0458b5b5b926860df3 Mon Sep 17 00:00:00 2001 From: oleg Date: Tue, 29 Sep 2026 07:43:03 +0000 Subject: [PATCH 09/55] feat(editor): Announce credentials while the n8n assistant builds workflows (no-changelog) (#39464) --- packages/@n8n/api-types/src/index.ts | 7 + ...ance-ai-setup-credential-selection.test.ts | 42 ++ .../instance-ai-setup-credential-selection.ts | 48 +++ .../src/schemas/instance-ai.schema.ts | 1 + .../instance-ai/docs/streaming-protocol.md | 22 +- .../skills/workflow-builder/SKILL.md | 24 +- .../instance-ai/src/agent/system-prompt.ts | 2 +- .../tools/__tests__/credentials.tool.test.ts | 144 ++++++- .../tools/__tests__/workflows.tool.test.ts | 76 +++- .../instance-ai/src/tools/credentials.tool.ts | 126 +++++- .../instance-ai/src/tools/workflows.tool.ts | 17 +- .../__tests__/build-workflow.tool.test.ts | 156 ++++++- .../__tests__/prepare-workflow-setup.test.ts | 270 ++++++++++++ .../setup-credential-selections.test.ts | 356 ++++++++++++++++ .../workflows/__tests__/setup-items.test.ts | 40 ++ .../tools/workflows/build-workflow.tool.ts | 61 ++- .../tools/workflows/prepare-workflow-setup.ts | 172 ++++++++ .../tools/workflows/resolve-credentials.ts | 12 +- .../workflows/setup-credential-selections.ts | 145 +++++++ .../src/tools/workflows/setup-items.ts | 47 ++- .../tools/workflows/workflow-file-bindings.ts | 30 +- .../frontend/@n8n/i18n/src/locales/en.json | 3 + .../src/app/constants/localStorage.ts | 2 + .../__tests__/canvasPreview.utils.test.ts | 17 + .../__tests__/useSetupPanelActions.test.ts | 191 ++++++++- .../__tests__/useSetupPanelState.test.ts | 50 ++- .../ai/instanceAi/canvasPreview.utils.ts | 9 + .../setupPanel/InstanceAiSetupCredential.vue | 104 +++-- .../setupPanel/InstanceAiSetupPanel.vue | 368 +++++++++++------ .../InstanceAiSetupCredential.test.ts | 384 ++++++++++++++++-- .../InstanceAiSetupPanel.interactions.test.ts | 187 ++++++++- .../__tests__/InstanceAiSetupPanel.test.ts | 92 +++++ .../composables/useSetupPanelActions.ts | 238 ++++++++--- .../composables/useSetupPanelState.ts | 38 +- .../credentials/credentialSetupFields.test.ts | 126 ++++++ .../credentials/credentialSetupFields.ts | 123 ++++++ .../composables/useWorkflowSetupItems.test.ts | 99 +++-- .../composables/useWorkflowSetupItems.ts | 47 ++- 38 files changed, 3492 insertions(+), 384 deletions(-) create mode 100644 packages/@n8n/api-types/src/schemas/__tests__/instance-ai-setup-credential-selection.test.ts create mode 100644 packages/@n8n/api-types/src/schemas/instance-ai-setup-credential-selection.ts create mode 100644 packages/@n8n/instance-ai/src/tools/workflows/__tests__/prepare-workflow-setup.test.ts create mode 100644 packages/@n8n/instance-ai/src/tools/workflows/__tests__/setup-credential-selections.test.ts create mode 100644 packages/@n8n/instance-ai/src/tools/workflows/prepare-workflow-setup.ts create mode 100644 packages/@n8n/instance-ai/src/tools/workflows/setup-credential-selections.ts create mode 100644 packages/frontend/editor-ui/src/features/credentials/credentialSetupFields.test.ts create mode 100644 packages/frontend/editor-ui/src/features/credentials/credentialSetupFields.ts diff --git a/packages/@n8n/api-types/src/index.ts b/packages/@n8n/api-types/src/index.ts index 7237d8b19a2..76558792c89 100644 --- a/packages/@n8n/api-types/src/index.ts +++ b/packages/@n8n/api-types/src/index.ts @@ -708,6 +708,13 @@ export type { InstanceAiEvalSeedFolder, } from './schemas/instance-ai.schema'; +export { + instanceAiSetupCredentialSelectionKey, + instanceAiSetupCredentialAppliedKey, + readPendingInstanceAiSetupCredentialSelections, + type InstanceAiSetupCredentialSelection, +} from './schemas/instance-ai-setup-credential-selection'; + export type { McpRegistryServerStatus, McpRegistryServerIconResponse, diff --git a/packages/@n8n/api-types/src/schemas/__tests__/instance-ai-setup-credential-selection.test.ts b/packages/@n8n/api-types/src/schemas/__tests__/instance-ai-setup-credential-selection.test.ts new file mode 100644 index 00000000000..0c7c2a35cf2 --- /dev/null +++ b/packages/@n8n/api-types/src/schemas/__tests__/instance-ai-setup-credential-selection.test.ts @@ -0,0 +1,42 @@ +import { + instanceAiSetupCredentialAppliedKey, + instanceAiSetupCredentialSelectionKey, + readPendingInstanceAiSetupCredentialSelections, + type InstanceAiSetupCredentialSelection, +} from '../instance-ai-setup-credential-selection'; + +const selection: InstanceAiSetupCredentialSelection = { + selectionId: 'choice-1', + credentialType: 'slackApi', + credentialId: 'credential-1', +}; +const itemId = 'workflow-1:credential:slackApi'; + +describe('readPendingInstanceAiSetupCredentialSelections', () => { + it('reads only valid selections for the requested workflow', () => { + expect( + readPendingInstanceAiSetupCredentialSelections( + { + [instanceAiSetupCredentialSelectionKey(itemId)]: selection, + [instanceAiSetupCredentialSelectionKey('workflow-2:credential:slackApi')]: selection, + [instanceAiSetupCredentialSelectionKey('workflow-1:credential:gmailOAuth2')]: selection, + [instanceAiSetupCredentialSelectionKey(`${itemId}:invalid`)]: { credentialId: 'id' }, + }, + 'workflow-1', + ), + ).toEqual([{ itemId, selection }]); + }); + + it('keeps a newer choice pending when an earlier save finishes', () => { + const metadata = { + [instanceAiSetupCredentialSelectionKey(itemId)]: selection, + [instanceAiSetupCredentialAppliedKey(itemId, 'older-choice')]: true, + }; + expect(readPendingInstanceAiSetupCredentialSelections(metadata, 'workflow-1')).toEqual([ + { itemId, selection }, + ]); + metadata[instanceAiSetupCredentialAppliedKey(itemId, selection.selectionId)] = true; + metadata[instanceAiSetupCredentialAppliedKey(itemId, 'older-choice')] = true; + expect(readPendingInstanceAiSetupCredentialSelections(metadata, 'workflow-1')).toEqual([]); + }); +}); diff --git a/packages/@n8n/api-types/src/schemas/instance-ai-setup-credential-selection.ts b/packages/@n8n/api-types/src/schemas/instance-ai-setup-credential-selection.ts new file mode 100644 index 00000000000..fb5229e0c32 --- /dev/null +++ b/packages/@n8n/api-types/src/schemas/instance-ai-setup-credential-selection.ts @@ -0,0 +1,48 @@ +import { z } from 'zod'; + +const instanceAiSetupCredentialSelectionSchema = z.object({ + selectionId: z.string().min(1), + credentialType: z.string().min(1), + credentialId: z.string().min(1), + nodeNames: z.array(z.string().min(1)).min(1).optional(), +}); + +export type InstanceAiSetupCredentialSelection = z.infer< + typeof instanceAiSetupCredentialSelectionSchema +>; + +const SELECTION_KEY_PREFIX = 'instanceAiSetupCredentialSelection:'; + +export function instanceAiSetupCredentialSelectionKey(itemId: string): string { + return `${SELECTION_KEY_PREFIX}${itemId}`; +} + +/** A marker for each choice keeps reordered saves independent. */ +export function instanceAiSetupCredentialAppliedKey(itemId: string, selectionId: string): string { + return `instanceAiSetupCredentialApplied:${itemId}:${selectionId}`; +} + +export function readPendingInstanceAiSetupCredentialSelections( + metadata: Record | undefined, + workflowId: string, +): Array<{ itemId: string; selection: InstanceAiSetupCredentialSelection }> { + const pending: Array<{ itemId: string; selection: InstanceAiSetupCredentialSelection }> = []; + const workflowPrefix = `${workflowId}:credential:`; + for (const [key, value] of Object.entries(metadata ?? {})) { + if (!key.startsWith(`${SELECTION_KEY_PREFIX}${workflowPrefix}`)) continue; + const parsed = instanceAiSetupCredentialSelectionSchema.safeParse(value); + if (!parsed.success) continue; + const itemId = key.slice(SELECTION_KEY_PREFIX.length); + const typePrefix = `${workflowPrefix}${parsed.data.credentialType}`; + if (itemId !== typePrefix && !itemId.startsWith(`${typePrefix}:`)) continue; + if (metadata?.[instanceAiSetupCredentialAppliedKey(itemId, parsed.data.selectionId)] === true) { + continue; + } + pending.push({ itemId, selection: parsed.data }); + } + // Apply node-specific choices after choices for the whole service. + return pending.sort( + (a, b) => + Number(a.selection.nodeNames !== undefined) - Number(b.selection.nodeNames !== undefined), + ); +} diff --git a/packages/@n8n/api-types/src/schemas/instance-ai.schema.ts b/packages/@n8n/api-types/src/schemas/instance-ai.schema.ts index 7fe9b4f5a19..60eb17212d0 100644 --- a/packages/@n8n/api-types/src/schemas/instance-ai.schema.ts +++ b/packages/@n8n/api-types/src/schemas/instance-ai.schema.ts @@ -1174,6 +1174,7 @@ export const setupItemSchema = z.discriminatedUnion('kind', [ kind: z.literal('credential'), credentialType: z.string(), appDisplayName: z.string().optional(), + preferNew: z.boolean().optional(), nodeBindings: z.array(z.object({ nodeName: z.string() })).optional(), setupHint: credentialSetupHintSchema.optional(), /** Why the app is needed, e.g. "for the docs search". */ diff --git a/packages/@n8n/instance-ai/docs/streaming-protocol.md b/packages/@n8n/instance-ai/docs/streaming-protocol.md index f063e8ec543..64d7ecf25b2 100644 --- a/packages/@n8n/instance-ai/docs/streaming-protocol.md +++ b/packages/@n8n/instance-ai/docs/streaming-protocol.md @@ -344,8 +344,26 @@ persisted snapshots at run start and drops a snapshot whose content did not change, so a recomputed, unchanged list publishes nothing. Snapshot reads wait for pending events to drain. The final workflow setup handoff confirms the stored snapshot before it saves the setup routing marker. A failed handoff returns an -error instead of `announced: true`. Credential replacement requests and existing -setup cards keep their selection and resume flows. +error instead of `announced: true`. Requests to replace a bound account and +existing setup cards keep their selection and resume flows. A new-account request +for an unconnected service stays in the panel. Choices saved during the current +build satisfy repeated new-account flags at the final handoff. + +Before generating source, the agent can call `credentials(action="setup")` +with `filePath`, `workflowName`, and known service credential types. This creates +a temporary workflow in the bound project and persists its source-file binding. +The tool confirms the stored checklist and returns `preBuild: true` without +suspending. Later calls replace the planned requirements until the first build. +New-account preferences keep those rows unselected. A saved user choice satisfies +the preference for the rest of that run, including later build repairs. +The build saves into the same workflow and replaces the checklist with its +actual requirements. + +The panel saves pending credential references through the existing thread +metadata endpoint. Each choice has its own completion marker. The builder +validates choices against project-scoped credentials before automatic selection. +The panel applies choices made later when the build ends. Refresh and artifact +switches retain pending choices. Removing a requirement never deletes a credential. The agent observes saved workflows at the start of a user turn. This read updates its private open-item memo. It does not publish a snapshot or select a workflow. diff --git a/packages/@n8n/instance-ai/skills/workflow-builder/SKILL.md b/packages/@n8n/instance-ai/skills/workflow-builder/SKILL.md index dc28308b595..57103f30dd4 100644 --- a/packages/@n8n/instance-ai/skills/workflow-builder/SKILL.md +++ b/packages/@n8n/instance-ai/skills/workflow-builder/SKILL.md @@ -59,6 +59,20 @@ first action turn (each extra sequential turn resends the whole context). When unsure which nodes to use, load this skill first and follow its research process below. +## Early service connections + +When the `credentials(action="setup")` schema offers `filePath`, announce +known service credentials immediately after the first node definitions or +credential type search. Do this before detailed planning, SDK research, or +source generation. Do not wait for every service to be resolved. + +Pick the source `filePath` and call credential setup with the complete list +of types known so far. Make it the only tool call in that response. Wait for +its successful `preBuild: true` result before continuing. Add later discoveries +with another complete list. Do not batch setup with source writing. + +When early setup is unavailable, keep the post-build setup flow below. + ## Repair Strategy When the edit is to fix a node the user reports as erroring or showing a red @@ -97,10 +111,12 @@ resources, credentials, channel IDs, or timezone; use placeholders or unresolved for a capability the user did not name, discover coverage first and use a Gateway credits–covered node instead of asking when the user has no credential for a comparable tool (see Gateway credits -Preference). Setup details — recipients, accounts, -resources, channels, credentials, timezone — belong in placeholders or -unresolved `newCredential()` calls until post-build setup. After the first -build, use `ask-user` when stuck or genuinely ambiguous; do not retry the same +Preference). Setup details such as recipients, accounts, resources, +channels, credentials, and timezone belong in placeholders or +unresolved `newCredential()` calls. Announce known credentials through early +setup when available, then continue building without waiting for the user. +After the first build, resolve remaining setup and use `ask-user` when stuck +or when choices are ambiguous; do not retry the same failing approach more than twice. Never re-ask an answered, deferred, or skipped question. A skip grants no additional permission. Choose defaults only for unspecified details within the requested task. If a skipped question seeks diff --git a/packages/@n8n/instance-ai/src/agent/system-prompt.ts b/packages/@n8n/instance-ai/src/agent/system-prompt.ts index e9d6970ee37..cec1a883c3f 100644 --- a/packages/@n8n/instance-ai/src/agent/system-prompt.ts +++ b/packages/@n8n/instance-ai/src/agent/system-prompt.ts @@ -203,7 +203,7 @@ If the user asks for a blocked operation, explain that the instance is in read-o */ function getCredentialSetupBullet(setupPanelEnabled?: boolean): string { if (setupPanelEnabled) { - return '**Credential setup** uses `workflows(action="setup")` when a workflowId is available. Requirements can appear in the setup panel while the workflow is being built. The user can complete them immediately. Do not describe setup as happening only after the build. When the result has `announced: true`, the setup panel lists the remaining credentials and parameters. Summarize that result, report any validation warnings, and end your turn. Other results need their returned guidance: correct validation errors, respect denials and skipped items, and wait for requested destination approvals. Explicit credential replacement and an already-open setup card keep their card flow, including apply and test-trigger results. Do not treat a resumed card as a panel announcement. Each new user turn carries a `` block with current configuration; trust it over older tool results. Configuration alone does not prove successful testing. Use `credentials(action="setup")` when the user explicitly asks to create a credential outside of any workflow context. Never call both tools for the same workflow. Never describe workflow setup as something the user starts from the canvas or editor, and never ask the user to paste secrets into chat.'; + return '**Early credential setup**: announce each explicitly requested service as soon as its exact credential type is known from node definitions or credential type search. Do this before detailed workflow planning, SDK research, or reasoning about implementation. Do not wait to identify every service. Call `credentials(action="setup", filePath, workflowName, credentials)` as the only tool call in that response. Wait for its successful `preBuild: true` result before planning the implementation or generating source. Never batch this call with workspace writes or build-workflow. Pick the source filePath now and reuse it throughout the build. Include workflowId for an existing workflow and folderPath when creating in a known folder. This creates the workflow context and makes setup actionable immediately. Use the announced credential types when configuring matching nodes. Announce only supported service-specific types; leave ambiguous services and generic authentication to build-time discovery. Pass the complete known requirement list again if the plan changes, including an empty list if all requirements are dropped. Do not wait for the user to connect. The early result has `preBuild: true`: continue generating source, omit folderPath from build-workflow, and do not end your turn. This early step overrides runtime skill guidance that defers all setup until after building. After the build, **credential setup** uses `workflows(action="setup")` when a workflowId is available. Requirements can appear in the setup panel while the workflow is being built. The user can complete them immediately. Do not describe setup as happening only after the build. When the result has `announced: true` without `preBuild: true`, the setup panel lists the remaining credentials and parameters. Summarize that result, report any validation warnings, and end your turn. Other results need their returned guidance: correct validation errors, respect denials and skipped items, and wait for requested destination approvals. Explicit credential replacement and an already-open setup card keep their card flow, including apply and test-trigger results. Do not treat a resumed card as a panel announcement. Each new user turn carries a `` block with current configuration; trust it over older tool results. Configuration alone does not prove successful testing. Use `credentials(action="setup")` without filePath when the user explicitly asks to create a credential outside of any workflow context. Early credential announcements and final workflow setup are separate stages; do not repeat either without changed requirements. Never describe workflow setup as something the user starts from the canvas or editor, and never ask the user to paste secrets into chat.'; } return '**Credential setup** uses `workflows(action="setup")` when a workflowId is available — it opens the inline setup card in the n8n Assistant panel and handles credentials, parameters, and triggers in one step. Use `credentials(action="setup")` only when the user explicitly asks to create a credential outside of any workflow context. Never call both tools for the same workflow. Never describe workflow setup as something the user starts from the canvas or editor. Setup cards are only open while the setup call is pending — once it returns a result, the card is resolved: describe the outcome (e.g. credentials selected and ready), never that a card is open or that the user still needs to authorize. When a node in `nodesStillNeedingSetup` carries `parameterIssues`, the connected credential can\'t reach the value that was configured (e.g. a model outside what the credential allows) — fix the value, then tell the user plainly which value didn\'t work and what you set instead. Never silently swap a model or other parameter without saying so. Nodes listed under `skippedByUser` are different: the user chose to skip them, so never re-open the setup card for those — say what stays unconfigured and offer to set it up later.'; } diff --git a/packages/@n8n/instance-ai/src/tools/__tests__/credentials.tool.test.ts b/packages/@n8n/instance-ai/src/tools/__tests__/credentials.tool.test.ts index 27f2b1bf0df..1edf888a63e 100644 --- a/packages/@n8n/instance-ai/src/tools/__tests__/credentials.tool.test.ts +++ b/packages/@n8n/instance-ai/src/tools/__tests__/credentials.tool.test.ts @@ -6,6 +6,7 @@ import type { Mock } from 'vitest'; import { executeTool } from '../../__tests__/tool-test-utils'; import type { InstanceAiContext, CredentialSummary, CredentialDetail } from '../../types'; import { createCredentialsTool, type CredentialAction } from '../credentials.tool'; +import { prepareWorkflowSetup } from '../workflows/prepare-workflow-setup'; import type { SetupRequest } from '../workflows/setup-workflow.schema'; import { analyzeWorkflow } from '../workflows/setup-workflow.service'; @@ -14,6 +15,8 @@ vi.mock('../workflows/setup-workflow.service', async (importOriginal) => ({ analyzeWorkflow: vi.fn(async () => await Promise.resolve([])), })); +vi.mock('../workflows/prepare-workflow-setup', () => ({ prepareWorkflowSetup: vi.fn() })); + // ── Helpers ────────────────────────────────────────────────────────────────── function createMockContext( @@ -954,6 +957,7 @@ describe('credentials tool', () => { describe('setup action — setup panel announcement', () => { beforeEach(() => { vi.mocked(analyzeWorkflow).mockReset().mockResolvedValue([]); + vi.mocked(prepareWorkflowSetup).mockReset(); }); function panelContext(overrides: Parameters[0] = {}) { @@ -983,6 +987,105 @@ describe('credentials tool', () => { ).toBe(true); }); + it('exposes early setup fields only when the panel is enabled', () => { + const control = inputJsonSchema(createCredentialsTool(createMockContext())); + const { context } = panelContext(); + const panel = inputJsonSchema(createCredentialsTool(context)); + const withFolders = inputJsonSchema( + createCredentialsTool({ + ...context, + folderExplorationEnabled: true, + }), + ); + + expect(control.properties).not.toHaveProperty('filePath'); + expect(control.properties).not.toHaveProperty('workflowName'); + expect(control.properties).not.toHaveProperty('folderPath'); + expect(panel.properties).toHaveProperty('filePath'); + expect(panel.properties).toHaveProperty('workflowName'); + expect(panel.properties).not.toHaveProperty('folderPath'); + expect(withFolders.properties).toHaveProperty('folderPath'); + }); + + it.each([ + { credentials: [{ credentialType: 'slackApi', preferNew: true }] }, + { credentials: [] }, + ])( + 'prepares source-bound setup without suspending for requirements %j', + async ({ credentials }) => { + const { context, emitter } = panelContext({ folderExplorationEnabled: true }); + const suspend = vi.fn(); + const prepared = { + success: true, + announced: true, + preBuild: true, + workflowId: 'wf-early', + filePath: 'src/workflows/main.ts', + message: 'Continue the build', + }; + vi.mocked(prepareWorkflowSetup).mockResolvedValue(prepared); + const input = { + action: 'setup', + filePath: prepared.filePath, + workflowName: 'Daily update', + folderPath: 'Notifications', + credentials, + }; + const tool = createCredentialsTool(context); + + expect(getInputSchema(tool).safeParse(input).success).toBe(true); + const result = await executeTool(tool, input, suspendCtx(suspend)); + + expect(result).toEqual(prepared); + expect(prepareWorkflowSetup).toHaveBeenCalledWith(context, input); + expect(suspend).not.toHaveBeenCalled(); + expect(emitter.merge).not.toHaveBeenCalled(); + }, + ); + + it('does not report early setup as announced when persistence fails', async () => { + const { context } = panelContext(); + const suspend = vi.fn(); + vi.mocked(prepareWorkflowSetup).mockRejectedValue(new Error('Could not persist setup')); + + const result = await executeTool( + createCredentialsTool(context), + { + action: 'setup', + filePath: 'src/workflows/main.ts', + workflowName: 'Daily update', + credentials: [{ credentialType: 'slackApi' }], + }, + suspendCtx(suspend), + ); + + expect(result).toEqual({ + success: false, + announced: false, + message: 'Could not persist setup', + }); + expect(suspend).not.toHaveBeenCalled(); + }); + + it('validates registered credential types before creating early workflow context', async () => { + const { context } = panelContext(); + context.credentialService.credentialTypeExists = vi.fn().mockResolvedValue(false); + + const result = await executeTool( + createCredentialsTool(context), + { + action: 'setup', + filePath: 'src/workflows/main.ts', + workflowName: 'Daily update', + credentials: [{ credentialType: 'slackApiTypo' }], + }, + suspendCtx(), + ); + + expect(result).toMatchObject({ error: 'unknown_credential_type' }); + expect(prepareWorkflowSetup).not.toHaveBeenCalled(); + }); + it('should announce the credentials to the setup panel and return without suspending', async () => { const { context, emitter } = panelContext(); (context.credentialService.list as Mock).mockResolvedValue([ @@ -1167,8 +1270,28 @@ describe('credentials tool', () => { expect(emitter.merge).not.toHaveBeenCalled(); }); - it('should keep the card when an entry asks for a new credential', async () => { + it.each(['bound', 'unreadable'])('keeps the card for %s workflows', async (state) => { const { context, emitter } = panelContext(); + vi.mocked(analyzeWorkflow).mockResolvedValue([ + { + node: { + name: 'Slack', + type: 'n8n-nodes-base.slack', + typeVersion: 2, + id: 'slack-node', + position: [0, 0], + parameters: {}, + credentials: { slackApi: { id: 'old-account', name: 'Old account' } }, + }, + credentialType: 'slackApi', + needsAction: false, + credentialNeedsAction: false, + isTrigger: false, + }, + ]); + if (state === 'unreadable') { + vi.mocked(analyzeWorkflow).mockRejectedValue(new Error('workflow unavailable')); + } const suspendFn = vi.fn(); await executeTool( @@ -1192,6 +1315,25 @@ describe('credentials tool', () => { ); }); + it('announces a first account requested through the credential tool without a card', async () => { + const { context, emitter } = panelContext(); + const suspend = vi.fn(); + const result = await executeTool( + createCredentialsTool(context), + { + action: 'setup', + workflowId: 'wf-1', + credentials: [{ credentialType: 'slackApi', preferNew: true }], + }, + suspendCtx(suspend), + ); + expect(result).toMatchObject({ success: true, announced: true }); + expect(suspend).not.toHaveBeenCalled(); + expect(emitter.merge).toHaveBeenCalledWith('wf-1', [ + expect.objectContaining({ credentialType: 'slackApi', preferNew: true }), + ]); + }); + it('should still announce when the emitter fails', async () => { const { context, emitter } = panelContext(); emitter.merge.mockImplementation(() => { diff --git a/packages/@n8n/instance-ai/src/tools/__tests__/workflows.tool.test.ts b/packages/@n8n/instance-ai/src/tools/__tests__/workflows.tool.test.ts index 9202eb42c4f..104784b8842 100644 --- a/packages/@n8n/instance-ai/src/tools/__tests__/workflows.tool.test.ts +++ b/packages/@n8n/instance-ai/src/tools/__tests__/workflows.tool.test.ts @@ -3904,7 +3904,14 @@ describe('workflows(action="setup") — setup panel', () => { }); it('keeps explicit credential replacement in the selection card', async () => { - const replacement = { ...openSlack, preferNewCredential: true }; + const replacement = { + ...openSlack, + node: { + ...openSlack.node, + credentials: { slackApi: { id: 'old-account', name: 'Old account' } }, + }, + preferNewCredential: true, + }; (analyzeWorkflow as Mock).mockResolvedValue([replacement, boundGmail]); const { context, emitter, markWorkflowSetupHandled } = panelContext(); const suspend = vi.fn(); @@ -3920,6 +3927,73 @@ describe('workflows(action="setup") — setup panel', () => { expect(markWorkflowSetupHandled).not.toHaveBeenCalled(); }); + it('announces unbound new-account requirements without opening a setup card', async () => { + (analyzeWorkflow as Mock).mockResolvedValue([ + { ...openSlack, preferNewCredential: true }, + boundGmail, + sheetParams, + ]); + const { context, emitter, markWorkflowSetupHandled } = panelContext(); + const suspend = vi.fn(); + const result = await executeTool( + createWorkflowsTool(context, 'full'), + { action: 'setup', workflowId: 'wf1', preferNewCredentials: ['slackApi'] }, + { suspend, resumeData: undefined } as never, + ); + expect(result).toMatchObject({ success: true, announced: true }); + expect(suspend).not.toHaveBeenCalled(); + expect(emitter.announce).toHaveBeenCalledWith( + 'wf1', + expect.arrayContaining([ + expect.objectContaining({ credentialType: 'slackApi', preferNew: true }), + expect.objectContaining({ credentialType: 'gmailOAuth2' }), + expect.objectContaining({ parameterNames: ['documentId'] }), + ]), + ); + expect(markWorkflowSetupHandled).toHaveBeenCalledWith('wf1'); + }); + + it('does not reopen an account selected during the current build', async () => { + (analyzeWorkflow as Mock).mockResolvedValue([boundGmail]); + const { context } = panelContext({ + runId: 'run-early', + threadId: 'thread-early', + threadMemory: { + getThread: vi.fn().mockResolvedValue({ + id: 'thread-early', + resourceId: 'user-1', + createdAt: new Date(), + updatedAt: new Date(), + metadata: { + instanceAiWorkflowSourceFiles: { + 'src/workflows/main.ts': { + filePath: 'src/workflows/main.ts', + workflowId: 'wf1', + setupPreferences: { + runId: 'run-early', + satisfiedCredentialTypes: ['gmailOAuth2'], + preferNewCredentialTypes: [], + }, + }, + }, + }, + }), + saveThread: vi.fn().mockResolvedValue(undefined), + }, + }); + const suspend = vi.fn(); + const result = await executeTool( + createWorkflowsTool(context, 'full'), + { action: 'setup', workflowId: 'wf1', preferNewCredentials: ['gmailOAuth2'] }, + { suspend, resumeData: undefined } as never, + ); + expect(result).toMatchObject({ success: true, announced: true, open: [] }); + expect(suspend).not.toHaveBeenCalled(); + expect(analyzeWorkflow).toHaveBeenCalledWith(context, 'wf1', undefined, { + includeSettled: true, + }); + }); + it('keeps failed connection checks in the announcement', async () => { (analyzeWorkflow as Mock).mockResolvedValue([ { ...boundGmail, credentialTestResult: { success: false, message: 'Connection failed' } }, diff --git a/packages/@n8n/instance-ai/src/tools/credentials.tool.ts b/packages/@n8n/instance-ai/src/tools/credentials.tool.ts index cac68a5198e..c43ec0eddc2 100644 --- a/packages/@n8n/instance-ai/src/tools/credentials.tool.ts +++ b/packages/@n8n/instance-ai/src/tools/credentials.tool.ts @@ -24,10 +24,13 @@ import { GENERIC_AUTH_CREDENTIAL_TYPES, N8N_CONNECT_DISPLAY_NAME, } from './workflows/credential-utils'; +import { prepareWorkflowSetup } from './workflows/prepare-workflow-setup'; +import { filterSatisfiedSetupCredentialTypes } from './workflows/setup-credential-selections'; import { buildSetupItemsFromCredentialRequests, buildSetupItemsFromAnnouncement, isSetupPanelEnabled, + requestsCredentialReplacement, } from './workflows/setup-items'; import { analyzeWorkflow } from './workflows/setup-workflow.service'; @@ -435,6 +438,33 @@ const setupAction = z.object({ ), }); +const earlySetupAction = setupAction.extend({ + action: z + .literal('setup') + .describe( + 'Announce workflow credential requirements without waiting by passing filePath before generating source. Omit filePath for existing workflow or standalone setup. Follow the returned guidance: announced means continue working, while a resumed card reports the completed interaction.', + ), + filePath: z + .string() + .optional() + .describe( + 'Before writing workflow source, announce known service credentials with its planned workspace filePath. This creates or reuses the workflow context and returns without waiting for setup. Pass the complete current requirement list, including an empty list when a plan drops all services. Omit for standalone credential setup.', + ), + workflowName: z + .string() + .optional() + .describe('Name of the new workflow, required on its first early setup call.'), +}); + +const earlySetupActionWithFolder = earlySetupAction.extend({ + folderPath: z + .string() + .optional() + .describe( + 'Folder for the new workflow. Pass the intended folder here before building; omit it on the later build-workflow call.', + ), +}); + const testAction = z.object({ action: z .literal('test') @@ -485,9 +515,16 @@ function getCredentialActions(options: CredentialsToolOptions): CredentialAction return CREDENTIAL_ACTION_ORDER.filter((action) => allowedActions.has(action)); } -function createCredentialInputSchema(actions: readonly CredentialAction[]) { - const actionSchemas: CredentialActionSchema[] = actions.map( - (action) => CREDENTIAL_ACTION_SCHEMAS[action], +function createCredentialInputSchema( + actions: readonly CredentialAction[], + context: InstanceAiContext, +) { + const actionSchemas: CredentialActionSchema[] = actions.map((action) => + action === 'setup' && isSetupPanelEnabled(context) + ? context.folderExplorationEnabled + ? earlySetupActionWithFolder + : earlySetupAction + : CREDENTIAL_ACTION_SCHEMAS[action], ); if (actionSchemas.length === 0) { @@ -515,11 +552,11 @@ type Input = | z.infer | z.infer | z.infer - | z.infer + | z.infer | z.infer; -function buildInputSchema(options: CredentialsToolOptions) { - return createCredentialInputSchema(getCredentialActions(options)); +function buildInputSchema(options: CredentialsToolOptions, context: InstanceAiContext) { + return createCredentialInputSchema(getCredentialActions(options), context); } function formatActionList(actions: readonly CredentialAction[]): string { @@ -764,6 +801,7 @@ async function announceSetupItems( context: InstanceAiContext & { setupItemsEmitter: SetupItemsEmitter }, input: Extract, workflowId: string, + analyzedRequests?: Awaited>, ) { const credentials = input.credentials ?? []; // Best-effort, like the build's emission: the panel is a side channel. @@ -771,9 +809,11 @@ async function announceSetupItems( // Announce against the saved workflow's analysis so generic auth types // land on their per-node rows and the snapshot stays whole. A merge, not // a replace: earlier announcements may list types no saved node uses yet. - const analyzed = await analyzeWorkflow(context, workflowId, undefined, { - includeSettled: true, - }); + const analyzed = + analyzedRequests ?? + (await analyzeWorkflow(context, workflowId, undefined, { + includeSettled: true, + })); context.setupItemsEmitter.merge( workflowId, buildSetupItemsFromAnnouncement(workflowId, credentials, analyzed), @@ -834,7 +874,7 @@ async function handleSetup( const resumeData = ctx.resumeData; const isFinalize = input.credentialFlow?.stage === 'finalize'; - if (!input.credentials || input.credentials.length === 0) { + if (!input.credentials || (input.credentials.length === 0 && !input.filePath)) { return { error: 'missing_credentials', message: @@ -882,21 +922,63 @@ async function handleSetup( problems: hintProblems, }; } + if (isSetupPanelEnabled(context) && input.filePath) { + if (isFinalize || input.requireUserSelection) { + return { + success: false, + message: + 'Use early setup for known service requirements. Explicit credential replacement keeps the existing setup flow.', + }; + } + try { + return await prepareWorkflowSetup(context, { ...input, filePath: input.filePath }); + } catch (error) { + return { + success: false, + announced: false, + message: error instanceof Error ? error.message : String(error), + }; + } + } - // Setup panel v2: a workflow's credential needs are announced to the - // persistent panel and the turn continues — no card, no suspension. The - // finalize stage, standalone setup (no workflow), and an explicit user - // choice — the picker (`requireUserSelection`) or a fresh credential - // (`preferNew`) — keep the card: the panel has no way to express "replace - // the bound one", so the next save would silently re-attach it. - const keepsCard = - isFinalize || - input.requireUserSelection === true || - input.credentials.some((req: { preferNew?: boolean }) => req.preferNew === true); + // Finalization and explicit selection keep their card. Workflow requirements + // use the panel unless the request replaces an account already bound there. + const keepsCard = isFinalize || input.requireUserSelection === true; if (isSetupPanelEnabled(context) && !keepsCard) { const panelWorkflowId = resolveSetupPanelWorkflowId(context, input); if (panelWorkflowId !== undefined) { - return await announceSetupItems(context, input, panelWorkflowId); + try { + const preferNewTypes = await filterSatisfiedSetupCredentialTypes( + context, + panelWorkflowId, + input.credentials + .filter((request) => request.preferNew) + .map((request) => request.credentialType), + ); + const analyzed = preferNewTypes?.length + ? await analyzeWorkflow(context, panelWorkflowId, undefined, { includeSettled: true }) + : undefined; + if (!analyzed || !requestsCredentialReplacement(analyzed, preferNewTypes)) { + return await announceSetupItems( + context, + { + ...input, + credentials: input.credentials.map((request) => + request.preferNew + ? { ...request, preferNew: preferNewTypes?.includes(request.credentialType) } + : request, + ), + }, + panelWorkflowId, + analyzed, + ); + } + } catch (error) { + context.logger.warn('Failed to check credential replacement; using setup card', { + workflowId: panelWorkflowId, + error: error instanceof Error ? error.message : String(error), + }); + } } } @@ -1124,7 +1206,7 @@ export function createCredentialsTool( context: InstanceAiContext, options: CredentialsToolOptions = {}, ) { - const inputSchema = buildInputSchema(options); + const inputSchema = buildInputSchema(options, context); return new Tool(CREDENTIALS_TOOL_ID) .description(getToolDescription(options, context.credentialDescriptionsEnabled === true)) diff --git a/packages/@n8n/instance-ai/src/tools/workflows.tool.ts b/packages/@n8n/instance-ai/src/tools/workflows.tool.ts index 52c1f76ba40..bc44aa13ea1 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows.tool.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows.tool.ts @@ -32,7 +32,8 @@ import { describeSavedPublishState, type SavedWorkflowState, } from './workflows/saved-workflow-state'; -import { isSetupPanelEnabled } from './workflows/setup-items'; +import { filterSatisfiedSetupCredentialTypes } from './workflows/setup-credential-selections'; +import { isSetupPanelEnabled, requestsCredentialReplacement } from './workflows/setup-items'; import { describeSetupItem, rememberWorkflowSetupState, @@ -1586,8 +1587,13 @@ async function handleSetup( // The setup panel lists bound slots too (rendered as done), so its snapshot // needs the settled requests the card logic below must not see. const setupPanelEnabled = isSetupPanelEnabled(context); + const preferNewCredentialTypes = await filterSatisfiedSetupCredentialTypes( + context, + input.workflowId, + input.preferNewCredentials, + ); const analyzedRequests = await analyzeWorkflow(context, input.workflowId, undefined, { - ...preferNewCredentialOptions(input), + ...(preferNewCredentialTypes?.length ? { preferNewCredentialTypes } : {}), ...(setupPanelEnabled ? { includeSettled: true } : {}), }); const allSetupRequests = setupPanelEnabled @@ -1735,8 +1741,11 @@ async function handleSetup( // Setup panel v2: announce the final checklist and return. The user // completes it in the panel; the turn ends with the agent's summary. - // Replacement needs an explicit selection. A saved binding already appears done in the panel. - if (isSetupPanelEnabled(context) && !input.preferNewCredentials?.length) { + // Only a bound account needs the explicit replacement card. + if ( + isSetupPanelEnabled(context) && + !requestsCredentialReplacement(analyzedRequests, preferNewCredentialTypes) + ) { return await announceWorkflowSetup(context, input.workflowId, analyzedRequests); } diff --git a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/build-workflow.tool.test.ts b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/build-workflow.tool.test.ts index 3cfcf70b1d2..9dc723e4897 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/build-workflow.tool.test.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/build-workflow.tool.test.ts @@ -1,4 +1,9 @@ import { isZodSchema } from '@n8n/agents'; +import { + instanceAiSetupCredentialAppliedKey, + instanceAiSetupCredentialSelectionKey, + type InstanceAiSetupCredentialSelection, +} from '@n8n/api-types'; import { executeTool } from '../../../__tests__/tool-test-utils'; import { FolderResolutionError } from '../../../errors/folder-resolution.error'; @@ -8,6 +13,7 @@ import { loadInstanceAiRuntimeSkillSource, loadInstanceAiRuntimeSkillSourceForBuildMode, } from '../../../skills/runtime-skills'; +import type { ThreadRecord } from '../../../storage/thread-patch'; import { emitTraceOnlyChildRun } from '../../../tracing/langsmith-tracing'; import type { InstanceAiContext } from '../../../types'; import type { WorkflowBuildOutcome } from '../../../workflow-loop/workflow-loop-state'; @@ -16,9 +22,10 @@ import { buildWorkflowInputSchema, createBuildWorkflowTool, } from '../build-workflow.tool'; +import { prepareWorkflowSetup } from '../prepare-workflow-setup'; import { buildCredentialMap, resolveCredentials } from '../resolve-credentials'; import type { SetupRequest } from '../setup-workflow.schema'; -import { analyzeWorkflow } from '../setup-workflow.service'; +import { analyzeWorkflow, getValidCredentialTypes } from '../setup-workflow.service'; import { getWorkflowSourceFileBinding, hashWorkflowSource } from '../workflow-file-bindings'; import { ensureWebhookIds } from '../workflow-json-utils'; import { compileWorkflowSource } from '../workflow-source-compiler'; @@ -89,6 +96,7 @@ vi.mock('../resolve-credentials', () => ({ vi.mock('../setup-workflow.service', () => ({ analyzeWorkflow: vi.fn(async () => await Promise.resolve([])), + getValidCredentialTypes: vi.fn(async () => new Set()), stripStaleCredentialsFromWorkflow: vi.fn(async () => await Promise.resolve()), })); @@ -1502,6 +1510,152 @@ describe('createBuildWorkflowTool', () => { ); }); + it('continues a stopped early setup draft with the same file and selected account', async () => { + const filePath = 'src/workflows/main.workflow.ts'; + const setupItemId = 'wf-early:credential:slackApi'; + const selection: InstanceAiSetupCredentialSelection = { + selectionId: 'selected-1', + credentialType: 'slackApi', + credentialId: 'selected-account', + }; + const appliedKey = instanceAiSetupCredentialAppliedKey(setupItemId, selection.selectionId); + let thread: ThreadRecord = { + id: 'thread-1', + resourceId: 'user-1', + createdAt: new Date(), + updatedAt: new Date(), + metadata: { + [instanceAiSetupCredentialSelectionKey(setupItemId)]: selection, + }, + }; + const { context } = makeContext({ + filePath, + overrides: { + threadId: thread.id, + runId: 'early-run', + threadMemory: { + getThread: vi.fn(async () => thread), + saveThread: vi.fn(async (updated: ThreadRecord) => (thread = updated)), + }, + setupItemsEmitter: { + emit: vi.fn(() => true), + announce: vi.fn().mockResolvedValue(undefined), + merge: vi.fn(() => true), + lastWorkflowId: vi.fn(), + workflowIds: vi.fn(() => []), + }, + }, + }); + let isTemporary = true; + let isArchived = false; + vi.mocked(context.workflowService.clearAiTemporary).mockImplementation(async () => { + isTemporary = false; + }); + context.workflowService.archiveIfAiTemporary = vi.fn(async () => { + isArchived = isTemporary; + return isArchived; + }); + vi.mocked(context.workflowService.createFromWorkflowJSON).mockResolvedValue( + await context.workflowService.get('wf-early'), + ); + const requirements = [{ credentialType: 'slackApi', preferNew: true }]; + await prepareWorkflowSetup(context, { + filePath, + workflowName: 'Slack updates', + credentials: requirements, + }); + await context.workflowService.archiveIfAiTemporary('wf-early'); + const resumedContext = { + ...context, + runId: 'continued-run', + aiCreatedWorkflowIds: new Set(), + }; + await prepareWorkflowSetup(resumedContext, { filePath, credentials: requirements }); + + vi.mocked(compileWorkflowSource).mockResolvedValueOnce({ + success: true, + compiler: 'sandbox-tsx', + warnings: [], + workflow: { + name: 'Slack updates', + connections: {}, + nodes: [ + { + id: 'slack-1', + name: 'Slack', + type: 'n8n-nodes-base.slack', + typeVersion: 2, + position: [0, 0], + credentials: { slackApi: { id: 'source-account', name: 'Source account' } }, + }, + ], + }, + }); + vi.mocked(buildCredentialMap).mockResolvedValueOnce( + new Map([ + [ + 'slackApi', + [ + { id: 'source-account', name: 'Source account', type: 'slackApi' }, + { id: 'selected-account', name: 'Selected account', type: 'slackApi' }, + ], + ], + ]), + ); + vi.mocked(getValidCredentialTypes).mockResolvedValue(new Set(['slackApi'])); + const actualResolver = + await vi.importActual('../resolve-credentials'); + vi.mocked(resolveCredentials).mockImplementationOnce(actualResolver.resolveCredentials); + vi.mocked(context.workflowService.updateFromWorkflowJSON).mockImplementationOnce( + async (workflowId) => { + if (isArchived) throw new Error('Cannot update an archived workflow.'); + expect(thread.metadata?.[appliedKey]).toBeUndefined(); + return { + id: workflowId, + name: 'Slack updates', + versionId: 'v-saved', + activeVersionId: null, + isArchived: false, + createdAt: '2026-01-01T00:00:00.000Z', + updatedAt: '2026-01-01T00:00:00.000Z', + nodes: [], + connections: {}, + checksum: 'checksum-saved', + }; + }, + ); + + const result = await executeTool(createBuildWorkflowTool(resumedContext), { + filePath, + preferNewCredentials: ['slackApi'], + }); + + expect(result).toMatchObject({ success: true, workflowId: 'wf-early' }); + expect(context.workflowService.createFromWorkflowJSON).toHaveBeenCalledTimes(1); + expect(context.workflowService.updateFromWorkflowJSON).toHaveBeenCalledWith( + 'wf-early', + expect.objectContaining({ + nodes: [ + expect.objectContaining({ + credentials: { slackApi: { id: 'selected-account', name: 'Selected account' } }, + }), + ], + }), + { expectedChecksum: 'checksum-current' }, + ); + expect(thread.metadata?.[appliedKey]).toBe(true); + await expect(getWorkflowSourceFileBinding(resumedContext, filePath)).resolves.toMatchObject({ + workflowId: 'wf-early', + workflowChecksum: 'checksum-saved', + setupPending: undefined, + setupPreferences: { + runId: 'continued-run', + satisfiedCredentialTypes: ['slackApi'], + preferNewCredentialTypes: [], + }, + }); + }); + it('updates a workflow created earlier in the run without requesting approval', async () => { const grantSessionToolApproval = vi.fn().mockResolvedValue(undefined); const { context, filePath } = makeContext({ diff --git a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/prepare-workflow-setup.test.ts b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/prepare-workflow-setup.test.ts new file mode 100644 index 00000000000..8340161813a --- /dev/null +++ b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/prepare-workflow-setup.test.ts @@ -0,0 +1,270 @@ +import type { InstanceAiSetupItem } from '@n8n/api-types'; +import { mock } from 'vitest-mock-extended'; + +import type { InstanceAiEventBus } from '../../../event-bus/event-bus.interface'; +import type { ThreadRecord } from '../../../storage/thread-patch'; +import type { InstanceAiContext } from '../../../types'; +import { prepareWorkflowSetup } from '../prepare-workflow-setup'; +import { createSetupItemsEmitter } from '../setup-items'; +import { analyzeWorkflow } from '../setup-workflow.service'; +import { getWorkflowSourceFileBinding } from '../workflow-file-bindings'; + +vi.mock('../setup-workflow.service', () => ({ analyzeWorkflow: vi.fn(async () => []) })); + +const filePath = 'src/workflows/slack.workflow.ts'; +const requirements = [{ credentialType: 'slackApi', reason: 'Send daily updates' }]; + +function createContext() { + let thread: ThreadRecord = { + id: 'thread-1', + resourceId: 'user-1', + metadata: { unrelated: 'retained' }, + createdAt: new Date(), + updatedAt: new Date(), + }; + const snapshots = new Map(); + const publish = vi.fn((_threadId, event) => { + if (event.type === 'setup-items') snapshots.set(event.payload.workflowId, event.payload.items); + }); + const threadMemory = { + patchThread: undefined, + getThread: vi.fn(async () => thread), + saveThread: vi.fn(async (updated: ThreadRecord) => (thread = updated)), + }; + const workflowService = mock({ + createFromWorkflowJSON: vi.fn().mockResolvedValue({ + id: 'wf-1', + versionId: 'version-1', + checksum: 'checksum-1', + }), + get: vi.fn().mockResolvedValue({ + id: 'wf-1', + versionId: 'version-2', + checksum: 'checksum-2', + }), + }); + const context = mock({ + threadId: thread.id, + runId: undefined, + workspaceRoot: undefined, + aiCreatedWorkflowIds: undefined, + permissions: mock(), + logger: mock(), + threadMemory, + workflowService, + setupItemsEmitter: createSetupItemsEmitter({ + eventBus: { publish }, + threadId: thread.id, + runId: 'run-1', + agentId: 'orchestrator', + readPersistedSnapshot: async (workflowId) => snapshots.get(workflowId), + }), + }); + return { context, threadMemory, workflowService, snapshots, publish }; +} + +describe('prepareWorkflowSetup', () => { + beforeEach(() => vi.clearAllMocks()); + + it('persists and promotes the draft before announcing its requirements', async () => { + const { context, workflowService, threadMemory, snapshots, publish } = createContext(); + + const result = await prepareWorkflowSetup(context, { + filePath: `./${filePath}`, + workflowName: 'Daily update', + folderPath: 'Notifications', + credentials: requirements, + }); + + expect(result).toMatchObject({ success: true, announced: true, workflowId: 'wf-1', filePath }); + expect(workflowService.createFromWorkflowJSON).toHaveBeenCalledWith( + { name: 'Daily update', nodes: [], connections: {} }, + { markAsAiTemporary: true, folderPath: 'Notifications' }, + ); + expect(threadMemory.saveThread.mock.invocationCallOrder[0]).toBeLessThan( + vi.mocked(workflowService.clearAiTemporary).mock.invocationCallOrder[0], + ); + expect(vi.mocked(workflowService.clearAiTemporary).mock.invocationCallOrder[0]).toBeLessThan( + publish.mock.invocationCallOrder[0], + ); + expect(await getWorkflowSourceFileBinding({ ...context }, filePath)).toMatchObject({ + workflowId: 'wf-1', + workflowVersionId: 'version-1', + setupPending: true, + }); + expect((await threadMemory.getThread()).metadata).toHaveProperty('unrelated', 'retained'); + expect(context.aiCreatedWorkflowIds).toContain('wf-1'); + expect(snapshots.get('wf-1')).toEqual([ + { id: 'wf-1:credential:slackApi', kind: 'credential', ...requirements[0] }, + ]); + expect(analyzeWorkflow).not.toHaveBeenCalled(); + }); + + it('reuses the persisted workflow after refresh and removes dropped requirements', async () => { + const { context, workflowService, snapshots } = createContext(); + await prepareWorkflowSetup(context, { + filePath, + workflowName: 'Daily update', + credentials: requirements, + }); + + const refreshedContext = { ...context }; + await prepareWorkflowSetup(refreshedContext, { + filePath, + credentials: [{ credentialType: 'gmailOAuth2' }], + }); + expect(snapshots.get('wf-1')).toEqual([ + { id: 'wf-1:credential:gmailOAuth2', kind: 'credential', credentialType: 'gmailOAuth2' }, + ]); + + await prepareWorkflowSetup(refreshedContext, { filePath, credentials: [] }); + expect(snapshots.get('wf-1')).toEqual([]); + expect(workflowService.createFromWorkflowJSON).toHaveBeenCalledTimes(1); + expect(workflowService.get).toHaveBeenCalledWith('wf-1'); + expect(await getWorkflowSourceFileBinding({ ...context }, filePath)).toMatchObject({ + workflowId: 'wf-1', + workflowVersionId: 'version-2', + setupPending: true, + }); + }); + + it('announces new-account requirements and retains them for the first build', async () => { + const { context, snapshots } = createContext(); + context.runId = 'run-early'; + await prepareWorkflowSetup(context, { + filePath, + workflowName: 'Daily update', + credentials: [ + { credentialType: 'slackApi', preferNew: true }, + { credentialType: 'gmailOAuth2' }, + ], + }); + expect(snapshots.get('wf-1')).toContainEqual({ + id: 'wf-1:credential:slackApi', + kind: 'credential', + credentialType: 'slackApi', + preferNew: true, + }); + await expect(getWorkflowSourceFileBinding(context, filePath)).resolves.toMatchObject({ + setupPreferences: { + runId: 'run-early', + satisfiedCredentialTypes: [], + preferNewCredentialTypes: ['slackApi'], + }, + }); + }); + + it('reuses one workflow for concurrent setup calls for the same source', async () => { + const { context, workflowService } = createContext(); + const input = { filePath, workflowName: 'Daily update', credentials: requirements }; + + const results = await Promise.all([ + prepareWorkflowSetup(context, input), + prepareWorkflowSetup(context, input), + ]); + + expect(results.map(({ workflowId }) => workflowId)).toEqual(['wf-1', 'wf-1']); + expect(workflowService.createFromWorkflowJSON).toHaveBeenCalledTimes(1); + }); + + it.each([ + { filePath: '../outside.ts', workflowName: 'Daily update', credentials: requirements }, + { filePath, workflowName: 'Daily update', credentials: [{ credentialType: 'httpHeaderAuth' }] }, + { filePath, workflowName: ' ', credentials: requirements }, + { filePath, workflowName: 'Daily update', credentials: [] }, + ])('rejects invalid early setup before creating a workflow: %j', async (input) => { + const { context, workflowService, publish } = createContext(); + + await expect(prepareWorkflowSetup(context, input)).rejects.toThrow(); + expect(workflowService.createFromWorkflowJSON).not.toHaveBeenCalled(); + expect(publish).not.toHaveBeenCalled(); + }); + + it('checks existing workflow access before announcing requirements', async () => { + const { context, workflowService, publish } = createContext(); + vi.mocked(workflowService.get).mockRejectedValue(new Error('Workflow is unavailable')); + + await expect( + prepareWorkflowSetup(context, { + filePath, + workflowId: 'wf-foreign', + credentials: requirements, + }), + ).rejects.toThrow('Workflow is unavailable'); + expect(workflowService.get).toHaveBeenCalledWith('wf-foreign'); + expect(publish).not.toHaveBeenCalled(); + }); + + it('announces a first account when adding a service to an existing workflow', async () => { + const { context, snapshots, workflowService } = createContext(); + await expect( + prepareWorkflowSetup(context, { + filePath, + workflowId: 'wf-1', + credentials: [{ credentialType: 'slackApi', preferNew: true }], + }), + ).resolves.toMatchObject({ success: true, announced: true, preBuild: true }); + expect(workflowService.createFromWorkflowJSON).not.toHaveBeenCalled(); + expect(snapshots.get('wf-1')).toContainEqual( + expect.objectContaining({ credentialType: 'slackApi', preferNew: true }), + ); + }); + + it('reuses the temporary workflow after its first binding write fails', async () => { + const { context, threadMemory, workflowService, publish } = createContext(); + threadMemory.saveThread.mockRejectedValueOnce(new Error('Storage unavailable')); + const input = { filePath, workflowName: 'Daily update', credentials: requirements }; + + await expect(prepareWorkflowSetup(context, input)).rejects.toThrow('Could not persist'); + expect(publish).not.toHaveBeenCalled(); + expect(workflowService.clearAiTemporary).not.toHaveBeenCalled(); + expect(context.aiCreatedWorkflowIds).toContain('wf-1'); + + await expect(prepareWorkflowSetup(context, input)).resolves.toMatchObject({ + success: true, + announced: true, + workflowId: 'wf-1', + }); + expect(workflowService.createFromWorkflowJSON).toHaveBeenCalledTimes(1); + expect(publish).toHaveBeenCalledTimes(1); + expect(threadMemory.saveThread.mock.invocationCallOrder[1]).toBeLessThan( + publish.mock.invocationCallOrder[0], + ); + await expect(getWorkflowSourceFileBinding({ ...context }, filePath)).resolves.toMatchObject({ + workflowId: 'wf-1', + setupPending: true, + }); + }); + + it('does not create a workflow when persisted source bindings cannot be read', async () => { + const { context, threadMemory, workflowService, publish } = createContext(); + threadMemory.getThread.mockRejectedValue(new Error('Storage unavailable')); + + await expect( + prepareWorkflowSetup(context, { + filePath, + workflowName: 'Daily update', + credentials: requirements, + }), + ).rejects.toThrow(); + expect(workflowService.createFromWorkflowJSON).not.toHaveBeenCalled(); + expect(publish).not.toHaveBeenCalled(); + }); + + it.each(['createWorkflow', 'updateWorkflow'] as const)( + 'respects a blocked %s permission', + async (permission) => { + const { context, workflowService } = createContext(); + context.permissions = mock({ [permission]: 'blocked' }); + + await expect( + prepareWorkflowSetup(context, { + filePath, + workflowName: 'Daily update', + credentials: requirements, + }), + ).rejects.toThrow('blocked by admin policy'); + expect(workflowService.createFromWorkflowJSON).not.toHaveBeenCalled(); + }, + ); +}); diff --git a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/setup-credential-selections.test.ts b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/setup-credential-selections.test.ts new file mode 100644 index 00000000000..f5e1d6476db --- /dev/null +++ b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/setup-credential-selections.test.ts @@ -0,0 +1,356 @@ +import { + AI_GATEWAY_MANAGED_TAG, + TEMPLATED_CUSTOM_AUTH_CREDENTIAL_TYPE, + instanceAiSetupCredentialAppliedKey, + instanceAiSetupCredentialSelectionKey, + readPendingInstanceAiSetupCredentialSelections, + type InstanceAiSetupCredentialSelection, +} from '@n8n/api-types'; +import type { NodeJSON, WorkflowJSON } from '@n8n/workflow-sdk'; +import { mock } from 'vitest-mock-extended'; + +import type { ThreadPatch, ThreadRecord } from '../../../storage/thread-patch'; +import type { InstanceAiContext, NodeDescription, SetupItemsEmitter } from '../../../types'; +import { resolveCredentials, type CredentialMap } from '../resolve-credentials'; +import type { ResolvedCredential } from '../resolved-credential.schema'; +import { + applyPendingSetupCredentialSelections, + markSetupCredentialSelectionsApplied, +} from '../setup-credential-selections'; + +const itemId = 'workflow-1:credential:slackApi'; +const selection: InstanceAiSetupCredentialSelection = { + selectionId: 'choice-1', + credentialType: 'slackApi', + credentialId: 'selected-credential', +}; +const credentialMap: CredentialMap = new Map([ + ['slackApi', [{ id: 'selected-credential', name: 'Selected account', type: 'slackApi' }]], + [ + 'httpBasicAuth', + [{ id: 'selected-credential', name: 'Selected account', type: 'httpBasicAuth' }], + ], +]); + +function node(name: string, type = 'slackApi', overrides: Partial = {}): NodeJSON { + return { id: name, name, type, typeVersion: 1, position: [0, 0], ...overrides }; +} + +function workflow(nodes: NodeJSON[]): WorkflowJSON { + return { name: 'Test', nodes, connections: {} }; +} + +function createContext(metadata: Record) { + const thread: ThreadRecord = { + id: 'thread-1', + resourceId: 'user-1', + metadata, + createdAt: new Date(), + updatedAt: new Date(), + }; + const threadMemory = { + getThread: vi.fn(async () => thread), + patchThread: vi.fn( + async ({ update }: { update: (current: ThreadRecord) => ThreadPatch | null | undefined }) => { + Object.assign(thread, update(thread)); + return thread; + }, + ), + }; + const context = mock({ + threadId: thread.id, + threadMemory, + setupItemsEmitter: mock(), + logger: mock(), + credentialService: mock(), + workflowService: mock({ + getAsWorkflowJSON: vi.fn().mockResolvedValue(workflow([])), + }), + nodeService: mock({ + getNodeCredentialTypes: undefined, + getDescription: vi.fn( + async (type: string): Promise => ({ + name: type, + displayName: type, + description: '', + version: 1, + group: [], + properties: [], + inputs: [], + outputs: [], + credentials: [{ name: type, displayOptions: { hide: { authentication: ['none'] } } }], + }), + ), + }), + }); + return { + context, + thread, + threadMemory, + apply: async (json: WorkflowJSON, candidates = credentialMap) => + await applyPendingSetupCredentialSelections(json, 'workflow-1', context, candidates), + resolve: async ( + json: WorkflowJSON, + candidates: CredentialMap, + preferNewCredentialTypes?: readonly string[], + setupSelections?: Record, + ) => + await resolveCredentials( + json, + 'workflow-1', + context, + candidates, + preferNewCredentialTypes, + setupSelections, + ), + }; +} + +describe('setup credential selections', () => { + it.each([false, true])( + 'keeps node-specific choices when inserted first: %s', + async (specificFirst) => { + const specific = { + ...selection, + selectionId: 'specific', + credentialId: 'specific-account', + nodeNames: ['First'], + }; + const entries = [ + [instanceAiSetupCredentialSelectionKey(itemId), selection], + [instanceAiSetupCredentialSelectionKey(`${itemId}:First`), specific], + ]; + const { apply } = createContext( + Object.fromEntries(specificFirst ? entries.reverse() : entries), + ); + const json = workflow([node('First'), node('Second')]); + const candidates: CredentialMap = new Map([ + [ + 'slackApi', + [ + { id: selection.credentialId, name: 'Default account', type: 'slackApi' }, + { id: specific.credentialId, name: 'Specific account', type: 'slackApi' }, + ], + ], + ]); + const result = await apply(json, candidates); + expect(json.nodes.map((current) => current.credentials?.slackApi?.id)).toEqual([ + 'specific-account', + 'selected-credential', + ]); + expect(result.resolvedCredentialsByNode.First).toEqual([ + { type: 'slackApi', id: 'specific-account', name: 'Specific account' }, + ]); + }, + ); + + it('applies an early choice to compatible nodes with the stored credential name', async () => { + const { apply } = createContext({ + [instanceAiSetupCredentialSelectionKey(itemId)]: selection, + }); + const json = workflow([ + node('First', 'slackApi', { credentials: { slackApi: { id: 'old', name: 'Old' } } }), + node('Second'), + node('Other service', 'gmailOAuth2'), + node('Inactive', 'slackApi', { parameters: { authentication: 'none' } }), + node('Disabled', 'slackApi', { disabled: true }), + ]); + const result = await apply(json); + expect(json.nodes.map(({ credentials }) => credentials)).toEqual([ + { slackApi: { id: 'selected-credential', name: 'Selected account' } }, + { slackApi: { id: 'selected-credential', name: 'Selected account' } }, + undefined, + undefined, + undefined, + ]); + expect(Object.keys(result.resolvedCredentialsByNode)).toEqual(['First', 'Second']); + expect(result.consumedSelections).toEqual([{ itemId, selection }]); + }); + + it('keeps an unavailable choice open and removes its previous binding', async () => { + const { context, apply, resolve } = createContext({ + [instanceAiSetupCredentialSelectionKey(itemId)]: selection, + }); + const json = workflow([ + node('Slack', 'slackApi', { + credentials: { slackApi: { id: 'other-project', name: 'Other' } }, + }), + ]); + vi.mocked(context.workflowService.getAsWorkflowJSON).mockResolvedValue(structuredClone(json)); + const result = await apply(json, new Map()); + expect(json.nodes[0].credentials).toEqual({}); + expect(result.unavailableCredentialTypes).toEqual(['slackApi']); + expect(result.consumedSelections).toEqual([]); + await resolve(json, new Map(), result.unavailableCredentialTypes); + expect(json.nodes[0].credentials).toEqual({}); + }); + + it('keeps an explicit custom-auth choice through automatic resolution', async () => { + const customType = TEMPLATED_CUSTOM_AUTH_CREDENTIAL_TYPE; + const customSelection = { ...selection, credentialType: customType, nodeNames: ['API'] }; + const { apply, resolve } = createContext({ + [instanceAiSetupCredentialSelectionKey(`workflow-1:credential:${customType}:API`)]: + customSelection, + }); + const json = workflow([node('API', customType)]); + const customMap: CredentialMap = new Map([ + [customType, [{ id: selection.credentialId, name: 'Selected account', type: customType }]], + ]); + const selected = await apply(json, customMap); + const resolved = await resolve(json, customMap, undefined, selected.resolvedCredentialsByNode); + expect(json.nodes[0].credentials?.[customType]).toEqual({ + id: selection.credentialId, + name: 'Selected account', + }); + expect(resolved.mockedNodeNames).toEqual([]); + }); + + it('applies generic credentials only to the selected nodes', async () => { + const genericSelection = { + ...selection, + credentialType: 'httpBasicAuth', + nodeNames: ['First'], + }; + const genericId = 'workflow-1:credential:httpBasicAuth:First'; + const { apply } = createContext({ + [instanceAiSetupCredentialSelectionKey(genericId)]: genericSelection, + }); + const json = workflow([node('First', 'httpBasicAuth'), node('Second', 'httpBasicAuth')]); + await apply(json); + expect(json.nodes[0].credentials).toEqual({ + httpBasicAuth: { id: 'selected-credential', name: 'Selected account' }, + }); + expect(json.nodes[1].credentials).toBeUndefined(); + }); + + it('does not fan a generic credential out without node names', async () => { + const { apply } = createContext({ + [instanceAiSetupCredentialSelectionKey('workflow-1:credential:httpBasicAuth')]: { + ...selection, + credentialType: 'httpBasicAuth', + }, + }); + const json = workflow([node('First', 'httpBasicAuth')]); + await apply(json); + expect(json.nodes[0].credentials).toBeUndefined(); + }); + + it('validates Gateway support before applying its marker', async () => { + const { context, apply } = createContext({ + [instanceAiSetupCredentialSelectionKey(itemId)]: { + ...selection, + credentialId: AI_GATEWAY_MANAGED_TAG, + }, + }); + const json = workflow([node('Slack')]); + vi.mocked(context.credentialService.isAiGatewayCredentialType!).mockResolvedValue(false); + expect((await apply(json)).unavailableCredentialTypes).toEqual(['slackApi']); + vi.mocked(context.credentialService.isAiGatewayCredentialType!).mockResolvedValue(true); + await apply(json); + expect(json.nodes[0].credentials).toEqual({ + slackApi: { id: null, name: 'Gateway credits', __aiGatewayManaged: true }, + }); + }); + + it('keeps selected Gateway credits when a stored alternative exists', async () => { + const { context, apply, resolve } = createContext({ + [instanceAiSetupCredentialSelectionKey(itemId)]: { + ...selection, + credentialId: AI_GATEWAY_MANAGED_TAG, + }, + }); + vi.mocked(context.credentialService.isAiGatewayCredentialType!).mockResolvedValue(true); + const json = workflow([ + node('Slack', 'slackApi', { + credentials: { slackApi: { id: 'selected-credential', name: 'Stored account' } }, + }), + ]); + const selected = await apply(json); + const resolved = await resolve( + json, + credentialMap, + undefined, + selected.resolvedCredentialsByNode, + ); + + expect(json.nodes[0].credentials).toEqual({ + slackApi: { id: null, name: 'Gateway credits', __aiGatewayManaged: true }, + }); + expect(resolved.resolvedCredentialsByNode.Slack).toEqual([ + { type: 'slackApi', id: null, name: 'Gateway credits', __aiGatewayManaged: true }, + ]); + expect(resolved.mockedCredentialTypes).toEqual([]); + }); + + it('keeps a choice until a later build adds the service', async () => { + const { context, thread, apply, resolve } = createContext({ + [instanceAiSetupCredentialSelectionKey(itemId)]: selection, + }); + const first = await apply(workflow([node('Trigger', 'manualTrigger')])); + await markSetupCredentialSelectionsApplied(context, first.consumedSelections); + expect(readPendingInstanceAiSetupCredentialSelections(thread.metadata, 'workflow-1')).toEqual([ + { itemId, selection }, + ]); + + const json = workflow([ + node('Slack', 'slackApi', { + credentials: { slackApi: { id: 'other', name: 'Other account' } }, + }), + ]); + const candidates: CredentialMap = new Map([ + [ + 'slackApi', + [ + { id: selection.credentialId, name: 'Selected account', type: 'slackApi' }, + { id: 'other', name: 'Other account', type: 'slackApi' }, + ], + ], + ]); + const result = await apply(json, candidates); + await resolve(json, candidates, undefined, result.resolvedCredentialsByNode); + expect(json.nodes[0].credentials?.slackApi?.id).toBe(selection.credentialId); + await markSetupCredentialSelectionsApplied(context, result.consumedSelections); + expect(readPendingInstanceAiSetupCredentialSelections(thread.metadata, 'workflow-1')).toEqual( + [], + ); + }); + + it('preserves a newer selection when the earlier workflow save finishes', async () => { + const { context, thread, apply } = createContext({ + [instanceAiSetupCredentialSelectionKey(itemId)]: selection, + }); + const result = await apply(workflow([node('Slack')])); + const newerSelection = { ...selection, selectionId: 'choice-2' }; + thread.metadata = { [instanceAiSetupCredentialSelectionKey(itemId)]: newerSelection }; + await markSetupCredentialSelectionsApplied(context, result.consumedSelections); + expect( + thread.metadata[instanceAiSetupCredentialAppliedKey(itemId, newerSelection.selectionId)], + ).toBeUndefined(); + expect(readPendingInstanceAiSetupCredentialSelections(thread.metadata, 'workflow-1')).toEqual([ + { itemId, selection: newerSelection }, + ]); + }); + + it('keeps the choice pending if marking a successful save fails', async () => { + const { context, thread, threadMemory } = createContext({ + [instanceAiSetupCredentialSelectionKey(itemId)]: selection, + }); + threadMemory.patchThread.mockRejectedValue(new Error('Storage unavailable')); + await expect( + markSetupCredentialSelectionsApplied(context, [{ itemId, selection }]), + ).resolves.toBeUndefined(); + expect(readPendingInstanceAiSetupCredentialSelections(thread.metadata, 'workflow-1')).toEqual([ + { itemId, selection }, + ]); + expect(context.logger.warn).toHaveBeenCalled(); + }); + + it('does not read selections when the setup panel is disabled', async () => { + const { context, threadMemory, apply } = createContext({ + [instanceAiSetupCredentialSelectionKey(itemId)]: selection, + }); + context.setupItemsEmitter = undefined; + await apply(workflow([node('Slack')])); + expect(threadMemory.getThread).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/setup-items.test.ts b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/setup-items.test.ts index 834c18fc145..c0ce2ab8c3c 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/setup-items.test.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/setup-items.test.ts @@ -9,6 +9,7 @@ import { buildSetupItemsFromCredentialRequests, buildSetupItemsFromSetupRequests, createSetupItemsEmitter, + requestsCredentialReplacement, } from '../setup-items'; import type { SetupRequest } from '../setup-workflow.schema'; @@ -66,6 +67,23 @@ function setupRequest(overrides: { }; } +describe('requestsCredentialReplacement', () => { + it.each([ + { boundCredential: undefined, replacement: false }, + { boundCredential: { id: '', name: 'Unconnected' }, replacement: false }, + { boundCredential: { id: 'existing', name: 'Existing' }, replacement: true }, + { boundCredential: { id: null, name: 'Gateway credits' }, replacement: true }, + ])('requires an existing account for replacement: %j', ({ boundCredential, replacement }) => { + const request = setupRequest({ + nodeName: 'Slack', + credentialType: 'slackApi', + boundCredential, + }); + expect(requestsCredentialReplacement([request], ['slackApi'])).toBe(replacement); + expect(requestsCredentialReplacement([request], ['gmailOAuth2'])).toBe(false); + }); +}); + describe('createSetupItemsEmitter', () => { it('waits for persisted state before it completes the final announcement', async () => { const items: InstanceAiSetupItem[] = [ @@ -355,6 +373,28 @@ const templatedAuthHint = { }; describe('buildSetupItemsFromCredentialRequests', () => { + it.each([ + [undefined, true], + [true, undefined], + ])('preserves preferNew across duplicate requests (%s, %s)', (first, second) => { + const requests = [first, second].map((preferNew) => ({ + credentialType: 'slackApi', + preferNew, + })); + const analyzed = [ + setupRequest({ nodeName: 'Slack', credentialType: 'slackApi', credentialNeedsAction: true }), + ]; + + for (const items of [ + buildSetupItemsFromCredentialRequests('wf-1', requests), + buildSetupItemsFromAnnouncement('wf-1', requests, analyzed), + ]) { + expect(items).toEqual([ + expect.objectContaining({ credentialType: 'slackApi', preferNew: true }), + ]); + } + }); + it('builds one service-keyed credential item per type, carrying reason and setupHint', () => { const items = buildSetupItemsFromCredentialRequests('wf-1', [ { credentialType: 'slackApi', reason: 'to post alerts' }, diff --git a/packages/@n8n/instance-ai/src/tools/workflows/build-workflow.tool.ts b/packages/@n8n/instance-ai/src/tools/workflows/build-workflow.tool.ts index df1c0427887..0dcda9220e5 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/build-workflow.tool.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/build-workflow.tool.ts @@ -26,6 +26,10 @@ import { import { resolvedCredentialSchema } from './resolved-credential.schema'; import { describeSavedPublishState, savedWorkflowStateSchema } from './saved-workflow-state'; import { isSetupPanelEnabled } from './setup-items'; +import { + applyPendingSetupCredentialSelections, + markSetupCredentialSelectionsApplied, +} from './setup-credential-selections'; import { recordWorkflowSetupState } from './setup-panel-state'; import { getSkippedSetupSubjects, partitionSkippedSetupRequests } from './setup-skip-state'; import { analyzeWorkflow, stripStaleCredentialsFromWorkflow } from './setup-workflow.service'; @@ -1174,13 +1178,49 @@ export function createBuildWorkflowTool(context: InstanceAiContext) { } const credentialMap = await buildCredentialMap(context.credentialService); + const setupSelections = await applyPendingSetupCredentialSelections( + json, + targetWorkflowId, + context, + credentialMap, + ); + const setupPreferences = + isSetupPanelEnabled(context) && binding.setupPreferences?.runId === context.runId + ? binding.setupPreferences + : undefined; + const selectedTypes = new Set( + Object.values(setupSelections.resolvedCredentialsByNode) + .flat() + .map((credential) => credential.type), + ); + // Keep a saved choice when the model repeats setup flags during a repair. + const preferNewCredentialTypes = [ + ...new Set([ + ...(setupPreferences?.preferNewCredentialTypes ?? []), + ...(input.preferNewCredentials ?? []), + ]), + ].filter( + (type) => + !selectedTypes.has(type) && !setupPreferences?.satisfiedCredentialTypes.includes(type), + ); const mockResult = await resolveCredentials( json, targetWorkflowId, context, credentialMap, - input.preferNewCredentials, + [...preferNewCredentialTypes, ...setupSelections.unavailableCredentialTypes], + setupSelections.resolvedCredentialsByNode, ); + for (const [nodeName, selections] of Object.entries( + setupSelections.resolvedCredentialsByNode, + )) { + mockResult.resolvedCredentialsByNode[nodeName] = [ + ...(mockResult.resolvedCredentialsByNode[nodeName] ?? []).filter( + (credential) => !selections.some((selection) => selection.type === credential.type), + ), + ...selections, + ]; + } // Deterministic backstop for a builder that never checked credentials: // a chat-model node for a provider the user has no credential for gets @@ -1418,15 +1458,14 @@ export function createBuildWorkflowTool(context: InstanceAiContext) { }, operation: 'create' | 'update', ) => { + await markSetupCredentialSelectionsApplied(context, setupSelections.consumedSelections); // The setup panel lists bound slots too (rendered as done), so its // snapshot needs the settled requests the routing below must not see. const setupItemsEmitter = isSetupPanelEnabled(context) ? context.setupItemsEmitter : undefined; const analyzedRequests = await analyzeWorkflow(context, saved.id, undefined, { - ...(input.preferNewCredentials - ? { preferNewCredentialTypes: input.preferNewCredentials } - : {}), + ...(preferNewCredentialTypes.length > 0 ? { preferNewCredentialTypes } : {}), ...(setupItemsEmitter ? { includeSettled: true } : {}), }); const setupRequests = analyzedRequests.filter((request) => !!request.needsAction); @@ -1476,6 +1515,18 @@ export function createBuildWorkflowTool(context: InstanceAiContext) { workflowVersionId: saved.versionId, ...(saved.checksum ? { workflowChecksum: saved.checksum } : {}), sourceHash, + setupPending: undefined, + ...(setupPreferences + ? { + setupPreferences: { + ...setupPreferences, + preferNewCredentialTypes, + satisfiedCredentialTypes: [ + ...new Set([...setupPreferences.satisfiedCredentialTypes, ...selectedTypes]), + ], + }, + } + : {}), }); // Trace-only compiled-JSON event for eval seed reconstruction — never part // of the tool result, so it never enters the agent's context. @@ -1635,7 +1686,7 @@ export function createBuildWorkflowTool(context: InstanceAiContext) { json, updateOptions, ); - return await createSuccessResponse(updated, 'update'); + return await createSuccessResponse(updated, binding.setupPending ? 'create' : 'update'); } const created = await context.workflowService.createFromWorkflowJSON(json, { diff --git a/packages/@n8n/instance-ai/src/tools/workflows/prepare-workflow-setup.ts b/packages/@n8n/instance-ai/src/tools/workflows/prepare-workflow-setup.ts new file mode 100644 index 00000000000..9469b2651d7 --- /dev/null +++ b/packages/@n8n/instance-ai/src/tools/workflows/prepare-workflow-setup.ts @@ -0,0 +1,172 @@ +import { GENERIC_AUTH_CREDENTIAL_TYPES } from '@n8n/api-types'; +import { OperationalError, UserError } from 'n8n-workflow'; + +import { + buildSetupItemsFromAnnouncement, + buildSetupItemsFromCredentialRequests, + isSetupPanelEnabled, + requestsCredentialReplacement, + type AnnouncedCredentialRequest, +} from './setup-items'; +import { analyzeWorkflow } from './setup-workflow.service'; +import { recordSessionOwnedWorkflow } from './workflow-build-context'; +import { promoteMainWorkflow } from './workflow-build-reporting'; +import { + getWorkflowSourceFileBinding, + normalizeWorkflowSourceFilePath, + saveWorkflowSourceFileBinding, + type WorkflowSourceFileBinding, +} from './workflow-file-bindings'; +import type { InstanceAiContext } from '../../types'; + +const pendingPreparations = new WeakMap>(); + +/** Parallel tool calls must not create two workflows for the same source file. */ +export async function prepareWorkflowSetup( + context: InstanceAiContext, + input: Parameters[1], +) { + const previous = pendingPreparations.get(context) ?? Promise.resolve(); + const pending = previous.catch(() => undefined).then(async () => await prepare(context, input)); + pendingPreparations.set(context, pending); + try { + return await pending; + } finally { + if (pendingPreparations.get(context) === pending) pendingPreparations.delete(context); + } +} + +/** Establish the saved workflow identity before the model writes its source. */ +async function prepare( + context: InstanceAiContext, + input: { + filePath: string; + workflowName?: string; + workflowId?: string; + folderPath?: string; + credentials: readonly AnnouncedCredentialRequest[]; + }, +) { + if (!isSetupPanelEnabled(context) || !context.threadMemory || !context.threadId) { + throw new OperationalError('Early workflow setup is unavailable'); + } + if ( + input.credentials.some(({ credentialType }) => + GENERIC_AUTH_CREDENTIAL_TYPES.has(credentialType), + ) + ) { + throw new UserError( + 'Announce service-specific credential types before building. Leave generic authentication for workflow setup after the build.', + ); + } + const filePath = normalizeWorkflowSourceFilePath(input.filePath, { + workspaceRoot: context.workspaceRoot, + }); + let binding = await getWorkflowSourceFileBinding(context, filePath, { requirePersistence: true }); + if (input.workflowId && binding?.workflowId && input.workflowId !== binding.workflowId) { + throw new UserError( + 'The source file belongs to another workflow. Use its bound workflow or a different filePath.', + ); + } + const workflowId = binding?.workflowId ?? input.workflowId; + const satisfiedCredentialTypes = + binding?.setupPreferences?.runId === context.runId + ? (binding?.setupPreferences?.satisfiedCredentialTypes ?? []) + : []; + const setupPreferences: NonNullable = { + runId: context.runId, + satisfiedCredentialTypes, + preferNewCredentialTypes: input.credentials + .filter( + (request) => + request.preferNew && !satisfiedCredentialTypes.includes(request.credentialType), + ) + .map((request) => request.credentialType), + }; + let analyzedRequests: Awaited> | undefined; + if (workflowId) { + // Validate access before publishing a workflow-scoped announcement. + const saved = await context.workflowService.get(workflowId); + if (!binding?.setupPending) { + analyzedRequests = await analyzeWorkflow(context, workflowId, undefined, { + includeSettled: true, + }); + } + if ( + analyzedRequests && + requestsCredentialReplacement(analyzedRequests, setupPreferences.preferNewCredentialTypes) + ) { + throw new UserError( + 'Use credential setup without filePath to replace an existing workflow account.', + ); + } + binding = await saveWorkflowSourceFileBinding( + context, + { + ...binding, + filePath, + workflowId, + workflowVersionId: saved.versionId, + workflowChecksum: saved.checksum, + setupPreferences, + }, + { requirePersistence: true }, + ); + if (context.aiCreatedWorkflowIds?.has(workflowId)) { + await recordSessionOwnedWorkflow(context, workflowId); + } + } else { + if ( + context.permissions?.createWorkflow === 'blocked' || + context.permissions?.updateWorkflow === 'blocked' + ) { + throw new UserError('Early workflow setup is blocked by admin policy'); + } + if (!input.workflowName?.trim() || input.credentials.length === 0) { + throw new UserError( + 'Provide a workflowName and the known credential requirements for a new workflow.', + ); + } + const saved = await context.workflowService.createFromWorkflowJSON( + { name: input.workflowName, nodes: [], connections: {} }, + { markAsAiTemporary: true, ...(input.folderPath ? { folderPath: input.folderPath } : {}) }, + ); + try { + binding = await saveWorkflowSourceFileBinding( + context, + { + filePath, + workflowId: saved.id, + workflowVersionId: saved.versionId, + workflowChecksum: saved.checksum, + setupPending: true, + setupPreferences, + }, + { requirePersistence: true }, + ); + } finally { + await recordSessionOwnedWorkflow(context, saved.id); + } + } + if (!binding.workflowId) + throw new OperationalError('The workflow setup context has no workflow ID'); + if (binding.setupPending) await promoteMainWorkflow(context, binding.workflowId); + const items = binding.setupPending + ? buildSetupItemsFromCredentialRequests(binding.workflowId, input.credentials) + : buildSetupItemsFromAnnouncement( + binding.workflowId, + input.credentials, + analyzedRequests ?? + (await analyzeWorkflow(context, binding.workflowId, undefined, { includeSettled: true })), + ); + await context.setupItemsEmitter.announce(binding.workflowId, items); + return { + success: true, + announced: true, + preBuild: true, + workflowId: binding.workflowId, + filePath, + message: + 'The setup panel is ready. Continue writing the workflow source and call build-workflow with this filePath. Do not wait for connections or repeat folderPath on the build. The user can connect while you work. Their selections will be applied to the generated nodes. If the plan changes before building, call setup again with the complete remaining requirements.', + }; +} diff --git a/packages/@n8n/instance-ai/src/tools/workflows/resolve-credentials.ts b/packages/@n8n/instance-ai/src/tools/workflows/resolve-credentials.ts index 067c698bc21..2f6e27b55e3 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/resolve-credentials.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/resolve-credentials.ts @@ -183,6 +183,7 @@ export async function resolveCredentials( ctx: InstanceAiContext, availableCredentials?: CredentialMap, preferNewCredentialTypes?: readonly string[], + setupSelections?: Record, ): Promise { const preferNewTypes = new Set(preferNewCredentialTypes ?? []); const heldForNewCredentialTypes = new Set(); @@ -435,6 +436,14 @@ export async function resolveCredentials( mockCredential(); }; + const setupSelection = node.name + ? setupSelections?.[node.name]?.find((selection) => selection.type === key) + : undefined; + if (setupSelection?.id === null && (await isGatewayCredentialType(key))) { + await attachGatewayCredential(); + continue; + } + if (value !== undefined && value !== null) { // Templated Custom Auth ids are service-agnostic at the type level, so a // model-attached reference can silently wire another service's credential @@ -444,7 +453,8 @@ export async function resolveCredentials( if (key === TEMPLATED_CUSTOM_AUTH_CREDENTIAL_TYPE) { const suppliedId = getCredentialId(value); const priorId = getCredentialId(existingCreds?.[key]); - if (suppliedId !== undefined && suppliedId === priorId) { + const selectedByUser = setupSelection?.id === suppliedId; + if (suppliedId !== undefined && (suppliedId === priorId || selectedByUser)) { cleanupMockPinData(json, node.name); continue; } diff --git a/packages/@n8n/instance-ai/src/tools/workflows/setup-credential-selections.ts b/packages/@n8n/instance-ai/src/tools/workflows/setup-credential-selections.ts new file mode 100644 index 00000000000..78745097a93 --- /dev/null +++ b/packages/@n8n/instance-ai/src/tools/workflows/setup-credential-selections.ts @@ -0,0 +1,145 @@ +import { + AI_GATEWAY_MANAGED_TAG, + GENERIC_AUTH_CREDENTIAL_TYPES, + instanceAiSetupCredentialAppliedKey, + readPendingInstanceAiSetupCredentialSelections, +} from '@n8n/api-types'; +import type { WorkflowJSON } from '@n8n/workflow-sdk'; + +import { + AI_GATEWAY_CREDENTIAL, + N8N_CONNECT_DISPLAY_NAME, + assignCredentialToNode, +} from './credential-utils'; +import type { CredentialMap } from './resolve-credentials'; +import type { ResolvedCredential } from './resolved-credential.schema'; +import { isSetupPanelEnabled } from './setup-items'; +import { getValidCredentialTypes } from './setup-workflow.service'; +import { findWorkflowSourceFileBindingsForWorkflow } from './workflow-file-bindings'; +import { getThread, patchThread } from '../../storage/thread-patch'; +import type { InstanceAiContext } from '../../types'; + +type PendingSelection = ReturnType[number]; + +/** A repeated setup call must retain choices already saved during this run. */ +export async function filterSatisfiedSetupCredentialTypes( + context: InstanceAiContext, + workflowId: string, + credentialTypes: readonly string[] | undefined, +): Promise { + if (!isSetupPanelEnabled(context) || !context.runId || !credentialTypes?.length) + return credentialTypes; + const bindings = await findWorkflowSourceFileBindingsForWorkflow(context, workflowId); + const satisfied = new Set( + bindings.flatMap(({ setupPreferences }) => + setupPreferences && setupPreferences.runId === context.runId + ? setupPreferences.satisfiedCredentialTypes + : [], + ), + ); + return credentialTypes.filter((type) => !satisfied.has(type)); +} + +export interface SetupCredentialSelectionResult { + consumedSelections: PendingSelection[]; + unavailableCredentialTypes: string[]; + resolvedCredentialsByNode: Record; +} + +/** Apply the user's latest choices before automatic credential resolution. */ +export async function applyPendingSetupCredentialSelections( + json: WorkflowJSON, + workflowId: string | undefined, + context: InstanceAiContext, + credentialMap: CredentialMap, +): Promise { + const result: SetupCredentialSelectionResult = { + consumedSelections: [], + unavailableCredentialTypes: [], + resolvedCredentialsByNode: {}, + }; + if (!isSetupPanelEnabled(context) || !workflowId || !context.threadId || !context.threadMemory) { + return result; + } + const thread = await getThread(context.threadMemory, context.threadId); + const selections = readPendingInstanceAiSetupCredentialSelections(thread?.metadata, workflowId); + if (selections.length === 0) return result; + + const activeNodes = await Promise.all( + json.nodes + .filter((node) => !node.disabled && node.name) + .map(async (node) => ({ + node, + credentialTypes: await getValidCredentialTypes(context, node), + })), + ); + + for (const pending of selections) { + const { credentialType, credentialId, nodeNames } = pending.selection; + const generic = GENERIC_AUTH_CREDENTIAL_TYPES.has(credentialType); + const targets = activeNodes.filter( + ({ node, credentialTypes }) => + credentialTypes.has(credentialType) && + (nodeNames ? nodeNames.includes(node.name ?? '') : !generic), + ); + if (targets.length === 0) continue; + + const stored = credentialMap.get(credentialType)?.find(({ id }) => id === credentialId); + const gateway = + credentialId === AI_GATEWAY_MANAGED_TAG && + (await context.credentialService.isAiGatewayCredentialType?.(credentialType)); + if (!stored && !gateway) { + // Leave these slots open instead of substituting another account. + for (const { node } of targets) { + delete node.credentials?.[credentialType]; + if (node.name && result.resolvedCredentialsByNode[node.name]) { + result.resolvedCredentialsByNode[node.name] = result.resolvedCredentialsByNode[ + node.name + ].filter((credential) => credential.type !== credentialType); + } + } + result.unavailableCredentialTypes.push(credentialType); + continue; + } + + const credential = stored + ? { id: stored.id, name: stored.name } + : { ...AI_GATEWAY_CREDENTIAL, name: N8N_CONNECT_DISPLAY_NAME }; + for (const { node } of targets) { + if (!node.name) continue; + assignCredentialToNode(node, credentialType, credential); + result.resolvedCredentialsByNode[node.name] = [ + ...(result.resolvedCredentialsByNode[node.name] ?? []).filter( + (current) => current.type !== credentialType, + ), + { type: credentialType, ...credential }, + ]; + } + result.consumedSelections.push(pending); + } + return result; +} + +/** Call only after the workflow save succeeds. */ +export async function markSetupCredentialSelectionsApplied( + context: InstanceAiContext, + selections: readonly PendingSelection[], +): Promise { + if (!context.threadMemory || !context.threadId || selections.length === 0) return; + try { + await patchThread(context.threadMemory, { + threadId: context.threadId, + update: ({ metadata = {} }) => { + const updated = { ...metadata }; + for (const { itemId, selection } of selections) { + updated[instanceAiSetupCredentialAppliedKey(itemId, selection.selectionId)] = true; + } + return { metadata: updated }; + }, + }); + } catch (error) { + context.logger?.warn('Failed to mark setup credential selections as applied', { + error: error instanceof Error ? error.message : String(error), + }); + } +} diff --git a/packages/@n8n/instance-ai/src/tools/workflows/setup-items.ts b/packages/@n8n/instance-ai/src/tools/workflows/setup-items.ts index cc4fead7310..abe59095006 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/setup-items.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/setup-items.ts @@ -12,6 +12,7 @@ import { } from '@n8n/api-types'; import { OperationalError } from 'n8n-workflow'; +import { isAiGatewayManagedCredential } from './credential-utils'; import type { SetupRequest } from './setup-workflow.schema'; import type { InstanceAiEventBus } from '../../event-bus/event-bus.interface'; import type { InstanceAiContext, SetupItemsEmitter } from '../../types'; @@ -149,9 +150,21 @@ export function parametersSetupItemId(workflowId: string, nodeName: string): str export interface AnnouncedCredentialRequest { credentialType: string; reason?: string; + preferNew?: boolean; setupHint?: InstanceAiCredentialSetupHint; } +function mergeCredentialRequests(requests: readonly AnnouncedCredentialRequest[]) { + const byType = new Map(); + for (const request of requests) { + const existing = byType.get(request.credentialType); + if (!existing || request.preferNew) { + byType.set(request.credentialType, existing ? { ...existing, preferNew: true } : request); + } + } + return byType; +} + /** * Items for a `credentials(action="setup")` announcement without node context: * one service-keyed row per type, no bindings; the next build snapshot fans them @@ -162,20 +175,20 @@ export function buildSetupItemsFromCredentialRequests( workflowId: string, requests: readonly AnnouncedCredentialRequest[], ): InstanceAiSetupItem[] { - const byType = new Map(); - for (const request of requests) { + const items: InstanceAiSetupItem[] = []; + for (const request of mergeCredentialRequests(requests).values()) { if (GENERIC_AUTH_CREDENTIAL_TYPES.has(request.credentialType)) continue; const id = credentialSetupItemId(workflowId, request.credentialType); - if (byType.has(id)) continue; - byType.set(id, { + items.push({ id, kind: 'credential', credentialType: request.credentialType, + ...(request.preferNew !== undefined ? { preferNew: request.preferNew } : {}), ...(request.reason ? { reason: request.reason } : {}), ...(request.setupHint ? { setupHint: request.setupHint } : {}), }); } - return [...byType.values()]; + return items; } /** @@ -190,12 +203,7 @@ export function buildSetupItemsFromAnnouncement( requests: readonly AnnouncedCredentialRequest[], analyzedRequests: readonly SetupRequest[], ): InstanceAiSetupItem[] { - const requestByType = new Map(); - for (const request of requests) { - if (!requestByType.has(request.credentialType)) { - requestByType.set(request.credentialType, request); - } - } + const requestByType = mergeCredentialRequests(requests); const coveredTypes = new Set(); const items = buildSetupItemsFromSetupRequests(workflowId, analyzedRequests).map((item) => { if (item.kind !== 'credential') return item; @@ -204,6 +212,7 @@ export function buildSetupItemsFromAnnouncement( coveredTypes.add(item.credentialType); return { ...item, + ...(request.preferNew !== undefined ? { preferNew: request.preferNew } : {}), ...(request.reason ? { reason: request.reason } : {}), ...(request.setupHint ? { setupHint: request.setupHint } : {}), }; @@ -223,6 +232,20 @@ function isBoundToStoredCredential(request: SetupRequest): boolean { return typeof bound?.id === 'string' && bound.id.length > 0; } +/** A request for a first account does not replace an existing binding. */ +export function requestsCredentialReplacement( + requests: readonly SetupRequest[], + credentialTypes: readonly string[] = [], +): boolean { + return requests.some( + (request) => + request.credentialType !== undefined && + credentialTypes.includes(request.credentialType) && + (isBoundToStoredCredential(request) || + isAiGatewayManagedCredential(request.node.credentials?.[request.credentialType])), + ); +} + /** * Items for a saved workflow from its setup analysis (`analyzeWorkflow` with * `includeSettled`): every credential slot that is open or already bound to a @@ -250,11 +273,13 @@ export function buildSetupItemsFromSetupRequests( existing.nodeBindings = [...(existing.nodeBindings ?? []), { nodeName }]; } if (!existing.setupHint && request.setupHint) existing.setupHint = request.setupHint; + if (request.preferNewCredential) existing.preferNew = true; } else { credentialItems.set(id, { id, kind: 'credential', credentialType, + ...(request.preferNewCredential ? { preferNew: true } : {}), nodeBindings: [{ nodeName }], ...(request.setupHint ? { setupHint: request.setupHint } : {}), }); diff --git a/packages/@n8n/instance-ai/src/tools/workflows/workflow-file-bindings.ts b/packages/@n8n/instance-ai/src/tools/workflows/workflow-file-bindings.ts index 61e297e1971..6dfae435336 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/workflow-file-bindings.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/workflow-file-bindings.ts @@ -1,5 +1,6 @@ import { createHash } from 'node:crypto'; import { z } from 'zod'; +import { OperationalError } from 'n8n-workflow'; import { getThread, patchThread } from '../../storage/thread-patch'; import type { InstanceAiContext } from '../../types'; @@ -14,6 +15,14 @@ const workflowSourceFileBindingSchema = z.object({ workflowVersionId: z.string().optional(), workflowChecksum: z.string().optional(), sourceHash: z.string().optional(), + setupPending: z.boolean().optional(), + setupPreferences: z + .object({ + runId: z.string().optional(), + satisfiedCredentialTypes: z.array(z.string()), + preferNewCredentialTypes: z.array(z.string()), + }) + .optional(), }); const workflowSourceFileBindingsSchema = z.record(z.string(), workflowSourceFileBindingSchema); @@ -53,13 +62,22 @@ function getFallbackBindings(context: InstanceAiContext): Map | undefined> { - if (!context.threadMemory || !context.threadId) return undefined; + if (!context.threadMemory || !context.threadId) { + if (requirePersistence) throw new OperationalError('Workflow setup persistence is unavailable'); + return undefined; + } try { const thread = await getThread(context.threadMemory, context.threadId); + if (requirePersistence) { + if (!thread) throw new OperationalError('The workflow setup thread is unavailable'); + return workflowSourceFileBindingsSchema.parse(thread.metadata?.[METADATA_KEY] ?? {}); + } return parseBindings(thread?.metadata?.[METADATA_KEY]); } catch (error) { + if (requirePersistence) throw error; context.logger?.debug('Failed to read workflow source file bindings from thread metadata', { error: error instanceof Error ? error.message : String(error), }); @@ -70,9 +88,10 @@ async function readThreadBindings( export async function getWorkflowSourceFileBinding( context: InstanceAiContext, filePath: string, + options: { requirePersistence?: boolean } = {}, ): Promise { const normalizedFilePath = normalizeWorkflowSourceFilePath(filePath); - const threadBindings = await readThreadBindings(context); + const threadBindings = await readThreadBindings(context, options.requirePersistence); if (threadBindings) { return ( threadBindings[normalizedFilePath] ?? getFallbackBindings(context).get(normalizedFilePath) @@ -109,14 +128,14 @@ export async function findWorkflowSourceFileBindingsForWorkflow( export async function saveWorkflowSourceFileBinding( context: InstanceAiContext, binding: WorkflowSourceFileBinding, + options: { requirePersistence?: boolean } = {}, ): Promise { const normalizedBinding = { ...binding, filePath: normalizeWorkflowSourceFilePath(binding.filePath), }; - // Always keep the run-local copy: a later thread-metadata read can fail, and the - // binding must still be found so an existing file is never treated as unbound. + // Retain the identity for retries even when its durable write fails. getFallbackBindings(context).set(normalizedBinding.filePath, normalizedBinding); if (context.threadMemory && context.threadId) { @@ -137,6 +156,9 @@ export async function saveWorkflowSourceFileBinding( }); } } + if (options.requirePersistence) { + throw new OperationalError('Could not persist the workflow setup context'); + } return normalizedBinding; } diff --git a/packages/frontend/@n8n/i18n/src/locales/en.json b/packages/frontend/@n8n/i18n/src/locales/en.json index ded2f2f5948..a77945a4305 100644 --- a/packages/frontend/@n8n/i18n/src/locales/en.json +++ b/packages/frontend/@n8n/i18n/src/locales/en.json @@ -7263,6 +7263,9 @@ "instanceAi.setupPanel.helpFindApiKey": "Help me find my API key", "instanceAi.setupPanel.helpFindAccessToken": "Help me find my access token", "instanceAi.setupPanel.helpSetUp": "Help me set this up", + "instanceAi.setupPanel.helpUnavailableWhileBuilding": "The assistant is still working. Help will be available when it finishes.", + "instanceAi.setupPanel.earlySetupCoachmark": "You can start completing these steps now. The list will update as the assistant builds your workflow.", + "instanceAi.setupPanel.dismissCoachmark": "Got it", "instanceAi.setupPanel.helpVisibleFields": "The OAuth form asks for: {fields}.", "instanceAi.setupPanel.helpOAuthState": "Selected OAuth mode: {mode}. Connection state: {state}.", "instanceAi.setupPanel.helpManagedOAuthAvailable": "Managed OAuth is available on this instance.", diff --git a/packages/frontend/editor-ui/src/app/constants/localStorage.ts b/packages/frontend/editor-ui/src/app/constants/localStorage.ts index 8695888c53a..923792cd98a 100644 --- a/packages/frontend/editor-ui/src/app/constants/localStorage.ts +++ b/packages/frontend/editor-ui/src/app/constants/localStorage.ts @@ -37,6 +37,8 @@ export const LOCAL_STORAGE_INSTANCE_AI_SETUP_DISMISSED = ( threadId: string, workflowId: string, ) => `N8N_INSTANCE_AI_SETUP_DISMISSED:${userId}:${threadId}:${workflowId}`; +export const LOCAL_STORAGE_INSTANCE_AI_SETUP_COACHMARK_SEEN = (userId: string) => + `N8N_INSTANCE_AI_SETUP_COACHMARK_SEEN:v1:${userId}`; export const LOCAL_STORAGE_INSTANCE_AI_CHAT_PANEL_WIDTH_RATIO = 'N8N_INSTANCE_AI_CHAT_PANEL_WIDTH_RATIO'; export const LOCAL_STORAGE_SIDEBAR_WIDTH = 'N8N_SIDEBAR_WIDTH'; diff --git a/packages/frontend/editor-ui/src/features/ai/instanceAi/__tests__/canvasPreview.utils.test.ts b/packages/frontend/editor-ui/src/features/ai/instanceAi/__tests__/canvasPreview.utils.test.ts index e7e88eb0bed..5d8b867bace 100644 --- a/packages/frontend/editor-ui/src/features/ai/instanceAi/__tests__/canvasPreview.utils.test.ts +++ b/packages/frontend/editor-ui/src/features/ai/instanceAi/__tests__/canvasPreview.utils.test.ts @@ -1185,6 +1185,23 @@ describe('getLatestWorkflowUpdateResult', () => { }); describe('isAgentEditingWorkflow', () => { + test('keeps early setup active between tool calls until the agent finishes', () => { + const node = makeAgentNode({ + status: 'active', + toolCalls: [ + makeToolCall({ + toolName: 'credentials', + args: { action: 'setup' }, + result: { announced: true, preBuild: true, workflowId: 'wf-1' }, + }), + ], + }); + expect(isAgentEditingWorkflow(node, 'wf-1')).toBe(true); + expect(isAgentEditingWorkflow(node, 'wf-2')).toBe(false); + node.status = 'completed'; + expect(isAgentEditingWorkflow(node, 'wf-1')).toBe(false); + }); + test('locks an announced workflow while its first build has no result yet', () => { const call = makeToolCall({ toolName: 'build-workflow', diff --git a/packages/frontend/editor-ui/src/features/ai/instanceAi/__tests__/useSetupPanelActions.test.ts b/packages/frontend/editor-ui/src/features/ai/instanceAi/__tests__/useSetupPanelActions.test.ts index 85f02e1e0dd..c82340c6419 100644 --- a/packages/frontend/editor-ui/src/features/ai/instanceAi/__tests__/useSetupPanelActions.test.ts +++ b/packages/frontend/editor-ui/src/features/ai/instanceAi/__tests__/useSetupPanelActions.test.ts @@ -3,15 +3,18 @@ import { setActivePinia } from 'pinia'; import { createTestingPinia } from '@pinia/testing'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { ResponseError } from '@n8n/rest-api-client'; +import { instanceAiSetupCredentialSelectionKey } from '@n8n/api-types'; import type { INodeTypeDescription } from 'n8n-workflow'; -import { createTestNode, createTestWorkflow } from '@/__tests__/mocks'; +import { createTestNode, createTestWorkflow, mockNodeTypeDescription } from '@/__tests__/mocks'; import { mockedStore } from '@/__tests__/utils'; import type { INodeUi, IWorkflowDb } from '@/Interface'; import { getWorkflow } from '@/app/api/workflows'; import { useNodeTypesStore } from '@/app/stores/nodeTypes.store'; import { useUIStore } from '@/app/stores/ui.store'; +import { useCredentialsStore } from '@/features/credentials/credentials.store'; +import { fetchThread, updateThreadMetadata } from '../instanceAi.memory.api'; import { createWorkflowDocumentId, useWorkflowDocumentStore, @@ -29,6 +32,20 @@ vi.mock('@/app/api/workflows', async (importOriginal) => ({ getWorkflow: vi.fn(), })); +let threadMetadata = ref>({}); +vi.mock('../instanceAi.store', () => ({ + useInstanceAiStore: () => ({ + getThreadMetadata: () => threadMetadata.value, + setThreadMetadata: (_id: string, metadata: Record) => { + threadMetadata.value = metadata; + }, + }), +})); +vi.mock('../instanceAi.memory.api', () => ({ + fetchThread: vi.fn(), + updateThreadMetadata: vi.fn(), +})); + // useNodeHelpers injects the host's document store at init, which needs a // component setup context — stub the one getter the mirror uses. const { getNodeCredentialIssues, getNodeInputIssues } = vi.hoisted(() => ({ @@ -72,6 +89,8 @@ function createHarness( ) { const building = ref(options.agentBuilding ?? false); const workflowId = ref('workflowId' in options ? options.workflowId : WORKFLOW_ID); + const savedWorkflowChecksum = ref('c1'); + const workflowNodes = ref(); const updateWorkflow = vi .fn() @@ -83,12 +102,23 @@ function createHarness( const onSaved = vi.fn<(workflow: IWorkflowDb) => void>(); const actions = useSetupPanelActions({ + threadId: 'thread-1', workflowId: () => workflowId.value, + workflowNodes, isAgentBuilding: () => building.value, + savedWorkflowChecksum, onFlushResult: options.onFlushResult, onSaved, }); - return { actions, building, workflowId, updateWorkflow, onSaved }; + return { + actions, + building, + workflowId, + workflowNodes, + savedWorkflowChecksum, + updateWorkflow, + onSaved, + }; } describe('useSetupPanelActions', () => { @@ -126,7 +156,7 @@ describe('useSetupPanelActions', () => { const { actions, building } = createHarness({ agentBuilding: true }); await actions.bindCredential(credentialItem, credential); vi.mocked(getWorkflow).mockImplementation(async () => { - expect(actions.pendingApplyCount.value).toBe(0); + expect(actions.pendingApplyCount.value).toBe(1); expect(actions.isApplying.value).toBe(true); expect(actions.getPendingCredential(credentialItem.id)).toEqual(credential); if (failed) throw new Error('Unavailable'); @@ -135,16 +165,39 @@ describe('useSetupPanelActions', () => { building.value = false; await expect(actions.flushPendingApplies()).resolves.toBe(failed ? 'error' : 'applied'); expect(actions.isApplying.value).toBe(false); - expect(actions.getPendingCredential(credentialItem.id)).toBeUndefined(); + expect(actions.getPendingCredential(credentialItem.id)).toEqual( + failed ? credential : undefined, + ); }); beforeEach(() => { + threadMetadata = ref({}); setActivePinia(createTestingPinia({ stubActions: false })); + vi.mocked(updateThreadMetadata).mockReset(); + vi.mocked(updateThreadMetadata).mockResolvedValue({ + thread: { id: 'thread-1', resourceId: 'user-1', createdAt: '', updatedAt: '' }, + }); + vi.mocked(fetchThread).mockImplementation(async () => ({ + thread: { + id: 'thread-1', + resourceId: 'user-1', + createdAt: '', + updatedAt: '', + metadata: threadMetadata.value, + }, + })); vi.mocked(getWorkflow).mockReset(); getNodeCredentialIssues.mockReset(); getNodeCredentialIssues.mockReturnValue(null); getNodeInputIssues.mockReset(); getNodeInputIssues.mockReturnValue(null); + const nodeTypes = mockedStore(useNodeTypesStore); + nodeTypes.loadNodeTypesIfNotLoaded.mockResolvedValue(undefined); + nodeTypes.getNodeType = vi + .fn() + .mockReturnValue( + mockNodeTypeDescription({ credentials: [{ name: 'slackApi', required: true }] }), + ); }); it.each([ @@ -302,16 +355,53 @@ describe('useSetupPanelActions', () => { expect(onSaved).not.toHaveBeenCalled(); }); - it('drops the bind without writing when every target node is gone', async () => { - const { actions, updateWorkflow, onSaved } = createHarness(); - vi.mocked(getWorkflow).mockResolvedValue( - makeWorkflow({ nodes: [createTestNode({ name: 'Other' })] }), - ); + it.each([false, true])( + 'retains unmatched choices when another bind is saved, node-specific: %s', + async (nodeSpecific) => { + const { actions, building, workflowNodes, savedWorkflowChecksum, updateWorkflow } = + createHarness({ agentBuilding: true }); + const nodeTypes = mockedStore(useNodeTypesStore); + nodeTypes.loadNodeTypesIfNotLoaded.mockResolvedValue(undefined); + nodeTypes.getNodeType = vi.fn((type) => + mockNodeTypeDescription({ + credentials: [{ name: type === 'other' ? 'httpBasicAuth' : 'slackApi', required: true }], + properties: [], + }), + ); + const otherNode = createTestNode({ name: 'Other', type: 'other' }); + const firstNodes = [otherNode, createTestNode({ name: 'Slack', type: 'other' })]; + workflowNodes.value = firstNodes; + vi.mocked(getWorkflow).mockResolvedValue(makeWorkflow({ nodes: firstNodes })); + await actions.bindCredential( + { ...credentialItem, nodeBindings: nodeSpecific ? credentialItem.nodeBindings : undefined }, + credential, + ); + await actions.bindCredential( + { + id: `${WORKFLOW_ID}:credential:httpBasicAuth:Other`, + kind: 'credential', + credentialType: 'httpBasicAuth', + nodeBindings: [{ nodeName: 'Other' }], + }, + { id: 'other-account', name: 'Other account' }, + ); + building.value = false; + await vi.waitFor(() => expect(actions.pendingApplyCount.value).toBe(0)); + expect(updateWorkflow).toHaveBeenCalledTimes(1); + expect(actions.getPendingCredential(credentialItem.id)).toEqual(credential); - await expect(actions.bindCredential(credentialItem, credential)).resolves.toBe('dropped'); - expect(updateWorkflow).not.toHaveBeenCalled(); - expect(onSaved).not.toHaveBeenCalled(); - }); + const nodes = [otherNode, createTestNode({ name: 'Slack' })]; + vi.mocked(getWorkflow).mockResolvedValue(makeWorkflow({ nodes, checksum: 'c3' })); + workflowNodes.value = nodes; + expect(actions.pendingApplyCount.value).toBe(1); + savedWorkflowChecksum.value = 'c3'; + await vi.waitFor(() => + expect(actions.getPendingCredential(credentialItem.id)).toBeUndefined(), + ); + expect(updateWorkflow).toHaveBeenCalledTimes(2); + expect(updateWorkflow.mock.calls[1][1].nodes[1].credentials.slackApi).toEqual(credential); + }, + ); it('skips the write when every target node already carries the credential', async () => { const { actions, updateWorkflow, onSaved } = createHarness(); @@ -467,6 +557,80 @@ describe('useSetupPanelActions', () => { expect(updateWorkflow).not.toHaveBeenCalled(); }); + it('restores an early selection after a remount and applies it when the empty artifact is saved with nodes', async () => { + const { actions, workflowId } = createHarness({ agentBuilding: true }); + await actions.bindCredential({ ...credentialItem, nodeBindings: undefined }, credential); + workflowId.value = 'wf-2'; + await nextTick(); + expect(actions.pendingApplyCount.value).toBe(0); + mockedStore(useCredentialsStore).getCredentialById = vi.fn().mockReturnValue(credential); + const restored = createHarness({ agentBuilding: true }); + expect(restored.actions.getPendingCredential(credentialItem.id)).toEqual(credential); + vi.mocked(getWorkflow).mockResolvedValue(makeWorkflow({ nodes: [] })); + restored.building.value = false; + await expect(restored.actions.flushPendingApplies()).resolves.toBe('queued'); + expect(restored.actions.pendingApplyCount.value).toBe(1); + + const nodeTypes = mockedStore(useNodeTypesStore); + nodeTypes.loadNodeTypesIfNotLoaded.mockResolvedValue(undefined); + nodeTypes.getNodeType = vi.fn().mockReturnValue({ + credentials: [{ name: 'slackApi', required: true }], + properties: [], + }); + vi.mocked(getWorkflow).mockResolvedValue(makeWorkflow({ checksum: 'c2' })); + restored.savedWorkflowChecksum.value = 'c2'; + await vi.waitFor(() => expect(restored.actions.pendingApplyCount.value).toBe(0)); + expect(restored.updateWorkflow).toHaveBeenCalledTimes(1); + expect(restored.updateWorkflow.mock.calls[0][1].nodes[0].credentials.slackApi).toEqual( + credential, + ); + }); + + it('does not queue a selection when its metadata save fails', async () => { + const { actions, updateWorkflow } = createHarness({ agentBuilding: true }); + vi.mocked(updateThreadMetadata).mockRejectedValueOnce(new Error('Unavailable')); + await expect(actions.bindCredential(credentialItem, credential)).resolves.toBe('error'); + expect(actions.getPendingCredential(credentialItem.id)).toBeUndefined(); + expect(updateWorkflow).not.toHaveBeenCalled(); + }); + + it('waits for durable selection writes and preserves their order', async () => { + const { actions } = createHarness({ agentBuilding: true }); + const firstSave = Promise.withResolvers>>(); + vi.mocked(updateThreadMetadata).mockReturnValueOnce(firstSave.promise); + const first = actions.bindCredential(credentialItem, credential); + const newer = { id: 'cred-2', name: 'Second account' }; + const second = actions.bindCredential(credentialItem, newer); + await vi.waitFor(() => expect(updateThreadMetadata).toHaveBeenCalledTimes(1)); + expect(actions.getPendingCredential(credentialItem.id)).toBeUndefined(); + firstSave.resolve({ + thread: { id: 'thread-1', resourceId: 'user-1', createdAt: '', updatedAt: '' }, + }); + await Promise.all([first, second]); + expect(actions.getPendingCredential(credentialItem.id)).toEqual(newer); + }); + + it('keeps a newer account choice when an earlier binding finishes', async () => { + const { actions, building, updateWorkflow } = createHarness({ agentBuilding: true }); + await actions.bindCredential(credentialItem, credential); + const saved = Promise.withResolvers(); + updateWorkflow.mockReturnValueOnce(saved.promise); + building.value = false; + const flush = actions.flushPendingApplies(); + await vi.waitFor(() => expect(updateWorkflow).toHaveBeenCalledTimes(1)); + building.value = true; + const newer = { id: 'cred-2', name: 'Second account' }; + await actions.bindCredential(credentialItem, newer); + saved.resolve(makeWorkflow()); + await flush; + expect(actions.getPendingCredential(credentialItem.id)).toEqual(newer); + expect( + threadMetadata.value[instanceAiSetupCredentialSelectionKey(credentialItem.id)], + ).toMatchObject({ + credentialId: newer.id, + }); + }); + it('reports a conflicted settle flush through onFlushResult', async () => { const onFlushResult = vi.fn(); const { actions, building, updateWorkflow, onSaved } = createHarness({ @@ -611,6 +775,7 @@ describe('useSetupPanelActions', () => { documentStore.hydrate(makeWorkflow()); const slackNodeType = { displayName: 'Slack', + credentials: [{ name: 'slackApi', required: true }], name: 'n8n-nodes-base.set', version: 1, group: [], diff --git a/packages/frontend/editor-ui/src/features/ai/instanceAi/__tests__/useSetupPanelState.test.ts b/packages/frontend/editor-ui/src/features/ai/instanceAi/__tests__/useSetupPanelState.test.ts index b4fbb423b0f..2b2691777bd 100644 --- a/packages/frontend/editor-ui/src/features/ai/instanceAi/__tests__/useSetupPanelState.test.ts +++ b/packages/frontend/editor-ui/src/features/ai/instanceAi/__tests__/useSetupPanelState.test.ts @@ -1,6 +1,6 @@ import { computed, reactive, ref, toValue } from 'vue'; import { describe, it, expect, vi, beforeEach } from 'vitest'; -import type { InstanceAiAgentNode, InstanceAiSetupItem } from '@n8n/api-types'; +import type { InstanceAiSetupItem } from '@n8n/api-types'; import { useWorkflowSetupItems } from '@/features/setupPanel/composables/useWorkflowSetupItems'; import { isAgentEditingWorkflow } from '../canvasPreview.utils'; import { useSetupPanelState, type SetupPanelThreadSource } from '../composables/useSetupPanelState'; @@ -32,6 +32,7 @@ function createHarness( workflowId?: string; agentEditing?: boolean; workflowAvailable?: boolean; + hasWorkflowNodes?: boolean; eventItems?: InstanceAiSetupItem[]; derivedItems?: InstanceAiSetupItem[]; } = {}, @@ -40,6 +41,7 @@ function createHarness( const { agentEditing = false, workflowAvailable = false, + hasWorkflowNodes = true, eventItems = [eventItem], derivedItems = [derivedItem], } = options; @@ -53,7 +55,11 @@ function createHarness( vi.mocked(isAgentEditingWorkflow).mockImplementation(() => editing.value); vi.mocked(useWorkflowSetupItems).mockReturnValue({ credentialsAvailable: computed(() => true), + workflowNodes: computed(() => undefined), + savedWorkflowChecksum: computed(() => undefined), + isCheckingOAuthCredentials: computed(() => false), isWorkflowAvailable: computed(() => available.value), + hasWorkflowNodes: computed(() => hasWorkflowNodes), workflowProjectId: computed(() => undefined), derivedItems: computed(() => derived.value), derivedCredentialItems: computed(() => @@ -67,7 +73,20 @@ function createHarness( }); const thread: SetupPanelThreadSource = reactive({ - messages: [{ agentTree: {} as InstanceAiAgentNode }], + messages: [ + { + agentTree: { + agentId: 'root', + role: 'orchestrator', + status: 'completed', + textContent: '', + reasoning: '', + timeline: [], + toolCalls: [], + children: [], + }, + }, + ], setupItemsByWorkflowId: { [WORKFLOW_ID]: eventItems }, }); @@ -101,6 +120,33 @@ describe('useSetupPanelState', () => { expect(state.rows.value.map((row) => row.item)).toEqual([eventItem]); }); + it('keeps early requirements after restoring an empty workflow', () => { + const { state, thread } = createHarness({ + workflowAvailable: true, + hasWorkflowNodes: false, + derivedItems: [], + }); + thread.messages[0].agentTree!.toolCalls.push({ + toolCallId: 'setup', + toolName: 'credentials', + args: { action: 'setup' }, + isLoading: false, + result: { success: true, preBuild: true, workflowId: WORKFLOW_ID }, + }); + expect(state.isAwaitingFirstBuild.value).toBe(true); + expect(state.rowSource.value).toBe('events'); + expect(state.rows.value.map((row) => row.item)).toEqual([eventItem]); + thread.messages[0].agentTree!.toolCalls.push({ + toolCallId: 'build', + toolName: 'build-workflow', + args: {}, + isLoading: false, + result: { success: true, workflowId: WORKFLOW_ID }, + }); + expect(state.rows.value).toEqual([]); + expect(state.isAwaitingFirstBuild.value).toBe(false); + }); + it('resolves event bindings and keeps the recipe as saved rows change', () => { const announcement: InstanceAiSetupItem = { ...eventItem, diff --git a/packages/frontend/editor-ui/src/features/ai/instanceAi/canvasPreview.utils.ts b/packages/frontend/editor-ui/src/features/ai/instanceAi/canvasPreview.utils.ts index cb60376fe31..ac55193313a 100644 --- a/packages/frontend/editor-ui/src/features/ai/instanceAi/canvasPreview.utils.ts +++ b/packages/frontend/editor-ui/src/features/ai/instanceAi/canvasPreview.utils.ts @@ -266,6 +266,15 @@ export function isAgentEditingWorkflow( (getLatestBuildResult(node)?.workflowId === workflowId || getLatestWorkflowSetupResult(node)?.workflowId === workflowId || getLatestWorkflowUpdateResult(node)?.workflowId === workflowId || + node.toolCalls.some( + (call) => + call.toolName === 'credentials' && + call.args?.action === 'setup' && + isRecord(call.result) && + call.result.announced === true && + call.result.preBuild === true && + call.result.workflowId === workflowId, + ) || announcedBuild) ) { return true; diff --git a/packages/frontend/editor-ui/src/features/ai/instanceAi/components/setupPanel/InstanceAiSetupCredential.vue b/packages/frontend/editor-ui/src/features/ai/instanceAi/components/setupPanel/InstanceAiSetupCredential.vue index 5f8d1b471d8..435000fbdfc 100644 --- a/packages/frontend/editor-ui/src/features/ai/instanceAi/components/setupPanel/InstanceAiSetupCredential.vue +++ b/packages/frontend/editor-ui/src/features/ai/instanceAi/components/setupPanel/InstanceAiSetupCredential.vue @@ -2,6 +2,7 @@ import { computed, onMounted, onScopeDispose, ref, watch } from 'vue'; import isEqual from 'lodash/isEqual'; import { TEMPLATED_CUSTOM_AUTH_CREDENTIAL_TYPE } from '@n8n/api-types'; +import { getResourcePermissions } from '@n8n/permissions'; import { N8nButton, N8nCallout, @@ -11,17 +12,14 @@ import { N8nSegmentControl, N8nSetupConnection, N8nText, + N8nTooltip, } from '@n8n/design-system'; import type { DropdownMenuItemProps } from '@n8n/design-system'; import { addCredentialTranslation, useI18n } from '@n8n/i18n'; import { useRootStore } from '@n8n/stores/useRootStore'; import { useToast } from '@n8n/composables/useToast'; import { useTelemetry } from '@n8n/composables/useTelemetry'; -import { - deepCopy, - DOMAIN_RESTRICTION_FIELDS, - type ICredentialDataDecryptedObject, -} from 'n8n-workflow'; +import { deepCopy, type ICredentialDataDecryptedObject } from 'n8n-workflow'; import type { INodeUi, INodeUpdatePropertiesInformation, IUpdateInformation } from '@/Interface'; import { AI_GATEWAY_UNSUPPORTED_NODE_TYPES, BUILTIN_CREDENTIALS_DOCS_URL } from '@/app/constants'; import { useUIStore } from '@/app/stores/ui.store'; @@ -34,6 +32,8 @@ import { useCredentialOAuth } from '@/features/credentials/composables/useCreden import { hasOAuthTokenData } from '@/features/credentials/composables/oauthCallback'; import { useQuickConnect } from '@/features/credentials/quickConnect/composables/useQuickConnect'; import { useCredentialsStore } from '@/features/credentials/credentials.store'; +import { groupCredentialSetupFields } from '@/features/credentials/credentialSetupFields'; +import { useEnvFeatureFlag } from '@/features/shared/envFeatureFlag/useEnvFeatureFlag'; import CredentialInputs from '@/features/credentials/components/CredentialEdit/CredentialInputs.vue'; import TemplatedAuthSimpleView from '@/features/credentials/components/CredentialEdit/TemplatedAuthSimpleView.vue'; import NodeCredentials from '@/features/credentials/components/NodeCredentials.vue'; @@ -79,6 +79,7 @@ const toast = useToast(); const telemetry = useTelemetry(); const externalHooks = useExternalHooks(); const credentialsStore = useCredentialsStore(); +const { check: envFeatureFlag } = useEnvFeatureFlag(); const oauth = useCredentialOAuth(); const quickConnect = useQuickConnect(); const gateway = useAiGateway(); @@ -87,7 +88,13 @@ const initialized = ref(false); const initializationFailed = ref(false); const busy = ref(false); const reopenAuthorization = ref<() => void>(); -const createNew = ref(false); +const createNew = ref( + Boolean( + props.item.preferNew && + !props.pendingCredential && + !props.node?.credentials?.[props.item.credentialType], + ), +); const hasDraft = ref(false); watch(busy, (value) => emit('update:busy', value)); watch( @@ -172,13 +179,17 @@ watch( selectedOAuthId, () => storedCredential.value?.updatedAt, () => storedCredential.value?.connectedByMe, + () => storedCredential.value?.scopes?.join(','), ], async ([id], _previous, onCleanup) => { let stale = false; onCleanup(() => { stale = true; }); - oauthConnection.value = undefined; + const permissions = getResourcePermissions(storedCredential.value?.scopes).credential; + oauthConnection.value = storedCredential.value?.isResolvable + ? storedCredential.value.connectedByMe + : permissions.read === true && !permissions.update; oauthMode.value = 'unknown'; loadingOAuth.value = Boolean(id); if (!id) return; @@ -201,7 +212,7 @@ watch( oauthMode.value = customClient || !form.managedOAuthAvailable.value ? 'custom' : 'managed'; } } catch { - // A shared credential can be usable without permission to read its data. + // Keep the permission-based fallback when credential data is unavailable. } finally { if (!stale) loadingOAuth.value = false; } @@ -245,35 +256,40 @@ watch( [gatewayAvailable, gateway.balance, usableCredentials], ([available, balance, credentials]) => { if (available && !modeChanged.value && !connected.value) - mode.value = (balance ?? 0) > 0 || credentials.length === 0 ? 'credits' : 'own'; + mode.value = + !props.item.preferNew && ((balance ?? 0) > 0 || credentials.length === 0) + ? 'credits' + : 'own'; }, { immediate: true }, ); -const inlineFields = computed(() => { - const fields = form.credentialProperties.value.filter( - (property) => - property.type !== 'hidden' && - property.type !== 'notice' && - !property.typeOptions?.copyButton && - !( - props.item.credentialType === 'googlePalmApi' && - property.name === 'host' && - (form.credentialData.value.host ?? property.default) === property.default - ) && - !DOMAIN_RESTRICTION_FIELDS.some(({ name }) => name === property.name), - ); - const required = fields.filter((property) => property.required); - // Older credential definitions can omit required flags even for access tokens. - const inputs = required.length ? required : fields.filter((property) => !property.default); - return inputs.length <= 2 ? inputs : []; -}); +const credentialFields = computed(() => + groupCredentialSetupFields( + props.item.credentialType, + form.parentTypes.value, + form.credentialProperties.value.filter( + (property) => !property.envFeatureFlag || envFeatureFlag.value(property.envFeatureFlag), + ), + ), +); +const helpFields = computed(() => + credentialFields.value.inline.filter( + (property) => property.type !== 'notice' && !property.typeOptions?.copyButton, + ), +); const fieldTitles = computed(() => isTemplated.value ? listPlaceholderTitles(form.credentialData.value) - : inlineFields.value.map((property) => property.displayName), + : helpFields.value.map((property) => property.displayName), +); +const useAdvancedForm = computed( + () => + !canQuickConnect.value && + (isTemplated.value + ? fieldTitles.value.length === 0 + : credentialFields.value.inline.length === 0), ); -const useAdvancedForm = computed(() => !canQuickConnect.value && fieldTitles.value.length === 0); const advancedIsPrimary = computed(() => isTemplated.value && useAdvancedForm.value); const valueLabel = computed(() => binding.value?.__aiGatewayManaged @@ -375,7 +391,7 @@ const actions = computed(() => { }); const helpLabel = computed(() => { - const fieldName = inlineFields.value.length === 1 ? inlineFields.value[0].name : undefined; + const fieldName = helpFields.value.length === 1 ? helpFields.value[0].name : undefined; return i18n.baseText( fieldName === 'apiKey' ? 'instanceAi.setupPanel.helpFindApiKey' @@ -763,17 +779,25 @@ onScopeDispose(() => { {{ value }}