From 09c8b2dea89295dad66bec7c33f422c8e0a086ed Mon Sep 17 00:00:00 2001 From: Danny Martini Date: Mon, 17 Nov 2025 10:40:43 +0100 Subject: [PATCH] fix(core): Add timeout and recovery for database connection health checks (#21506) Co-authored-by: Declan Carroll --- .../__tests__/db-connection.test.ts | 12 +- .../@n8n/db/src/connection/db-connection.ts | 27 ++- packages/cli/src/abstract-server.ts | 6 +- .../n8n-test-container-dependencies.ts | 1 + .../containers/n8n-test-container-helpers.ts | 27 ++- packages/testing/playwright/package.json | 1 + .../testing/playwright/services/api-helper.ts | 12 +- .../playwright/tests/chaos/multimain.spec.ts | 6 +- .../playwright/tests/chaos/postgres.spec.ts | 65 +++++++ pnpm-lock.yaml | 177 ++++++++++-------- 10 files changed, 241 insertions(+), 93 deletions(-) create mode 100644 packages/testing/playwright/tests/chaos/postgres.spec.ts diff --git a/packages/@n8n/db/src/connection/__tests__/db-connection.test.ts b/packages/@n8n/db/src/connection/__tests__/db-connection.test.ts index 239fa772a43..2addb2c7b29 100644 --- a/packages/@n8n/db/src/connection/__tests__/db-connection.test.ts +++ b/packages/@n8n/db/src/connection/__tests__/db-connection.test.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/unbound-method */ +import type { Logger } from '@n8n/backend-common'; import type { DatabaseConfig } from '@n8n/config'; import { DataSource, type DataSourceOptions } from '@n8n/typeorm'; import { mock, mockDeep } from 'jest-mock-extended'; @@ -9,7 +11,9 @@ import type { Migration } from '../../migrations/migration-types'; import { DbConnection } from '../db-connection'; import type { DbConnectionOptions } from '../db-connection-options'; +// eslint-disable-next-line @typescript-eslint/no-unsafe-return jest.mock('@n8n/typeorm', () => ({ + // eslint-disable-next-line @typescript-eslint/naming-convention DataSource: jest.fn(), ...jest.requireActual('@n8n/typeorm'), })); @@ -19,6 +23,7 @@ describe('DbConnection', () => { const migrations = [{ name: 'TestMigration1' }, { name: 'TestMigration2' }] as Migration[]; const errorReporter = mock(); const databaseConfig = mock(); + const logger = mock(); const dataSource = mockDeep({ options: { migrations } }); const connectionOptions = mockDeep(); const postgresOptions: DataSourceOptions = { @@ -37,7 +42,7 @@ describe('DbConnection', () => { connectionOptions.getOptions.mockReturnValue(postgresOptions); (DataSource as jest.Mock) = jest.fn().mockImplementation(() => dataSource); - dbConnection = new DbConnection(errorReporter, connectionOptions, databaseConfig); + dbConnection = new DbConnection(errorReporter, connectionOptions, databaseConfig, logger); }); describe('init', () => { @@ -133,6 +138,7 @@ describe('DbConnection', () => { it('should update connection state on successful ping', async () => { // @ts-expect-error readonly property dataSource.isInitialized = true; + // eslint-disable-next-line @typescript-eslint/naming-convention dataSource.query.mockResolvedValue([{ '1': 1 }]); dbConnection.connectionState.connected = false; @@ -158,6 +164,7 @@ describe('DbConnection', () => { it('should schedule next ping after execution', async () => { // @ts-expect-error readonly property dataSource.isInitialized = true; + // eslint-disable-next-line @typescript-eslint/naming-convention dataSource.query.mockResolvedValue([{ '1': 1 }]); // eslint-disable-next-line @typescript-eslint/no-explicit-any const scheduleNextPingSpy = jest.spyOn(dbConnection as any, 'scheduleNextPing'); @@ -178,7 +185,7 @@ describe('DbConnection', () => { expect(dataSource.query).not.toHaveBeenCalled(); }); - it('should execute ping on schedule', async () => { + it('should execute ping on schedule', () => { jest.useFakeTimers(); try { // ARRANGE @@ -188,6 +195,7 @@ describe('DbConnection', () => { mock({ pingIntervalSeconds: 1, }), + logger, ); // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/@n8n/db/src/connection/db-connection.ts b/packages/@n8n/db/src/connection/db-connection.ts index 91e7bd27d8f..3bebdd37b30 100644 --- a/packages/@n8n/db/src/connection/db-connection.ts +++ b/packages/@n8n/db/src/connection/db-connection.ts @@ -1,11 +1,12 @@ -import { inTest } from '@n8n/backend-common'; +import { inTest, Logger } from '@n8n/backend-common'; import { DatabaseConfig } from '@n8n/config'; import { Time } from '@n8n/constants'; import { Memoized } from '@n8n/decorators'; import { Container, Service } from '@n8n/di'; import { DataSource } from '@n8n/typeorm'; import { ErrorReporter } from 'n8n-core'; -import { DbConnectionTimeoutError, ensureError } from 'n8n-workflow'; +import { DbConnectionTimeoutError, ensureError, OperationalError } from 'n8n-workflow'; +import { setTimeout as setTimeoutP } from 'timers/promises'; import { DbConnectionOptions } from './db-connection-options'; import { wrapMigration } from '../migrations/migration-helpers'; @@ -31,6 +32,7 @@ export class DbConnection { private readonly errorReporter: ErrorReporter, private readonly connectionOptions: DbConnectionOptions, private readonly databaseConfig: DatabaseConfig, + private readonly logger: Logger, ) { this.dataSource = new DataSource(this.options); Container.set(DataSource, this.dataSource); @@ -93,14 +95,31 @@ export class DbConnection { private async ping() { if (!this.dataSource.isInitialized) return; + const abortController = new AbortController(); + try { - await this.dataSource.query('SELECT 1'); + await Promise.race([ + this.dataSource.query('SELECT 1'), + setTimeoutP(5000, undefined, { signal: abortController.signal }).then(() => { + throw new OperationalError('Database connection timed out'); + }), + ]); + + if (!this.connectionState.connected) { + this.logger.info('Database connection recovered'); + } + this.connectionState.connected = true; return; } catch (error) { this.connectionState.connected = false; - this.errorReporter.error(error); + if (error instanceof OperationalError) { + this.logger.warn(error.message); + } else { + this.errorReporter.error(error); + } } finally { + abortController.abort(); this.scheduleNextPing(); } } diff --git a/packages/cli/src/abstract-server.ts b/packages/cli/src/abstract-server.ts index 47bd906781c..2eb169dc14c 100644 --- a/packages/cli/src/abstract-server.ts +++ b/packages/cli/src/abstract-server.ts @@ -121,7 +121,7 @@ export abstract class AbstractServer { protected setupPushServer() {} - private async setupHealthCheck() { + private setupHealthCheck() { // main health check should not care about DB connections this.app.get('/healthz', (_req, res) => { res.send({ status: 'ok' }); @@ -200,7 +200,7 @@ export abstract class AbstractServer { this.externalHooks = Container.get(ExternalHooks); - await this.setupHealthCheck(); + this.setupHealthCheck(); this.logger.info(`n8n ready on ${address}, port ${port}`); } @@ -296,7 +296,7 @@ export abstract class AbstractServer { * then closes them forcefully. */ @OnShutdown() - async onShutdown(): Promise { + onShutdown(): void { if (!this.server) { return; } diff --git a/packages/testing/containers/n8n-test-container-dependencies.ts b/packages/testing/containers/n8n-test-container-dependencies.ts index f41f8a95ded..9cef1dcce86 100644 --- a/packages/testing/containers/n8n-test-container-dependencies.ts +++ b/packages/testing/containers/n8n-test-container-dependencies.ts @@ -53,6 +53,7 @@ export async function setupPostgres({ 'com.docker.compose.service': 'postgres', }) .withName(`${projectName}-postgres`) + .withAddedCapabilities('NET_ADMIN') // Allows us to drop IP tables and block traffic .withReuse() .start(); diff --git a/packages/testing/containers/n8n-test-container-helpers.ts b/packages/testing/containers/n8n-test-container-helpers.ts index 86241e18811..62918bab673 100644 --- a/packages/testing/containers/n8n-test-container-helpers.ts +++ b/packages/testing/containers/n8n-test-container-helpers.ts @@ -23,6 +23,7 @@ interface WaitForLogOptions { namePattern?: string | RegExp; timeoutMs?: number; caseSensitive?: boolean; + throwOnTimeout?: boolean; } interface StreamLogMatch { @@ -71,15 +72,19 @@ export class ContainerTestHelpers { /** * Wait for a log message matching pattern (case-insensitive by default) * Uses streaming approach for immediate detection + * + * @returns LogMatch if found, null if timeout reached and throwOnTimeout is false + * @throws Error if timeout reached and throwOnTimeout is true (default) */ async waitForLog( messagePattern: string | RegExp, options: WaitForLogOptions = {}, - ): Promise { + ): Promise { const { namePattern, timeoutMs = ContainerTestHelpers.DEFAULT_TIMEOUT_MS, caseSensitive = false, + throwOnTimeout = true, } = options; const messageRegex = this.createRegex(messagePattern, caseSensitive); @@ -87,7 +92,7 @@ export class ContainerTestHelpers { const startTime = Date.now(); console.log( - `🔍 Waiting for log pattern: ${messageRegex} in ${targetContainers.length} containers (timeout: ${timeoutMs}ms)`, + `🔍 Waiting for log pattern: ${messageRegex} in ${targetContainers.length} containers (timeout: ${timeoutMs}ms, throwOnTimeout: ${throwOnTimeout})`, ); // First check: scan existing logs quickly @@ -98,7 +103,13 @@ export class ContainerTestHelpers { } // Monitor new logs with streaming approach - return await this.pollForNewLogs(targetContainers, messageRegex, startTime, timeoutMs); + return await this.pollForNewLogs( + targetContainers, + messageRegex, + startTime, + timeoutMs, + throwOnTimeout, + ); } /** @@ -134,7 +145,8 @@ export class ContainerTestHelpers { messageRegex: RegExp, startTime: number, timeoutMs: number, - ): Promise { + throwOnTimeout: boolean, + ): Promise { let currentCheckTime = Math.floor(Date.now() / 1000); let iteration = 0; @@ -170,7 +182,12 @@ export class ContainerTestHelpers { } console.log(`❌ Timeout reached after ${timeoutMs}ms`); - throw new Error(`Timeout reached after ${timeoutMs}ms`); + + if (throwOnTimeout) { + throw new Error(`Timeout reached after ${timeoutMs}ms`); + } + + return null; } /** diff --git a/packages/testing/playwright/package.json b/packages/testing/playwright/package.json index cbf14f08684..e84024084c2 100644 --- a/packages/testing/playwright/package.json +++ b/packages/testing/playwright/package.json @@ -29,6 +29,7 @@ "devDependencies": { "@currents/playwright": "^1.15.3", "@n8n/api-types": "workspace:^", + "@n8n/constants": "workspace:^", "@n8n/db": "workspace:*", "@playwright/test": "1.56.0", "@types/lodash": "catalog:", diff --git a/packages/testing/playwright/services/api-helper.ts b/packages/testing/playwright/services/api-helper.ts index 65dafe1be7b..cb8f303e147 100644 --- a/packages/testing/playwright/services/api-helper.ts +++ b/packages/testing/playwright/services/api-helper.ts @@ -217,11 +217,21 @@ export class ApiHelpers { async get(path: string, params?: URLSearchParams) { const response = await this.request.get(path, { params }); - const { data } = await response.json(); return data; } + /** + * Check if n8n is healthy + * @returns True if n8n is healthy, false otherwise + */ + async isHealthy(probe: 'liveness' | 'readiness' = 'liveness'): Promise { + const url = probe === 'liveness' ? '/healthz' : '/healthz/readiness'; + const response = await this.request.get(url); + const data = await response.json(); + return data.status === 'ok'; + } + // ===== PRIVATE METHODS ===== private async loginAndSetCookies( diff --git a/packages/testing/playwright/tests/chaos/multimain.spec.ts b/packages/testing/playwright/tests/chaos/multimain.spec.ts index ca739a418eb..d1325ba274e 100644 --- a/packages/testing/playwright/tests/chaos/multimain.spec.ts +++ b/packages/testing/playwright/tests/chaos/multimain.spec.ts @@ -8,10 +8,10 @@ test('Leader election @mode:multi-main @chaostest', async ({ chaos }) => { namePattern, }); - expect(findContainerByLog).toBeDefined(); - const currentLeader = findContainerByLog.containerName; + expect(findContainerByLog, 'Leader should be found').toBeDefined(); + const currentLeader = findContainerByLog?.containerName; // Stop leader - await chaos.stopContainer(currentLeader); + await chaos.stopContainer(currentLeader!); // Find new leader const newLeader = await chaos.waitForLog('Leader is now this', { diff --git a/packages/testing/playwright/tests/chaos/postgres.spec.ts b/packages/testing/playwright/tests/chaos/postgres.spec.ts new file mode 100644 index 00000000000..719f8104777 --- /dev/null +++ b/packages/testing/playwright/tests/chaos/postgres.spec.ts @@ -0,0 +1,65 @@ +import { Time } from '@n8n/constants'; + +import { test, expect } from '../../fixtures/base'; + +test( + 'Database connection timeout health check bug @mode:postgres @chaostest', + { + annotation: { + type: 'issue', + description: 'CAT-1018', + }, + }, + async ({ api, chaos }) => { + test.setTimeout(300000); + + // ========== SETUP: Verify Initial Health ========== + // Ensure n8n starts in a healthy state before we begin chaos testing + { + const isLive = await api.isHealthy('liveness'); + const isReady = await api.isHealthy('readiness'); + expect(isLive).toBe(true); + expect(isReady).toBe(true); + } + + // ========== CHAOS INJECTION: Block Database Traffic ========== + // Find postgres container and install iptables to simulate network issues + const postgres = chaos.findContainers('postgres*')[0]; + + // Install iptables in the postgres container (Alpine Linux) + const apkUpdate = await postgres.exec(['apk', 'update']); + const apkInstall = await postgres.exec(['apk', 'add', 'iptables']); + // Block all incoming TCP traffic to PostgreSQL port 5432 + // This simulates a network partition between n8n and the database + const rule = ['INPUT', '-p', 'tcp', '--dport', '5432', '-j', 'DROP']; + const blockPostgresTraffic = await postgres.exec(['iptables', '-A', ...rule]); + expect(apkUpdate.exitCode).toBe(0); + expect(apkInstall.exitCode).toBe(0); + expect(blockPostgresTraffic.exitCode).toBe(0); + + // ========== WAIT FOR CONNECTION ISSUES ========== + await chaos.waitForLog('Database connection timed out', { + namePattern: 'n8n-*', + timeoutMs: 20 * Time.seconds.toMilliseconds, + }); + + // ========== VERIFY: Health Checks ========== + { + const isLive = await api.isHealthy('liveness'); + const isReady = await api.isHealthy('readiness'); + expect(isLive).toBe(true); + expect(isReady).toBe(false); + } + + // ========== RESTORE DATABASE CONNECTION ========== + // Remove the iptables rule to allow traffic again + const allowPostgresTraffic = await postgres.exec(['iptables', '-D', ...rule]); + expect(allowPostgresTraffic.exitCode).toBe(0); + + // ========== VERIFY: Automatic Recovery ========== + await chaos.waitForLog('Database connection recovered', { + namePattern: 'n8n-*', + timeoutMs: 20 * Time.seconds.toMilliseconds, + }); + }, +); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1fd1d186d36..712a2120f98 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3219,6 +3219,9 @@ importers: '@n8n/api-types': specifier: workspace:^ version: link:../../@n8n/api-types + '@n8n/constants': + specifier: workspace:^ + version: link:../../@n8n/constants '@n8n/db': specifier: workspace:* version: link:../../@n8n/db @@ -6559,20 +6562,16 @@ packages: '@otplib/preset-v11@12.0.1': resolution: {integrity: sha512-9hSetMI7ECqbFiKICrNa4w70deTUfArtwXykPUvSHWOdzOlfa9ajglu7mNCntlvxycTiOAXkQGwjQCzzDEMRMg==} - '@oxc-project/runtime@0.71.0': - resolution: {integrity: sha512-QwoF5WUXIGFQ+hSxWEib4U/aeLoiDN9JlP18MnBgx9LLPRDfn1iICtcow7Jgey6HLH4XFceWXQD5WBJ39dyJcw==} - engines: {node: '>=6.9.0'} - '@oxc-project/runtime@0.92.0': resolution: {integrity: sha512-Z7x2dZOmznihvdvCvLKMl+nswtOSVxS2H2ocar+U9xx6iMfTp0VGIrX6a4xB1v80IwOPC7dT1LXIJrY70Xu3Jw==} engines: {node: ^20.19.0 || >=22.12.0} - '@oxc-project/types@0.71.0': - resolution: {integrity: sha512-5CwQ4MI+P4MQbjLWXgNurA+igGwu/opNetIE13LBs9+V93R64MLvDKOOLZIXSzEfovU3Zef3q3GjPnMTgJTn2w==} - '@oxc-project/types@0.94.0': resolution: {integrity: sha512-+UgQT/4o59cZfH6Cp7G0hwmqEQ0wE+AdIwhikdwnhWI9Dp8CgSY081+Q3O67/wq3VJu8mgUEB93J9EHHn70fOw==} + '@oxc-project/types@0.96.0': + resolution: {integrity: sha512-r/xkmoXA0xEpU6UGtn18CNVjXH6erU3KCpCDbpLmbVxBFor1U9MqN5Z2uMmCHJuXjJzlnDR+hWY+yPoLo8oHDw==} + '@paralleldrive/cuid2@2.2.2': resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==} @@ -6790,14 +6789,21 @@ packages: cpu: [arm64] os: [android] + '@rolldown/binding-android-arm64@1.0.0-beta.47': + resolution: {integrity: sha512-vPP9/MZzESh9QtmvQYojXP/midjgkkc1E4AdnPPAzQXo668ncHJcVLKjJKzoBdsQmaIvNjrMdsCwES8vTQHRQw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + '@rolldown/binding-darwin-arm64@1.0.0-beta.42': resolution: {integrity: sha512-abw/wtgJA8OCgaTlL+xJxnN/Z01BwV1rfzIp5Hh9x+IIO6xOBfPsQ0nzi0+rWx3TyZ9FZXyC7bbC+5NpQ9EaXQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-arm64@1.0.0-beta.9-commit.d91dfb5': - resolution: {integrity: sha512-Mp0/gqiPdepHjjVm7e0yL1acWvI0rJVVFQEADSezvAjon9sjQ7CEg9JnXICD4B1YrPmN9qV/e7cQZCp87tTV4w==} + '@rolldown/binding-darwin-arm64@1.0.0-beta.47': + resolution: {integrity: sha512-Lc3nrkxeaDVCVl8qR3qoxh6ltDZfkQ98j5vwIr5ALPkgjZtDK4BGCrrBoLpGVMg+csWcaqUbwbKwH5yvVa0oOw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] @@ -6807,8 +6813,9 @@ packages: cpu: [x64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-beta.9-commit.d91dfb5': - resolution: {integrity: sha512-40re4rMNrsi57oavRzIOpRGmg3QRlW6Ea8Q3znaqgOuJuKVrrm2bIQInTfkZJG7a4/5YMX7T951d0+toGLTdCA==} + '@rolldown/binding-darwin-x64@1.0.0-beta.47': + resolution: {integrity: sha512-eBYxQDwP0O33plqNVqOtUHqRiSYVneAknviM5XMawke3mwMuVlAsohtOqEjbCEl/Loi/FWdVeks5WkqAkzkYWQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] @@ -6818,8 +6825,9 @@ packages: cpu: [x64] os: [freebsd] - '@rolldown/binding-freebsd-x64@1.0.0-beta.9-commit.d91dfb5': - resolution: {integrity: sha512-8BDM939bbMariZupiHp3OmP5N+LXPT4mULA0hZjDaq970PCxv4krZOSMG+HkWUUwmuQROtV+/00xw39EO0P+8g==} + '@rolldown/binding-freebsd-x64@1.0.0-beta.47': + resolution: {integrity: sha512-Ns+kgp2+1Iq/44bY/Z30DETUSiHY7ZuqaOgD5bHVW++8vme9rdiWsN4yG4rRPXkdgzjvQ9TDHmZZKfY4/G11AA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] @@ -6829,8 +6837,9 @@ packages: cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.9-commit.d91dfb5': - resolution: {integrity: sha512-sntsPaPgrECpBB/+2xrQzVUt0r493TMPI+4kWRMhvMsmrxOqH1Ep5lM0Wua/ZdbfZNwm1aVa5pcESQfNfM4Fhw==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.47': + resolution: {integrity: sha512-4PecgWCJhTA2EFOlptYJiNyVP2MrVP4cWdndpOu3WmXqWqZUmSubhb4YUAIxAxnXATlGjC1WjxNPhV7ZllNgdA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] @@ -6841,8 +6850,9 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.9-commit.d91dfb5': - resolution: {integrity: sha512-5clBW/I+er9F2uM1OFjJFWX86y7Lcy0M+NqsN4s3o07W+8467Zk8oQa4B45vdaXoNUF/yqIAgKkA/OEdQDxZqA==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.47': + resolution: {integrity: sha512-CyIunZ6D9U9Xg94roQI1INt/bLkOpPsZjZZkiaAZ0r6uccQdICmC99M9RUPlMLw/qg4yEWLlQhG73W/mG437NA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] @@ -6854,8 +6864,9 @@ packages: os: [linux] libc: [musl] - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.9-commit.d91dfb5': - resolution: {integrity: sha512-wv+rnAfQDk9p/CheX8/Kmqk2o1WaFa4xhWI9gOyDMk/ljvOX0u0ubeM8nI1Qfox7Tnh71eV5AjzSePXUhFOyOg==} + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.47': + resolution: {integrity: sha512-doozc/Goe7qRCSnzfJbFINTHsMktqmZQmweull6hsZZ9sjNWQ6BWQnbvOlfZJe4xE5NxM1NhPnY5Giqnl3ZrYQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] @@ -6867,8 +6878,9 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.9-commit.d91dfb5': - resolution: {integrity: sha512-gxD0/xhU4Py47IH3bKZbWtvB99tMkUPGPJFRfSc5UB9Osoje0l0j1PPbxpUtXIELurYCqwLBKXIMTQGifox1BQ==} + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.47': + resolution: {integrity: sha512-fodvSMf6Aqwa0wEUSTPewmmZOD44rc5Tpr5p9NkwQ6W1SSpUKzD3SwpJIgANDOhwiYhDuiIaYPGB7Ujkx1q0UQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] @@ -6880,8 +6892,9 @@ packages: os: [linux] libc: [musl] - '@rolldown/binding-linux-x64-musl@1.0.0-beta.9-commit.d91dfb5': - resolution: {integrity: sha512-HotuVe3XUjDwqqEMbm3o3IRkP9gdm8raY/btd/6KE3JGLF/cv4+3ff1l6nOhAZI8wulWDPEXPtE7v+HQEaTXnA==} + '@rolldown/binding-linux-x64-musl@1.0.0-beta.47': + resolution: {integrity: sha512-Rxm5hYc0mGjwLh5sjlGmMygxAaV2gnsx7CNm2lsb47oyt5UQyPDZf3GP/ct8BEcwuikdqzsrrlIp8+kCSvMFNQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] @@ -6892,14 +6905,20 @@ packages: cpu: [arm64] os: [openharmony] + '@rolldown/binding-openharmony-arm64@1.0.0-beta.47': + resolution: {integrity: sha512-YakuVe+Gc87jjxazBL34hbr8RJpRuFBhun7NEqoChVDlH5FLhLXjAPHqZd990TVGVNkemourf817Z8u2fONS8w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + '@rolldown/binding-wasm32-wasi@1.0.0-beta.42': resolution: {integrity: sha512-2Ft32F7uiDTrGZUKws6CLNTlvTWHC33l4vpXrzUucf9rYtUThAdPCOt89Pmn13tNX6AulxjGEP2R0nZjTSW3eQ==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@rolldown/binding-wasm32-wasi@1.0.0-beta.9-commit.d91dfb5': - resolution: {integrity: sha512-8Cx+ucbd8n2dIr21FqBh6rUvTVL0uTgEtKR7l+MUZ5BgY4dFh1e4mPVX8oqmoYwOxBiXrsD2JIOCz4AyKLKxWA==} - engines: {node: '>=14.21.3'} + '@rolldown/binding-wasm32-wasi@1.0.0-beta.47': + resolution: {integrity: sha512-ak2GvTFQz3UAOw8cuQq8pWE+TNygQB6O47rMhvevvTzETh7VkHRFtRUwJynX5hwzFvQMP6G0az5JrBGuwaMwYQ==} + engines: {node: '>=14.0.0'} cpu: [wasm32] '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.42': @@ -6908,8 +6927,9 @@ packages: cpu: [arm64] os: [win32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.9-commit.d91dfb5': - resolution: {integrity: sha512-Vhq5vikrVDxAa75fxsyqj0c0Y/uti/TwshXI71Xb8IeUQJOBnmLUsn5dgYf5ljpYYkNa0z9BPAvUDIDMmyDi+w==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.47': + resolution: {integrity: sha512-o5BpmBnXU+Cj+9+ndMcdKjhZlPb79dVPBZnWwMnI4RlNSSq5yOvFZqvfPYbyacvnW03Na4n5XXQAPhu3RydZ0w==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] @@ -6919,8 +6939,9 @@ packages: cpu: [ia32] os: [win32] - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.9-commit.d91dfb5': - resolution: {integrity: sha512-lN7RIg9Iugn08zP2aZN9y/MIdG8iOOCE93M1UrFlrxMTqPf8X+fDzmR/OKhTSd1A2pYNipZHjyTcb5H8kyQSow==} + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.47': + resolution: {integrity: sha512-FVOmfyYehNE92IfC9Kgs913UerDog2M1m+FADJypKz0gmRg3UyTt4o1cZMCAl7MiR89JpM9jegNO1nXuP1w1vw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] @@ -6930,16 +6951,17 @@ packages: cpu: [x64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.9-commit.d91dfb5': - resolution: {integrity: sha512-7/7cLIn48Y+EpQ4CePvf8reFl63F15yPUlg4ZAhl+RXJIfydkdak1WD8Ir3AwAO+bJBXzrfNL+XQbxm0mcQZmw==} + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.47': + resolution: {integrity: sha512-by/70F13IUE101Bat0oeH8miwWX5mhMFPk1yjCdxoTNHTyTdLgb0THNaebRM6AP7Kz+O3O2qx87sruYuF5UxHg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] '@rolldown/pluginutils@1.0.0-beta.42': resolution: {integrity: sha512-N7pQzk9CyE7q0bBN/q0J8s6Db279r5kUZc6d7/wWRe9/zXqC52HQovVyu6iXPIDY4BEzzgbVLhVFXrOuGJ22ZQ==} - '@rolldown/pluginutils@1.0.0-beta.9-commit.d91dfb5': - resolution: {integrity: sha512-8sExkWRK+zVybw3+2/kBkYBFeLnEUWz1fT7BLHplpzmtqkOfTbAQ9gkt4pzwGIIZmg4Qn5US5ACjUBenrhezwQ==} + '@rolldown/pluginutils@1.0.0-beta.47': + resolution: {integrity: sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==} '@rollup/plugin-inject@5.0.5': resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} @@ -15841,8 +15863,9 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rolldown@1.0.0-beta.9-commit.d91dfb5: - resolution: {integrity: sha512-FHkj6gGEiEgmAXQchglofvUUdwj2Oiw603Rs+zgFAnn9Cb7T7z3fiaEc0DbN3ja4wYkW6sF2rzMEtC1V4BGx/g==} + rolldown@1.0.0-beta.47: + resolution: {integrity: sha512-Mid74GckX1OeFAOYz9KuXeWYhq3xkXbMziYIC+ULVdUzPTG9y70OBSBQDQn9hQP8u/AfhuYw1R0BSg15nBI4Dg==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true rollup@4.49.0: @@ -22501,14 +22524,12 @@ snapshots: '@otplib/plugin-crypto': 12.0.1 '@otplib/plugin-thirty-two': 12.0.1 - '@oxc-project/runtime@0.71.0': {} - '@oxc-project/runtime@0.92.0': {} - '@oxc-project/types@0.71.0': {} - '@oxc-project/types@0.94.0': {} + '@oxc-project/types@0.96.0': {} + '@paralleldrive/cuid2@2.2.2': dependencies: '@noble/hashes': 1.8.0 @@ -22720,88 +22741,94 @@ snapshots: '@rolldown/binding-android-arm64@1.0.0-beta.42': optional: true + '@rolldown/binding-android-arm64@1.0.0-beta.47': + optional: true + '@rolldown/binding-darwin-arm64@1.0.0-beta.42': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-beta.9-commit.d91dfb5': + '@rolldown/binding-darwin-arm64@1.0.0-beta.47': optional: true '@rolldown/binding-darwin-x64@1.0.0-beta.42': optional: true - '@rolldown/binding-darwin-x64@1.0.0-beta.9-commit.d91dfb5': + '@rolldown/binding-darwin-x64@1.0.0-beta.47': optional: true '@rolldown/binding-freebsd-x64@1.0.0-beta.42': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-beta.9-commit.d91dfb5': + '@rolldown/binding-freebsd-x64@1.0.0-beta.47': optional: true '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.42': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.9-commit.d91dfb5': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.47': optional: true '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.42': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.9-commit.d91dfb5': + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.47': optional: true '@rolldown/binding-linux-arm64-musl@1.0.0-beta.42': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.9-commit.d91dfb5': + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.47': optional: true '@rolldown/binding-linux-x64-gnu@1.0.0-beta.42': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.9-commit.d91dfb5': + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.47': optional: true '@rolldown/binding-linux-x64-musl@1.0.0-beta.42': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-beta.9-commit.d91dfb5': + '@rolldown/binding-linux-x64-musl@1.0.0-beta.47': optional: true '@rolldown/binding-openharmony-arm64@1.0.0-beta.42': optional: true + '@rolldown/binding-openharmony-arm64@1.0.0-beta.47': + optional: true + '@rolldown/binding-wasm32-wasi@1.0.0-beta.42': dependencies: '@napi-rs/wasm-runtime': 1.0.7 optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-beta.9-commit.d91dfb5': + '@rolldown/binding-wasm32-wasi@1.0.0-beta.47': dependencies: - '@napi-rs/wasm-runtime': 0.2.11 + '@napi-rs/wasm-runtime': 1.0.7 optional: true '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.42': optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.9-commit.d91dfb5': + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.47': optional: true '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.42': optional: true - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.9-commit.d91dfb5': + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.47': optional: true '@rolldown/binding-win32-x64-msvc@1.0.0-beta.42': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.9-commit.d91dfb5': + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.47': optional: true '@rolldown/pluginutils@1.0.0-beta.42': {} - '@rolldown/pluginutils@1.0.0-beta.9-commit.d91dfb5': {} + '@rolldown/pluginutils@1.0.0-beta.47': {} '@rollup/plugin-inject@5.0.5(rollup@4.52.4)': dependencies: @@ -33719,7 +33746,7 @@ snapshots: rndm@1.2.0: {} - rolldown-plugin-dts@0.16.11(rolldown@1.0.0-beta.9-commit.d91dfb5)(typescript@5.9.2)(vue-tsc@2.2.8(patch_hash=e2aee939ccac8a57fe449bfd92bedd8117841579526217bc39aca26c6b8c317f)(typescript@5.9.2)): + rolldown-plugin-dts@0.16.11(rolldown@1.0.0-beta.47)(typescript@5.9.2)(vue-tsc@2.2.8(patch_hash=e2aee939ccac8a57fe449bfd92bedd8117841579526217bc39aca26c6b8c317f)(typescript@5.9.2)): dependencies: '@babel/generator': 7.28.3 '@babel/parser': 7.28.4 @@ -33730,7 +33757,7 @@ snapshots: dts-resolver: 2.1.2 get-tsconfig: 4.10.1 magic-string: 0.30.19 - rolldown: 1.0.0-beta.9-commit.d91dfb5 + rolldown: 1.0.0-beta.47 optionalDependencies: typescript: 5.9.2 vue-tsc: 2.2.8(patch_hash=e2aee939ccac8a57fe449bfd92bedd8117841579526217bc39aca26c6b8c317f)(typescript@5.9.2) @@ -33777,25 +33804,25 @@ snapshots: '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.42 '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.42 - rolldown@1.0.0-beta.9-commit.d91dfb5: + rolldown@1.0.0-beta.47: dependencies: - '@oxc-project/runtime': 0.71.0 - '@oxc-project/types': 0.71.0 - '@rolldown/pluginutils': 1.0.0-beta.9-commit.d91dfb5 - ansis: 4.2.0 + '@oxc-project/types': 0.96.0 + '@rolldown/pluginutils': 1.0.0-beta.47 optionalDependencies: - '@rolldown/binding-darwin-arm64': 1.0.0-beta.9-commit.d91dfb5 - '@rolldown/binding-darwin-x64': 1.0.0-beta.9-commit.d91dfb5 - '@rolldown/binding-freebsd-x64': 1.0.0-beta.9-commit.d91dfb5 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.9-commit.d91dfb5 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.9-commit.d91dfb5 - '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.9-commit.d91dfb5 - '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.9-commit.d91dfb5 - '@rolldown/binding-linux-x64-musl': 1.0.0-beta.9-commit.d91dfb5 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.9-commit.d91dfb5 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.9-commit.d91dfb5 - '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.9-commit.d91dfb5 - '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.9-commit.d91dfb5 + '@rolldown/binding-android-arm64': 1.0.0-beta.47 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.47 + '@rolldown/binding-darwin-x64': 1.0.0-beta.47 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.47 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.47 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.47 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.47 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.47 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.47 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.47 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.47 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.47 + '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.47 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.47 rollup@4.49.0: dependencies: @@ -35319,8 +35346,8 @@ snapshots: diff: 8.0.2 empathic: 2.0.0 hookable: 5.5.3 - rolldown: 1.0.0-beta.9-commit.d91dfb5 - rolldown-plugin-dts: 0.16.11(rolldown@1.0.0-beta.9-commit.d91dfb5)(typescript@5.9.2)(vue-tsc@2.2.8(patch_hash=e2aee939ccac8a57fe449bfd92bedd8117841579526217bc39aca26c6b8c317f)(typescript@5.9.2)) + rolldown: 1.0.0-beta.47 + rolldown-plugin-dts: 0.16.11(rolldown@1.0.0-beta.47)(typescript@5.9.2)(vue-tsc@2.2.8(patch_hash=e2aee939ccac8a57fe449bfd92bedd8117841579526217bc39aca26c6b8c317f)(typescript@5.9.2)) semver: 7.7.2 tinyexec: 1.0.1 tinyglobby: 0.2.15