From f78a9e3dc765226eb469f84337edb8ef3874bd9c Mon Sep 17 00:00:00 2001 From: "kanawoot.k" <160588725+maritonx@users.noreply.github.com> Date: Sat, 24 Oct 2026 22:36:51 +0700 Subject: [PATCH] test: run two instances against one Postgres and one S3 bucket in CI New multi-instance job: two `easy-cms serve` instances (NODE_ENV=production, migrated first) on one Postgres and one RustFS bucket, behind a round-robin proxy. It checks that REST writes and reads work from either instance, a session and CSRF token made on one are accepted by the other and logout ends it on both, failed logins are counted across instances, a file uploaded through one is served by the other, and that scheduled publishes and config jobs run once when both instances run jobs at once. Runs on its own config (multi-instance/vitest.config.ts), excluded from the integration suites. Closes #154 Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 40 ++ .../multi-instance/easy-cms.config.ts | 46 +++ .../multi-instance/multi-instance.test.ts | 261 +++++++++++++ .../multi-instance/vitest.config.ts | 16 + packages/integration/package.json | 6 +- packages/integration/tsconfig.json | 2 +- packages/integration/vitest.config.ts | 4 +- pnpm-lock.yaml | 351 +++++++++++++++++- 8 files changed, 722 insertions(+), 4 deletions(-) create mode 100644 packages/integration/multi-instance/easy-cms.config.ts create mode 100644 packages/integration/multi-instance/multi-instance.test.ts create mode 100644 packages/integration/multi-instance/vitest.config.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b75593e..f915fd28 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -228,6 +228,46 @@ jobs: run: for i in $(seq 1 30); do curl -s -o /dev/null localhost:9000 && exit 0; sleep 1; done; exit 1 - run: pnpm --filter @easy-cms/storage-s3 test + multi-instance: + needs: scope + if: needs.scope.outputs.full == 'true' + # Two `easy-cms serve` instances on one Postgres and one S3 bucket, behind a round-robin + # proxy (ADR-0051): REST, sessions and CSRF, login rate limits, uploads and jobs. + runs-on: ubuntu-24.04 + services: + postgres: + image: postgres:17 + env: + POSTGRES_USER: easycms + POSTGRES_PASSWORD: easycms + POSTGRES_DB: easycms + ports: ['5432:5432'] + options: >- + --health-cmd "pg_isready -U easycms" --health-interval 5s --health-timeout 5s --health-retries 10 + s3: + image: rustfs/rustfs:1.0.0 + env: + RUSTFS_ACCESS_KEY: ecmsaccess + RUSTFS_SECRET_KEY: ecmssecret123 + ports: ['9000:9000'] + env: + POSTGRES_URL: postgres://easycms:easycms@localhost:5432/easycms + S3_TEST_ENDPOINT: http://localhost:9000 + S3_TEST_ACCESS_KEY: ecmsaccess + S3_TEST_SECRET_KEY: ecmssecret123 + steps: + - uses: actions/checkout@v7 + - uses: pnpm/action-setup@v6 + - uses: actions/setup-node@v7 + with: + node-version: 24 + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: pnpm build + - name: Wait for the S3 server + run: for i in $(seq 1 30); do curl -s -o /dev/null localhost:9000 && exit 0; sleep 1; done; exit 1 + - run: pnpm --filter easy-cms-integration-tests test:multi-instance + e2e: needs: scope if: needs.scope.outputs.full == 'true' diff --git a/packages/integration/multi-instance/easy-cms.config.ts b/packages/integration/multi-instance/easy-cms.config.ts new file mode 100644 index 00000000..02bf9b22 --- /dev/null +++ b/packages/integration/multi-instance/easy-cms.config.ts @@ -0,0 +1,46 @@ +// Run by `easy-cms serve` as two instances against one Postgres and one S3 bucket (CI's +// multi-instance job), as the reference architecture describes (ADR-0051). +import { defineConfig } from '@easy-cms/core' +import { postgres } from '@easy-cms/db-postgres' +import { s3Storage } from '@easy-cms/storage-s3' + +const env = (name: string) => { + const value = process.env[name] + if (!value) throw new Error(`${name} is not set`) + return value +} + +export default defineConfig({ + secret: env('EASY_CMS_SECRET'), + cronSecret: env('CRON_SECRET'), + db: postgres({ url: env('POSTGRES_URL'), max: 5 }), + upload: { + storage: s3Storage({ + bucket: env('S3_TEST_BUCKET'), + endpoint: env('S3_TEST_ENDPOINT'), + region: 'us-east-1', + accessKeyId: env('S3_TEST_ACCESS_KEY'), + secretAccessKey: env('S3_TEST_SECRET_KEY'), + }), + }, + collections: [ + { + slug: 'posts', + drafts: true, + schedule: true, + access: { read: () => true }, + fields: [{ name: 'title', type: 'text' }], + }, + // One document per run of the job below. + { slug: 'runs', fields: [{ name: 'at', type: 'text' }] }, + ], + jobs: [ + { + name: 'test:count-runs', + every: 3600, + run: async ({ cms, now }) => { + await cms.create('runs', { at: now.toISOString() }) + }, + }, + ], +}) diff --git a/packages/integration/multi-instance/multi-instance.test.ts b/packages/integration/multi-instance/multi-instance.test.ts new file mode 100644 index 00000000..0f5c49e1 --- /dev/null +++ b/packages/integration/multi-instance/multi-instance.test.ts @@ -0,0 +1,261 @@ +// Two `easy-cms serve` instances on one Postgres and one S3 bucket, behind a round-robin proxy: +// the reference architecture (ADR-0051). Assertions that need a request to reach a given +// instance call it directly; the rest go through the proxy, alternating between them. +import { type ChildProcess, execFile, spawn } from 'node:child_process' +import { mkdtempSync, rmSync } from 'node:fs' +import { createServer, request as httpRequest, type Server } from 'node:http' +import type { AddressInfo } from 'node:net' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { promisify } from 'node:util' +import { createEasyCMS, type EasyCMS, silentLogger } from '@easy-cms/core' +import { AwsClient } from 'aws4fetch' +import sharp from 'sharp' +import { afterAll, beforeAll, describe, expect, it } from 'vitest' +import config from './easy-cms.config.js' + +const configFile = fileURLToPath(new URL('./easy-cms.config.ts', import.meta.url)) +const bin = fileURLToPath(new URL('../bin/easy-cms.js', import.meta.resolve('easy-cms'))) +const PASSWORD = 'password123' +const cwd = mkdtempSync(join(tmpdir(), 'easy-cms-multi-')) +// The instances run as in production; the secrets and bucket come from vitest.config.ts. +const env = { ...process.env, NODE_ENV: 'production' } + +const instances: ChildProcess[] = [] +let a: string +let b: string +let proxy: Server +let lb: string +/** The Local API on the same database, to set up and check what the instances did. */ +let cms: EasyCMS + +async function freePort() { + const server = createServer().listen(0) + await new Promise((resolve) => server.once('listening', resolve)) + const { port } = server.address() as AddressInfo + await new Promise((resolve) => server.close(resolve)) + return port +} + +async function cli(...args: string[]) { + await promisify(execFile)( + process.execPath, + [bin, ...args, '--config', configFile, '--cwd', cwd], + { + env, + }, + ) +} + +async function serve() { + const port = await freePort() + const child = spawn( + process.execPath, + [bin, 'serve', '--port', String(port), '--trust-proxy', '--config', configFile, '--cwd', cwd], + { env, stdio: ['ignore', 'inherit', 'inherit'] }, + ) + instances.push(child) + const url = `http://127.0.0.1:${port}` + for (let i = 0; i < 120; i++) { + if (child.exitCode !== null) throw new Error(`serve exited with ${child.exitCode}`) + if ( + await fetch(`${url}/healthz`).then( + (r) => r.ok, + () => false, + ) + ) + return url + await new Promise((resolve) => setTimeout(resolve, 250)) + } + throw new Error('serve did not start') +} + +/** Round robin, as a load balancer without sticky sessions; passes the client IP on. */ +function roundRobin(targets: string[]) { + let next = 0 + return createServer((req, res) => { + const target = new URL(targets[next++ % targets.length] as string) + const upstream = httpRequest( + { + host: target.hostname, + port: target.port, + method: req.method, + path: req.url, + headers: { + ...req.headers, + 'x-forwarded-for': (req.headers['x-test-ip'] as string) ?? '203.0.113.1', + 'x-forwarded-proto': 'http', + }, + }, + (response) => { + res.writeHead(response.statusCode ?? 502, response.headers) + response.pipe(res) + }, + ) + upstream.on('error', () => res.writeHead(502).end()) + req.pipe(upstream) + }).listen(0) +} + +// Same origin as the instance called: in production both answer on one hostname. +const api = (base: string, path: string, init: RequestInit = {}) => + fetch(`${base}/api/cms${path}`, { + ...init, + headers: { origin: base, ...(init.headers as Record) }, + }) +const json = (body: unknown, headers: Record = {}) => ({ + method: 'POST', + headers: { 'content-type': 'application/json', ...headers }, + body: JSON.stringify(body), +}) + +/** Logs in on one instance and returns headers that authenticate like a browser. */ +async function browser(base: string, email: string) { + const login = await api(base, '/auth/login', json({ email, password: PASSWORD })) + expect(login.status).toBe(200) + const cookie = login.headers + .getSetCookie() + // `__Host-` in production + .find((c) => /^(__Host-)?ecms-session=/.test(c)) + ?.split(';')[0] as string + const { csrfToken } = (await login.json()) as { csrfToken: string } + return { cookie, 'x-csrf-token': csrfToken } +} + +beforeAll(async () => { + const s3 = new AwsClient({ + accessKeyId: process.env.S3_TEST_ACCESS_KEY as string, + secretAccessKey: process.env.S3_TEST_SECRET_KEY as string, + service: 's3', + region: 'us-east-1', + }) + expect( + ( + await s3.fetch(`${process.env.S3_TEST_ENDPOINT}/${process.env.S3_TEST_BUCKET}`, { + method: 'PUT', + }) + ).ok, + ).toBe(true) + // As in production: migrate once, then start the instances (which refuse pending migrations). + await cli('migrate:create', 'init') + await cli('migrate') + ;[a, b] = await Promise.all([serve(), serve()]) + proxy = roundRobin([a as string, b as string]) + await new Promise((resolve) => proxy.once('listening', resolve)) + lb = `http://127.0.0.1:${(proxy.address() as AddressInfo).port}` + cms = await createEasyCMS(config, { + cwd, + schema: 'verify', + scheduler: false, + logger: silentLogger, + }) + await cms.create('users', { email: 'admin@x.co', password: PASSWORD, role: 'admin' }) +}, 120_000) + +afterAll(async () => { + for (const child of instances) child.kill('SIGTERM') + proxy?.close() + await cms?.destroy() + rmSync(cwd, { recursive: true, force: true }) +}) + +describe('two instances behind a load balancer', () => { + it('serves REST writes and reads from either instance', async () => { + const admin = await browser(a, 'admin@x.co') + const created = await api(b, '/posts', json({ title: 'From B', status: 'published' }, admin)) + expect(created.status).toBe(201) + const { id } = (await created.json()) as { id: number } + expect(await (await api(a, `/posts/${id}`)).json()).toMatchObject({ title: 'From B' }) + + const updated = await api(a, `/posts/${id}`, { + ...json({ title: 'Edited on A', status: 'published' }, admin), + method: 'PATCH', + }) + expect(updated.status).toBe(200) + expect(await (await api(b, `/posts/${id}`)).json()).toMatchObject({ title: 'Edited on A' }) + + // Through the balancer: every request lands on the other instance than the one before. + for (let i = 0; i < 6; i++) { + expect(await (await api(lb, `/posts/${id}`)).json()).toMatchObject({ title: 'Edited on A' }) + } + expect((await api(b, `/posts/${id}`, { method: 'DELETE', headers: admin })).status).toBe(200) + expect((await api(a, `/posts/${id}`)).status).toBe(404) + }) + + it('keeps a session made on one instance on the other, and ends it everywhere', async () => { + const onA = await browser(a, 'admin@x.co') + const me = (await (await api(b, '/auth/me', { headers: { cookie: onA.cookie } })).json()) as { + user: { email: string } | null + csrfToken: string + } + expect(me.user?.email).toBe('admin@x.co') + // The CSRF token from A is accepted by B. + expect(me.csrfToken).toBe(onA['x-csrf-token']) + expect((await api(b, '/posts', json({ title: 'csrf' }, onA))).status).toBe(201) + expect( + (await api(b, '/posts', json({ title: 'csrf' }, { ...onA, 'x-csrf-token': 'wrong' }))).status, + ).toBe(403) + + expect((await api(b, '/auth/logout', { method: 'POST', headers: onA })).status).toBe(200) + const after = await (await api(a, '/auth/me', { headers: { cookie: onA.cookie } })).json() + expect(after).toMatchObject({ user: null }) + }) + + it('counts failed logins from one client across instances', async () => { + const attempt = (password: string) => + api( + lb, + '/auth/login', + json({ email: 'admin@x.co', password }, { 'x-test-ip': '198.51.100.9' }), + ) + for (let i = 0; i < 5; i++) expect((await attempt('wrong-password')).status).toBe(401) + // Locked out on both instances, even with the right password. + expect((await attempt(PASSWORD)).status).toBe(429) + expect((await attempt(PASSWORD)).status).toBe(429) + }) + + it('serves a file uploaded through one instance from the other', async () => { + const admin = await browser(a, 'admin@x.co') + const form = new FormData() + const png = await sharp({ + create: { width: 4, height: 4, channels: 3, background: '#c00' }, + }) + .png() + .toBuffer() + form.set('file', new Blob([new Uint8Array(png)], { type: 'image/png' }), 'red.png') + const uploaded = await api(a, '/media', { method: 'POST', headers: admin, body: form }) + expect(uploaded.status).toBe(201) + const media = (await uploaded.json()) as { id: number; url: string } + const file = await fetch(`${b}${media.url}`) + expect(file.status).toBe(200) + expect(Buffer.from(await file.arrayBuffer()).equals(png)).toBe(true) + expect((await api(b, `/media/${media.id}`, { method: 'DELETE', headers: admin })).status).toBe( + 200, + ) + expect((await fetch(`${a}${media.url}`)).status).toBe(404) + }) + + it('runs each scheduled publish and each job once when both instances run jobs', async () => { + const posts = await Promise.all( + Array.from({ length: 20 }, (_, i) => cms.create('posts', { title: `Scheduled ${i}` })), + ) + const at = new Date(Date.now() - 1000) + for (const post of posts) await cms.schedule('posts', post.id, { action: 'publish', at }) + + // A cron service and both instances' schedulers, all at once. + const runs = await Promise.all( + Array.from({ length: 10 }, (_, i) => + api(i % 2 ? a : b, '/jobs/run', { + headers: { authorization: `Bearer ${process.env.CRON_SECRET}` }, + }).then((r) => r.json() as Promise<{ scheduled: { ran: number }; jobs: { ran: number } }>), + ), + ) + expect(runs.reduce((sum, run) => sum + run.scheduled.ran, 0)).toBe(20) + expect(runs.reduce((sum, run) => sum + run.jobs.ran, 0)).toBe(1) + expect(await cms.count('runs')).toBe(1) + for (const post of posts) { + expect(await cms.findById('posts', post.id)).toMatchObject({ status: 'published' }) + } + }) +}) diff --git a/packages/integration/multi-instance/vitest.config.ts b/packages/integration/multi-instance/vitest.config.ts new file mode 100644 index 00000000..8d846a1a --- /dev/null +++ b/packages/integration/multi-instance/vitest.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from 'vitest/config' + +// Not part of `vitest run`: needs Postgres and an S3 server (CI's multi-instance job). +export default defineConfig({ + test: { + root: import.meta.dirname, + testTimeout: 60_000, + hookTimeout: 120_000, + // Read by easy-cms.config.ts, in the test and in the instances it starts. + env: { + EASY_CMS_SECRET: 'multi-instance-test-secret-0123456789abcdef', + CRON_SECRET: 'multi-instance-cron-secret-0123456789abcdef', + S3_TEST_BUCKET: `ecms-multi-${Date.now()}`, + }, + }, +}) diff --git a/packages/integration/package.json b/packages/integration/package.json index d8c02cf0..a3fc977d 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -6,15 +6,19 @@ "typecheck": "tsc --noEmit", "test": "vitest run", "bench": "node bench.ts", - "load": "node load.ts" + "load": "node load.ts", + "test:multi-instance": "vitest run --config multi-instance/vitest.config.ts" }, "devDependencies": { "@easy-cms/auth-oauth": "workspace:^", "@easy-cms/core": "workspace:^", "@easy-cms/db-postgres": "workspace:^", "@easy-cms/db-sqlite": "workspace:^", + "@easy-cms/storage-s3": "workspace:^", "@electric-sql/pglite": "^0.5.8", "@libsql/client": "^0.18.0", + "aws4fetch": "^1.0.20", + "easy-cms": "workspace:^", "postgres": "^3.4.9", "sharp": "^0.35.5" } diff --git a/packages/integration/tsconfig.json b/packages/integration/tsconfig.json index cc07dbe5..32d5b74c 100644 --- a/packages/integration/tsconfig.json +++ b/packages/integration/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../../tsconfig.base.json", - "include": ["test", "vitest.config.ts", "bench.ts"] + "include": ["test", "multi-instance", "vitest.config.ts", "bench.ts"] } diff --git a/packages/integration/vitest.config.ts b/packages/integration/vitest.config.ts index 327de440..56b73e94 100644 --- a/packages/integration/vitest.config.ts +++ b/packages/integration/vitest.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from 'vitest/config' +import { configDefaults, defineConfig } from 'vitest/config' // The same suites run against every database: SQLite, PGlite (Postgres in WebAssembly), // and a real Postgres server when POSTGRES_URL is set (CI). @@ -9,6 +9,8 @@ export default defineConfig({ // The first test of a file starts PGlite and loads drizzle-kit. testTimeout: 90_000, hookTimeout: 90_000, + // Run on its own: multi-instance/vitest.config.ts. + exclude: [...configDefaults.exclude, 'multi-instance/**'], projects: dialects.map((dialect) => ({ extends: true, test: { name: dialect, env: { EASY_CMS_TEST_DIALECT: dialect } }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4ceb544f..c4d67b60 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -446,12 +446,21 @@ importers: '@easy-cms/db-sqlite': specifier: workspace:^ version: link:../db-sqlite + '@easy-cms/storage-s3': + specifier: workspace:^ + version: link:../storage-s3 '@electric-sql/pglite': specifier: ^0.5.8 version: 0.5.8 '@libsql/client': specifier: ^0.18.0 version: 0.18.0 + aws4fetch: + specifier: ^1.0.20 + version: 1.0.20 + easy-cms: + specifier: workspace:^ + version: link:../cli postgres: specifier: ^3.4.9 version: 3.4.9 @@ -507,7 +516,7 @@ importers: version: 4.3.2(@playwright/test@1.63.0)(@vue/test-utils@2.5.1(@vue/compiler-dom@3.5.44)(@vue/server-renderer@3.5.44)(vue@3.5.43(typescript@6.0.3)))(esbuild@0.28.2)(happy-dom@20.14.5)(magicast@0.5.5)(playwright-core@1.63.0)(rolldown@1.2.14)(rollup@4.64.7)(typescript@6.0.3)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))(vitest@5.0.1) nuxt: specifier: ^4.6.2 - version: 4.6.2(8341e7b36084563b1d1df6456ea53827) + version: 4.6.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@biomejs/biome@2.5.14)(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@netlify/blobs@11.1.3)(@oxc-project/types@0.154.0)(@types/node@22.20.4)(@vercel/blob@2.8.0)(@vue/compiler-sfc@3.5.44)(acorn@8.19.0)(aws4fetch@1.0.20)(confbox@0.3.1)(db0@0.3.4(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(drizzle-orm@0.45.3(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@opentelemetry/api@1.9.1)(postgres@3.4.9)))(dotenv@17.4.2)(drizzle-orm@0.45.3(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@opentelemetry/api@1.9.1)(postgres@3.4.9))(esbuild@0.28.2)(giget@3.3.1)(ioredis@5.11.1)(lightningcss@1.33.0)(mlly@1.8.2)(rolldown@1.2.14)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.14)(rollup@4.64.7))(rollup@4.64.7)(srvx@1.0.5)(terser@5.51.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))(yaml@2.9.1) vue: specifier: ^3.5.0 version: 3.5.43(typescript@6.0.3) @@ -8430,6 +8439,99 @@ snapshots: - webpack - xml2js + '@nuxt/nitro-server@4.6.2(c810ee51f9945db652f39682956d9e9c)': + dependencies: + '@nuxt/kit': 4.6.2(@nuxt/schema@4.6.2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.3)(mlly@1.8.2)(rolldown@1.2.14)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))) + '@unhead/vue': 3.4.3(@oxc-project/types@0.154.0)(esbuild@0.28.2)(lightningcss@1.33.0)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.44(typescript@6.0.3)) + consola: 3.4.2 + defu: 6.1.7 + destr: 2.0.5 + devalue: 6.0.3 + errx: 0.2.2 + escape-string-regexp: 5.0.0 + exsolve: 1.1.3 + flru: 1.0.2 + h3: 1.15.11 + impound: 1.2.0(esbuild@0.28.2)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + klona: 2.0.6 + mocked-exports: 0.1.1 + my-bad: 0.2.11(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + nitropack: 2.13.4(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@netlify/blobs@11.1.3)(@vercel/blob@2.8.0)(aws4fetch@1.0.20)(drizzle-orm@0.45.3(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@opentelemetry/api@1.9.1)(postgres@3.4.9))(rolldown@1.2.14)(srvx@1.0.5)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + nostics: 1.3.0 + nuxt: 4.6.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@biomejs/biome@2.5.14)(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@netlify/blobs@11.1.3)(@oxc-project/types@0.154.0)(@types/node@22.20.4)(@vercel/blob@2.8.0)(@vue/compiler-sfc@3.5.44)(acorn@8.19.0)(aws4fetch@1.0.20)(confbox@0.3.1)(db0@0.3.4(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(drizzle-orm@0.45.3(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@opentelemetry/api@1.9.1)(postgres@3.4.9)))(dotenv@17.4.2)(drizzle-orm@0.45.3(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@opentelemetry/api@1.9.1)(postgres@3.4.9))(esbuild@0.28.2)(giget@3.3.1)(ioredis@5.11.1)(lightningcss@1.33.0)(mlly@1.8.2)(rolldown@1.2.14)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.14)(rollup@4.64.7))(rollup@4.64.7)(srvx@1.0.5)(terser@5.51.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))(yaml@2.9.1) + ohash: 2.0.12 + pathe: 2.0.3 + radix3: 1.1.2 + rou3: 0.9.2 + std-env: 4.3.0 + ufo: 1.6.4 + unctx: 3.0.1(magic-string@1.4.3)(rolldown@1.2.14)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))) + unrouting: 0.2.3 + unstorage: 1.17.6(@netlify/blobs@11.1.3)(@vercel/blob@2.8.0)(aws4fetch@1.0.20)(db0@0.3.4(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(drizzle-orm@0.45.3(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@opentelemetry/api@1.9.1)(postgres@3.4.9)))(ioredis@5.11.1) + vue: 3.5.44(typescript@6.0.3) + vue-bundle-renderer: 2.4.0 + vue-devtools-stub: 0.1.0 + optionalDependencies: + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@electric-sql/pglite' + - '@farmfe/core' + - '@libsql/client' + - '@netlify/blobs' + - '@nuxt/schema' + - '@oxc-project/types' + - '@parcel/watcher' + - '@planetscale/database' + - '@rsbuild/core' + - '@rspack/core' + - '@unhead/cli' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - '@vitejs/devtools-kit' + - aws4fetch + - bare-abort-controller + - bare-buffer + - better-sqlite3 + - bun-types-no-globals + - confbox + - db0 + - dotenv + - drizzle-orm + - encoding + - esbuild + - giget + - idb-keyval + - ioredis + - jiti + - lightningcss + - magic-string + - mlly + - mysql2 + - oxc-parser + - react-native-b4a + - rolldown + - rollup + - sqlite3 + - srvx + - supports-color + - typescript + - unloader + - unplugin + - uploadthing + - vite + - webpack + - xml2js + '@nuxt/schema@4.5.2': dependencies: '@vue/shared': 3.5.43 @@ -8506,6 +8608,73 @@ snapshots: - vite - webpack + '@nuxt/vite-builder@4.6.2(0d3200de7489b01200fdaee340d678c7)': + dependencies: + '@nuxt/kit': 4.6.2(@nuxt/schema@4.6.2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.3)(mlly@1.8.2)(rolldown@1.2.14)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))) + '@vitejs/plugin-vue': 6.0.10(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.44(typescript@6.0.3)) + '@vitejs/plugin-vue-jsx': 5.1.6(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.44(typescript@6.0.3)) + autoprefixer: 10.6.1(postcss@8.5.30) + clickable-path: 0.1.1 + consola: 3.4.2 + cssnano: 9.9.0(postcss@8.5.30) + defu: 6.1.7 + escape-string-regexp: 5.0.0 + exsolve: 1.1.3 + generic-names: 4.0.0 + get-port-please: 3.2.0 + js-tokens: 10.0.0 + knitwork: 1.3.0 + mocked-exports: 0.1.1 + my-bad: 0.2.11(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + nuxt: 4.6.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@biomejs/biome@2.5.14)(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@netlify/blobs@11.1.3)(@oxc-project/types@0.154.0)(@types/node@22.20.4)(@vercel/blob@2.8.0)(@vue/compiler-sfc@3.5.44)(acorn@8.19.0)(aws4fetch@1.0.20)(confbox@0.3.1)(db0@0.3.4(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(drizzle-orm@0.45.3(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@opentelemetry/api@1.9.1)(postgres@3.4.9)))(dotenv@17.4.2)(drizzle-orm@0.45.3(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@opentelemetry/api@1.9.1)(postgres@3.4.9))(esbuild@0.28.2)(giget@3.3.1)(ioredis@5.11.1)(lightningcss@1.33.0)(mlly@1.8.2)(rolldown@1.2.14)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.14)(rollup@4.64.7))(rollup@4.64.7)(srvx@1.0.5)(terser@5.51.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))(yaml@2.9.1) + pathe: 2.0.3 + pkg-types: 2.3.3 + postcss: 8.5.30 + rolldown-string: 0.3.1(rolldown@1.2.14) + seroval: 1.6.9 + std-env: 4.3.0 + ufo: 1.6.4 + unenv: 2.0.0-rc.24 + vite: 8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) + vite-node: 6.0.0(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) + vite-plugin-checker: 0.14.5(@biomejs/biome@2.5.14)(typescript@6.0.3)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))(vue-tsc@3.3.11(typescript@6.0.3)) + vue: 3.5.44(typescript@6.0.3) + vue-bundle-renderer: 2.4.0 + optionalDependencies: + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + rolldown: 1.2.14 + rollup-plugin-visualizer: 7.1.1(rolldown@1.2.14)(rollup@4.64.7) + transitivePeerDependencies: + - '@biomejs/biome' + - '@nuxt/schema' + - '@types/node' + - '@vitejs/devtools' + - confbox + - dotenv + - esbuild + - eslint + - giget + - jiti + - less + - magic-string + - meow + - mlly + - optionator + - oxc-parser + - oxlint + - sass + - sass-embedded + - stylelint + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - unplugin + - vue-tsc + - yaml + '@nuxt/vite-builder@4.6.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@biomejs/biome@2.5.14)(@nuxt/schema@4.6.2)(@types/node@22.20.4)(confbox@0.3.1)(dotenv@17.4.2)(esbuild@0.28.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.3)(mlly@1.8.2)(nuxt@4.6.2(8341e7b36084563b1d1df6456ea53827))(rolldown@1.2.14)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.14)(rollup@4.64.7))(terser@5.51.2)(tsx@4.23.15)(typescript@6.0.3)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)))(vue-tsc@3.3.11(typescript@6.0.3))(vue@3.5.44(typescript@6.0.3))(yaml@2.9.1)': dependencies: '@nuxt/kit': 4.6.2(@nuxt/schema@4.6.2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.3)(mlly@1.8.2)(rolldown@1.2.14)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))) @@ -8600,6 +8769,33 @@ snapshots: - rolldown - unplugin + '@nuxt/vite-server@4.6.2(f7633775e65acf2e3ca13481065e583f)': + dependencies: + '@nuxt/kit': 4.6.2(@nuxt/schema@4.6.2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.3)(mlly@1.8.2)(rolldown@1.2.14)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))) + clickable-path: 0.1.1 + defu: 6.1.7 + exsolve: 1.1.3 + mocked-exports: 0.1.1 + my-bad: 0.2.11(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + nuxt: 4.6.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@biomejs/biome@2.5.14)(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@netlify/blobs@11.1.3)(@oxc-project/types@0.154.0)(@types/node@22.20.4)(@vercel/blob@2.8.0)(@vue/compiler-sfc@3.5.44)(acorn@8.19.0)(aws4fetch@1.0.20)(confbox@0.3.1)(db0@0.3.4(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(drizzle-orm@0.45.3(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@opentelemetry/api@1.9.1)(postgres@3.4.9)))(dotenv@17.4.2)(drizzle-orm@0.45.3(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@opentelemetry/api@1.9.1)(postgres@3.4.9))(esbuild@0.28.2)(giget@3.3.1)(ioredis@5.11.1)(lightningcss@1.33.0)(mlly@1.8.2)(rolldown@1.2.14)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.14)(rollup@4.64.7))(rollup@4.64.7)(srvx@1.0.5)(terser@5.51.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))(yaml@2.9.1) + pathe: 2.0.3 + rou3: 0.9.2 + srvx: 1.0.5 + ufo: 1.6.4 + ultrahtml: 1.7.0 + vite: 8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) + transitivePeerDependencies: + - '@nuxt/schema' + - confbox + - dotenv + - giget + - jiti + - magic-string + - mlly + - oxc-parser + - rolldown + - unplugin + '@one-ini/wasm@0.2.1': {} '@opentelemetry/api-logs@0.221.0': @@ -11566,6 +11762,159 @@ snapshots: - xml2js - yaml + nuxt@4.6.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@biomejs/biome@2.5.14)(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@netlify/blobs@11.1.3)(@oxc-project/types@0.154.0)(@types/node@22.20.4)(@vercel/blob@2.8.0)(@vue/compiler-sfc@3.5.44)(acorn@8.19.0)(aws4fetch@1.0.20)(confbox@0.3.1)(db0@0.3.4(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(drizzle-orm@0.45.3(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@opentelemetry/api@1.9.1)(postgres@3.4.9)))(dotenv@17.4.2)(drizzle-orm@0.45.3(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@opentelemetry/api@1.9.1)(postgres@3.4.9))(esbuild@0.28.2)(giget@3.3.1)(ioredis@5.11.1)(lightningcss@1.33.0)(mlly@1.8.2)(rolldown@1.2.14)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.14)(rollup@4.64.7))(rollup@4.64.7)(srvx@1.0.5)(terser@5.51.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))(yaml@2.9.1): + dependencies: + '@dxup/nuxt': 0.5.10(@nuxt/schema@4.6.2)(confbox@0.3.1)(dotenv@17.4.2)(esbuild@0.28.2)(giget@3.3.1)(jiti@2.7.0)(mlly@1.8.2)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + '@jridgewell/trace-mapping': 0.3.31 + '@nuxt/cli': 4.0.1(@nuxt/schema@4.6.2)(jiti@2.7.0)(rolldown@1.2.14)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + '@nuxt/devalue': 2.0.2 + '@nuxt/devtools': 3.4.3(@netlify/blobs@11.1.3)(@nuxt/schema@4.6.2)(@vercel/blob@2.8.0)(aws4fetch@1.0.20)(confbox@0.3.1)(db0@0.3.4(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(drizzle-orm@0.45.3(@electric-sql/pglite@0.5.8)(@libsql/client@0.18.0)(@opentelemetry/api@1.9.1)(postgres@3.4.9)))(dotenv@17.4.2)(giget@3.3.1)(ioredis@5.11.1)(jiti@2.7.0)(magic-string@1.4.3)(mlly@1.8.2)(rolldown@1.2.14)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)))(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.44(typescript@6.0.3)) + '@nuxt/kit': 4.6.2(@nuxt/schema@4.6.2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.3)(mlly@1.8.2)(rolldown@1.2.14)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))) + '@nuxt/nitro-server': 4.6.2(c810ee51f9945db652f39682956d9e9c) + '@nuxt/schema': 4.6.2 + '@nuxt/telemetry': 2.9.1(@nuxt/kit@4.6.2(@nuxt/schema@4.6.2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.3)(mlly@1.8.2)(rolldown@1.2.14)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)))) + '@nuxt/vite-builder': 4.6.2(0d3200de7489b01200fdaee340d678c7) + '@nuxt/vite-server': 4.6.2(f7633775e65acf2e3ca13481065e583f) + '@unhead/vue': 3.4.3(@oxc-project/types@0.154.0)(esbuild@0.28.2)(lightningcss@1.33.0)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.44(typescript@6.0.3)) + '@vue/shared': 3.5.44 + chokidar: 5.0.0 + clickable-path: 0.1.1 + compatx: 0.2.0 + consola: 3.4.2 + cookie-es: 3.1.1 + defu: 6.1.7 + destr: 2.0.5 + devalue: 6.0.3 + errx: 0.2.2 + escape-string-regexp: 5.0.0 + exsolve: 1.1.3 + fetchdts: 0.2.0 + fnv1a-64: 0.1.2 + hookable: 6.1.2 + ignore: 7.0.12 + impound: 1.2.0(esbuild@0.28.2)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + iron-webcrypto: 2.0.0 + jiti: 2.7.0 + klona: 2.0.6 + knitwork: 1.3.0 + magic-string: 1.4.3 + my-bad: 0.2.11(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + nanotar: 0.3.0 + nostics: 1.3.0 + object-identity: 0.2.3 + ofetch: 1.5.1 + ohash: 2.0.12 + on-change: 6.0.2 + oxc-walker: 1.1.1(@oxc-project/types@0.154.0)(rolldown@1.2.14) + pathe: 2.0.3 + perfect-debounce: 2.1.0 + picomatch: 4.0.7 + pkg-types: 2.3.3 + rolldown: 1.2.14 + rolldown-string: 0.3.1(rolldown@1.2.14) + rou3: 0.9.2 + scule: 1.3.0 + std-env: 4.3.0 + tinyglobby: 0.2.17 + ufo: 1.6.4 + ultrahtml: 1.7.0 + uncrypto: 0.1.3 + unctx: 3.0.1(magic-string@1.4.3)(rolldown@1.2.14)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))) + undici: 8.12.2 + unhead: 3.4.3(esbuild@0.28.2)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + unimport: 7.0.2(acorn@8.19.0)(esbuild@0.28.2)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + unplugin: 3.4.0(esbuild@0.28.2)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + unrouting: 0.2.3 + untyped: 2.0.0 + verkit: 0.5.0 + vue: 3.5.44(typescript@6.0.3) + vue-bundle-renderer: 2.4.0 + vue-component-type-helpers: 3.3.13 + vue-router: 5.4.0(@vue/compiler-sfc@3.5.44)(esbuild@0.28.2)(rolldown@1.2.14)(rollup@4.64.7)(vite@8.3.4(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.44(typescript@6.0.3)) + optionalDependencies: + '@types/node': 22.20.4 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@babel/plugin-proposal-decorators' + - '@babel/plugin-syntax-jsx' + - '@babel/plugin-syntax-typescript' + - '@biomejs/biome' + - '@capacitor/preferences' + - '@deno/kv' + - '@electric-sql/pglite' + - '@farmfe/core' + - '@libsql/client' + - '@netlify/blobs' + - '@nuxt/docs' + - '@oxc-project/types' + - '@pinia/colada' + - '@planetscale/database' + - '@rollup/plugin-babel' + - '@rsbuild/core' + - '@rspack/core' + - '@unhead/cli' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - '@vitejs/devtools' + - '@vitejs/devtools-kit' + - '@vue/compiler-sfc' + - acorn + - aws4fetch + - bare-abort-controller + - bare-buffer + - better-sqlite3 + - bufferutil + - bun-types-no-globals + - confbox + - db0 + - dotenv + - drizzle-orm + - encoding + - esbuild + - eslint + - giget + - idb-keyval + - ioredis + - less + - lightningcss + - meow + - mlly + - mysql2 + - optionator + - oxc-parser + - oxlint + - pinia + - react-native-b4a + - rollup + - rollup-plugin-visualizer + - sass + - sass-embedded + - serve + - sqlite3 + - srvx + - stylelint + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - unloader + - uploadthing + - utf-8-validate + - vite + - vue-tsc + - webpack + - xml2js + - yaml + nypm@0.6.10: dependencies: citty: 0.2.2