fix(core): Add timeout and recovery for database connection health checks (#21506)

Co-authored-by: Declan Carroll <declan@n8n.io>
This commit is contained in:
Danny Martini 2025-11-17 10:40:43 +01:00 committed by GitHub
parent ec5e17ff4b
commit 09c8b2dea8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 241 additions and 93 deletions

View File

@ -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<ErrorReporter>();
const databaseConfig = mock<DatabaseConfig>();
const logger = mock<Logger>();
const dataSource = mockDeep<DataSource>({ options: { migrations } });
const connectionOptions = mockDeep<DbConnectionOptions>();
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<DatabaseConfig>({
pingIntervalSeconds: 1,
}),
logger,
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any

View File

@ -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();
}
}

View File

@ -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<void> {
onShutdown(): void {
if (!this.server) {
return;
}

View File

@ -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();

View File

@ -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<LogMatch> {
): Promise<LogMatch | null> {
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<LogMatch> {
throwOnTimeout: boolean,
): Promise<LogMatch | null> {
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;
}
/**

View File

@ -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:",

View File

@ -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<boolean> {
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(

View File

@ -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', {

View File

@ -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,
});
},
);

View File

@ -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